Remove Suggestion v1 code.

Change-Id: Ie6e9c8f2b1b5b609d44e287accb9fbbef9054d34
Fixes: 68719093
Test: robotests
This commit is contained in:
Fan Zhang
2018-01-02 11:22:36 -08:00
parent ce3633be80
commit 1e27d2b28e
24 changed files with 130 additions and 1630 deletions

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:clickable="true"
android:focusable="true"
android:minHeight="@dimen/dashboard_tile_minimum_height" />

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<optional-steps>
<step category="com.android.settings.suggested.category.DEFERRED_SETUP"
exclusive="true" />
<step category="com.android.settings.suggested.category.FIRST_IMPRESSION"
exclusiveExpireDays="14"
exclusive="true"
multiple="true" />
<step category="com.android.settings.suggested.category.LOCK_SCREEN" />
<step category="com.android.settings.suggested.category.TRUST_AGENT" />
<step category="com.android.settings.suggested.category.EMAIL" />
<step category="com.android.settings.suggested.category.PARTNER_ACCOUNT"
multiple="true" />
<step category="com.android.settings.suggested.category.GESTURE" />
<step category="com.android.settings.suggested.category.HOTWORD" />
<step category="com.android.settings.suggested.category.DEFAULT"
multiple="true" />
<step category="com.android.settings.suggested.category.SETTINGS_ONLY"
multiple="true" />
</optional-steps>

View File

@@ -21,7 +21,6 @@ package com.android.settings.core;
*/
public class FeatureFlags {
public static final String SEARCH_V2 = "settings_search_v2";
public static final String SUGGESTIONS_V2 = "new_settings_suggestion";
public static final String APP_INFO_V2 = "settings_app_info_v2";
public static final String CONNECTED_DEVICE_V2 = "settings_connected_device_v2";
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";

View File

@@ -39,7 +39,6 @@ public class MetricsFeatureProvider {
protected void installLogWriters() {
mLoggerWriters.add(new EventLogWriter());
mLoggerWriters.add(new SettingSuggestionsLogWriter());
}
public void visible(Context context, int source, int category) {

View File

@@ -1,91 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.core.instrumentation;
import android.content.Context;
import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.dashboard.suggestions.EventStore;
/**
* {@link LogWriter} that writes setting suggestion related logs.
*/
public class SettingSuggestionsLogWriter implements LogWriter {
private EventStore mEventStore;
@Override
public void visible(Context context, int source, int category) {
}
@Override
public void hidden(Context context, int category) {
}
@Override
public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
}
@Override
public void actionWithSource(Context context, int source, int category) {
}
@Override
public void action(int category, int value, Pair<Integer, Object>... taggedData) {
}
@Override
public void action(int category, boolean value, Pair<Integer, Object>... taggedData) {
}
@Override
public void action(Context context, int category, int value) {
}
@Override
public void action(Context context, int category, boolean value) {
}
@Override
public void action(Context context, int category, String pkg,
Pair<Integer, Object>... taggedData) {
if (mEventStore == null) {
mEventStore = new EventStore(context);
}
switch (category) {
case MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION:
mEventStore.writeEvent(pkg, EventStore.EVENT_SHOWN);
break;
case MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION:
mEventStore.writeEvent(pkg, EventStore.EVENT_DISMISSED);
break;
case MetricsEvent.ACTION_SETTINGS_SUGGESTION:
mEventStore.writeEvent(pkg, EventStore.EVENT_CLICKED);
break;
}
}
@Override
public void count(Context context, String name, int value) {
}
@Override
public void histogram(Context context, String name, int bucket) {
}
}

View File

@@ -52,7 +52,6 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionParser;
import java.util.ArrayList;
import java.util.List;
@@ -61,7 +60,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
implements SummaryLoader.SummaryConsumer {
public static final String TAG = "DashboardAdapter";
private static final String STATE_SUGGESTION_LIST = "suggestion_list";
private static final String STATE_SUGGESTION_LIST_V2 = "suggestion_list_v2";
private static final String STATE_CATEGORY_LIST = "category_list";
private static final String STATE_SUGGESTIONS_SHOWN_LOGGED = "suggestions_shown_logged";
@@ -69,8 +67,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
static final String STATE_SUGGESTION_CONDITION_MODE = "suggestion_condition_mode";
@VisibleForTesting
static final int SUGGESTION_CONDITION_HEADER_POSITION = 0;
@VisibleForTesting
static final int MAX_SUGGESTION_TO_SHOW = 5;
private final IconCache mCache;
private final Context mContext;
@@ -80,7 +76,6 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
private final ArrayList<String> mSuggestionsShownLogged;
private boolean mFirstFrameDrawn;
private RecyclerView mRecyclerView;
private SuggestionParser mSuggestionParser;
private SuggestionAdapter mSuggestionAdapter;
private SuggestionDismissController mSuggestionDismissHandler;
private SuggestionDismissController.Callback mCallback;
@@ -97,13 +92,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
};
public DashboardAdapter(Context context, Bundle savedInstanceState,
List<Condition> conditions, SuggestionParser suggestionParser,
SuggestionControllerMixin suggestionControllerMixin,
List<Condition> conditions, SuggestionControllerMixin suggestionControllerMixin,
SuggestionDismissController.Callback callback) {
// @deprecated In favor of suggestionsV2 below.
List<Tile> suggestions = null;
List<Suggestion> suggestionsV2 = null;
List<Suggestion> suggestions = null;
DashboardCategory category = null;
int suggestionConditionMode = DashboardData.HEADER_MODE_DEFAULT;
@@ -113,14 +105,12 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
mCache = new IconCache(context);
mSuggestionParser = suggestionParser;
mCallback = callback;
setHasStableIds(true);
if (savedInstanceState != null) {
suggestions = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST);
suggestionsV2 = savedInstanceState.getParcelableArrayList(STATE_SUGGESTION_LIST_V2);
category = savedInstanceState.getParcelable(STATE_CATEGORY_LIST);
suggestionConditionMode = savedInstanceState.getInt(
STATE_SUGGESTION_CONDITION_MODE, suggestionConditionMode);
@@ -133,37 +123,15 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
mDashboardData = new DashboardData.Builder()
.setConditions(conditions)
.setSuggestions(suggestions)
.setSuggestionsV2(suggestionsV2)
.setCategory(category)
.setSuggestionConditionMode(suggestionConditionMode)
.build();
}
public List<Tile> getSuggestions() {
return mDashboardData.getSuggestions();
}
/**
* @deprecated in favor of {@link #setCategory(DashboardCategory)} and
* {@link #setSuggestionsV2(List)}.
*/
@Deprecated
public void setCategoriesAndSuggestions(DashboardCategory category, List<Tile> suggestions) {
tintIcons(category, suggestions);
public void setSuggestions(List<Suggestion> data) {
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestions(suggestions.subList(0,
Math.min(suggestions.size(), MAX_SUGGESTION_TO_SHOW)))
.setCategory(category)
.build();
notifyDashboardDataChanged(prevData);
}
public void setSuggestionsV2(List<Suggestion> data) {
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestionsV2(data)
.setSuggestions(data)
.build();
notifyDashboardDataChanged(prevData);
}
@@ -187,30 +155,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
notifyDashboardDataChanged(prevData);
}
/**
* @deprecated in favor of {@link #onSuggestionDismissed(Suggestion)}.
*/
@Deprecated
public void onSuggestionDismissed(Tile suggestion) {
final List<Tile> suggestions = mDashboardData.getSuggestions();
if (suggestions == null || suggestions.isEmpty()) {
return;
}
if (suggestions.size() == 1) {
// The only suggestion is dismissed, and the the empty suggestion container will
// remain as the dashboard item. Need to refresh the dashboard list.
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestions(null)
.build();
notifyDashboardDataChanged(prevData);
} else {
mSuggestionAdapter.removeSuggestion(suggestion);
}
}
public void onSuggestionDismissed(Suggestion suggestion) {
final List<Suggestion> list = mDashboardData.getSuggestionsV2();
final List<Suggestion> list = mDashboardData.getSuggestions();
if (list == null || list.size() == 0) {
return;
}
@@ -219,7 +165,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
// remain as the dashboard item. Need to refresh the dashboard list.
final DashboardData prevData = mDashboardData;
mDashboardData = new DashboardData.Builder(prevData)
.setSuggestionsV2(null)
.setSuggestions(null)
.build();
notifyDashboardDataChanged(prevData);
} else {
@@ -309,18 +255,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
return mDashboardData.getItemEntityById(itemId);
}
/**
* @deprecated in favor of {@link #getSuggestionV2(int)}.
*/
@Deprecated
public Tile getSuggestion(int position) {
public Suggestion getSuggestion(int position) {
return mSuggestionAdapter.getSuggestion(position);
}
public Suggestion getSuggestionV2(int position) {
return mSuggestionAdapter.getSuggestionsV2(position);
}
@VisibleForTesting
void notifyDashboardDataChanged(DashboardData prevData) {
if (mFirstFrameDrawn && prevData != null) {
@@ -422,26 +360,17 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
int position) {
// If there is suggestions to show, it will be at position 0 as we don't show the suggestion
// header anymore.
final List<Suggestion> suggestionsV2 = mDashboardData.getSuggestionsV2();
final List<Tile> suggestions = mDashboardData.getSuggestions();
final List<Suggestion> suggestions = mDashboardData.getSuggestions();
boolean conditionOnly = true;
if (position == SUGGESTION_CONDITION_HEADER_POSITION) {
if (suggestions != null && suggestions.size() > 0) {
if (suggestions != null && suggestions.size() > 0) {
conditionOnly = false;
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
(List<Tile>) mDashboardData.getItemEntityByPosition(position),
null, mSuggestionsShownLogged);
mSuggestionDismissHandler = new SuggestionDismissController(mContext,
holder.data, mSuggestionControllerMixin, mSuggestionParser, mCallback);
holder.data.setAdapter(mSuggestionAdapter);
} else if (suggestionsV2 != null && suggestionsV2.size() > 0) {
conditionOnly = false;
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
null, (List<Suggestion>) mDashboardData.getItemEntityByPosition(position),
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position),
mSuggestionsShownLogged);
mSuggestionDismissHandler = new SuggestionDismissController(mContext,
holder.data, mSuggestionControllerMixin, null /* parser */, mCallback);
holder.data, mSuggestionControllerMixin, mCallback);
holder.data.setAdapter(mSuggestionAdapter);
}
}
@@ -494,14 +423,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
void onSaveInstanceState(Bundle outState) {
final DashboardCategory category = mDashboardData.getCategory();
final List<Tile> suggestions = mDashboardData.getSuggestions();
final List<Suggestion> suggestionV2 = mDashboardData.getSuggestionsV2();
final List<Suggestion> suggestions = mDashboardData.getSuggestions();
if (suggestions != null) {
outState.putParcelableArrayList(STATE_SUGGESTION_LIST, new ArrayList<>(suggestions));
}
if (suggestionV2 != null) {
outState.putParcelableArrayList(STATE_SUGGESTION_LIST_V2,
new ArrayList<>(suggestionV2));
outState.putParcelableArrayList(STATE_SUGGESTION_LIST,
new ArrayList<>(suggestions));
}
if (category != null) {
outState.putParcelable(STATE_CATEGORY_LIST, category);

View File

@@ -69,16 +69,14 @@ public class DashboardData {
private final List<Item> mItems;
private final DashboardCategory mCategory;
private final List<Condition> mConditions;
private final List<Tile> mSuggestions;
private final List<Suggestion> mSuggestionsV2;
private final List<Suggestion> mSuggestions;
@HeaderMode
private final int mSuggestionConditionMode;
private DashboardData(Builder builder) {
mCategory = builder.mCategory;
mConditions = builder.mConditions;
mSuggestions = builder.mSuggestions;
mSuggestionsV2 = builder.mSuggestionsV2;
mSuggestions = builder.mSuggestionsV2;
mSuggestionConditionMode = builder.mSuggestionConditionMode;
mItems = new ArrayList<>();
@@ -123,14 +121,10 @@ public class DashboardData {
return mConditions;
}
public List<Tile> getSuggestions() {
public List<Suggestion> getSuggestions() {
return mSuggestions;
}
public List<Suggestion> getSuggestionsV2() {
return mSuggestionsV2;
}
public int getSuggestionConditionMode() {
return mSuggestionConditionMode;
}
@@ -197,22 +191,15 @@ public class DashboardData {
* and mIsShowingAll, mSuggestionConditionMode flag.
*/
private void buildItemsData() {
final boolean useSuggestionV2 = mSuggestionsV2 != null;
final boolean hasSuggestions = useSuggestionV2
? sizeOf(mSuggestionsV2) > 0
: sizeOf(mSuggestions) > 0;
final boolean hasSuggestions = sizeOf(mSuggestions) > 0;
final List<Condition> conditions = getConditionsToShow(mConditions);
final boolean hasConditions = sizeOf(conditions) > 0;
final List<Tile> suggestions = getSuggestionsToShow(mSuggestions);
final List<Suggestion> suggestionsV2 = getSuggestionsV2ToShow(mSuggestionsV2);
final List<Suggestion> suggestions = getSuggestionsToShow(mSuggestions);
final int hiddenSuggestion;
if (useSuggestionV2) {
hiddenSuggestion = hasSuggestions ? sizeOf(mSuggestionsV2) - sizeOf(suggestionsV2) : 0;
} else {
hiddenSuggestion = hasSuggestions ? sizeOf(mSuggestions) - sizeOf(suggestions) : 0;
}
final int hiddenSuggestion = hasSuggestions
? sizeOf(mSuggestions) - sizeOf(suggestions)
: 0;
final boolean hasSuggestionAndCollapsed = hasSuggestions
&& mSuggestionConditionMode == HEADER_MODE_COLLAPSED;
@@ -231,15 +218,8 @@ public class DashboardData {
R.layout.suggestion_condition_header,
STABLE_ID_SUGGESTION_CONDITION_MIDDLE_HEADER, onlyHasConditionAndCollapsed);
if (useSuggestionV2) {
addToItemList(suggestionsV2, R.layout.suggestion_condition_container,
STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestionsV2) > 0);
} else {
/* Suggestion container. This is the card view that contains the list of suggestions.
* This will be added whenever the suggestion list is not empty */
addToItemList(suggestions, R.layout.suggestion_condition_container,
STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestions) > 0);
}
addToItemList(suggestions, R.layout.suggestion_condition_container,
STABLE_ID_SUGGESTION_CONTAINER, sizeOf(suggestions) > 0);
/* Second suggestion/condition header. This will be added when there is at least one
* suggestion or condition that is not currently displayed, and the user can expand the
@@ -296,22 +276,7 @@ public class DashboardData {
return result;
}
/**
* @deprecated in favor of {@link #getSuggestionsV2ToShow}.
*/
@Deprecated
private List<Tile> getSuggestionsToShow(List<Tile> suggestions) {
if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
return null;
}
if (mSuggestionConditionMode != HEADER_MODE_DEFAULT
|| suggestions.size() <= DEFAULT_SUGGESTION_COUNT) {
return suggestions;
}
return suggestions.subList(0, DEFAULT_SUGGESTION_COUNT);
}
private List<Suggestion> getSuggestionsV2ToShow(List<Suggestion> suggestions) {
private List<Suggestion> getSuggestionsToShow(List<Suggestion> suggestions) {
if (suggestions == null || mSuggestionConditionMode == HEADER_MODE_COLLAPSED) {
return null;
}
@@ -333,11 +298,6 @@ public class DashboardData {
private DashboardCategory mCategory;
private List<Condition> mConditions;
/**
* @deprecated in favor of SuggestionList
*/
@Deprecated
private List<Tile> mSuggestions;
private List<Suggestion> mSuggestionsV2;
public Builder() {
@@ -346,8 +306,7 @@ public class DashboardData {
public Builder(DashboardData dashboardData) {
mCategory = dashboardData.mCategory;
mConditions = dashboardData.mConditions;
mSuggestions = dashboardData.mSuggestions;
mSuggestionsV2 = dashboardData.mSuggestionsV2;
mSuggestionsV2 = dashboardData.mSuggestions;
mSuggestionConditionMode = dashboardData.mSuggestionConditionMode;
}
@@ -361,16 +320,7 @@ public class DashboardData {
return this;
}
/**
* @deprecated in favor of {@link #setSuggestionsV2(List)})}
*/
@Deprecated
public Builder setSuggestions(List<Tile> suggestions) {
this.mSuggestions = suggestions;
return this;
}
public Builder setSuggestionsV2(List<Suggestion> suggestions) {
public Builder setSuggestions(List<Suggestion> suggestions) {
this.mSuggestionsV2 = suggestions;
return this;
}

View File

@@ -19,7 +19,6 @@ package com.android.settings.dashboard;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.service.settings.suggestions.Suggestion;
@@ -41,19 +40,14 @@ import com.android.settings.dashboard.conditional.FocusRecyclerView;
import com.android.settings.dashboard.conditional.FocusRecyclerView.FocusListener;
import com.android.settings.dashboard.suggestions.SuggestionControllerMixin;
import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.ActionBarShadowController;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.SettingsDrawerActivity;
import com.android.settingslib.drawer.SettingsDrawerActivity.CategoryListener;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionList;
import com.android.settingslib.suggestions.SuggestionParser;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.List;
public class DashboardSummary extends InstrumentedFragment
@@ -73,11 +67,9 @@ public class DashboardSummary extends InstrumentedFragment
private DashboardAdapter mAdapter;
private SummaryLoader mSummaryLoader;
private ConditionManager mConditionManager;
private SuggestionParser mSuggestionParser;
private LinearLayoutManager mLayoutManager;
private SuggestionControllerMixin mSuggestionControllerMixin;
private DashboardFeatureProvider mDashboardFeatureProvider;
private SuggestionFeatureProvider mSuggestionFeatureProvider;
private boolean isOnCategoriesChangedCalled;
private boolean mOnConditionsChangedCalled;
@@ -92,13 +84,9 @@ public class DashboardSummary extends InstrumentedFragment
@Override
public void onAttach(Context context) {
super.onAttach(context);
mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
.getSuggestionFeatureProvider(context);
if (mSuggestionFeatureProvider.isSuggestionV2Enabled(context)) {
Log.d(TAG, "Suggestion v2 is enabled, creating SuggestionControllerMixin");
mSuggestionControllerMixin = new SuggestionControllerMixin(context, this /* host */,
getLifecycle());
}
Log.d(TAG, "Creating SuggestionControllerMixin");
mSuggestionControllerMixin = new SuggestionControllerMixin(context, this /* host */,
getLifecycle());
}
@Override
@@ -121,10 +109,6 @@ public class DashboardSummary extends InstrumentedFragment
mConditionManager = ConditionManager.get(activity, false);
getLifecycle().addObserver(mConditionManager);
if (mSuggestionFeatureProvider.isSuggestionEnabled(activity)) {
mSuggestionParser = new SuggestionParser(activity,
mSuggestionFeatureProvider.getSharedPrefs(activity), R.xml.suggestion_ordering);
}
if (DEBUG_TIMING) {
Log.d(TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
}
@@ -211,8 +195,7 @@ public class DashboardSummary extends InstrumentedFragment
mDashboard.setHasFixedSize(true);
mDashboard.setListener(this);
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(),
mSuggestionParser, mSuggestionControllerMixin,
this /* SuggestionDismissController.Callback */);
mSuggestionControllerMixin, this /* SuggestionDismissController.Callback */);
mDashboard.setAdapter(mAdapter);
mDashboard.setItemAnimator(new DashboardItemAnimator());
mSummaryLoader.setSummaryConsumer(mAdapter);
@@ -228,16 +211,7 @@ public class DashboardSummary extends InstrumentedFragment
@VisibleForTesting
void rebuildUI() {
if (!mSuggestionFeatureProvider.isSuggestionEnabled(getContext())) {
Log.d(TAG, "Suggestion v1 feature is disabled, skipping suggestion v1");
ThreadUtils.postOnBackgroundThread(() -> updateCategory());
} else {
new SuggestionLoader().execute();
// Set categories on their own if loading suggestions takes too long.
mHandler.postDelayed(() -> {
updateCategory();
}, MAX_WAIT_MILLIS);
}
ThreadUtils.postOnBackgroundThread(() -> updateCategory());
}
@Override
@@ -274,19 +248,9 @@ public class DashboardSummary extends InstrumentedFragment
@Override
public Suggestion getSuggestionAt(int position) {
return mAdapter.getSuggestionV2(position);
}
@Override
public Tile getSuggestionForPosition(int position) {
return mAdapter.getSuggestion(position);
}
@Override
public void onSuggestionDismissed(Tile suggestion) {
mAdapter.onSuggestionDismissed(suggestion);
}
@Override
public void onSuggestionDismissed(Suggestion suggestion) {
mAdapter.onSuggestionDismissed(suggestion);
@@ -295,7 +259,7 @@ public class DashboardSummary extends InstrumentedFragment
@Override
public void onSuggestionReady(List<Suggestion> suggestions) {
mStagingSuggestions = suggestions;
mAdapter.setSuggestionsV2(suggestions);
mAdapter.setSuggestions(suggestions);
if (mStagingCategory != null) {
Log.d(TAG, "Category has loaded, setting category from suggestionReady");
mHandler.removeCallbacksAndMessages(null);
@@ -303,49 +267,6 @@ public class DashboardSummary extends InstrumentedFragment
}
}
/**
* @deprecated in favor of {@link #mSuggestionControllerMixin}.
*/
@Deprecated
private class SuggestionLoader extends AsyncTask<Void, Void, List<Tile>> {
@Override
protected List<Tile> doInBackground(Void... params) {
final Context context = getContext();
boolean isSmartSuggestionEnabled =
mSuggestionFeatureProvider.isSmartSuggestionEnabled(context);
final SuggestionList sl = mSuggestionParser.getSuggestions(isSmartSuggestionEnabled);
final List<Tile> suggestions = sl.getSuggestions();
if (isSmartSuggestionEnabled) {
List<String> suggestionIds = new ArrayList<>(suggestions.size());
for (Tile suggestion : suggestions) {
suggestionIds.add(mSuggestionFeatureProvider.getSuggestionIdentifier(
context, suggestion));
}
// TODO: create a Suggestion class to maintain the id and other info
mSuggestionFeatureProvider.rankSuggestions(suggestions, suggestionIds);
}
for (int i = 0; i < suggestions.size(); i++) {
Tile suggestion = suggestions.get(i);
if (mSuggestionFeatureProvider.isSuggestionComplete(context,
suggestion.intent.getComponent())) {
suggestions.remove(i--);
}
}
if (sl.isExclusiveSuggestionCategory()) {
mSuggestionFeatureProvider.filterExclusiveSuggestions(suggestions);
}
return suggestions;
}
@Override
protected void onPostExecute(List<Tile> tiles) {
// tell handler that suggestions were loaded quickly enough
mHandler.removeCallbacksAndMessages(null);
updateCategoryAndSuggestion(tiles);
}
}
@WorkerThread
void updateCategory() {
final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
@@ -356,7 +277,7 @@ public class DashboardSummary extends InstrumentedFragment
Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
ThreadUtils.postOnMainThread(() -> {
if (mStagingSuggestions != null) {
mAdapter.setSuggestionsV2(mStagingSuggestions);
mAdapter.setSuggestions(mStagingSuggestions);
}
mAdapter.setCategory(mStagingCategory);
});
@@ -365,26 +286,4 @@ public class DashboardSummary extends InstrumentedFragment
mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
}
}
/**
* @deprecated in favor of {@link #mSuggestionControllerMixin}.
*/
@Deprecated
@VisibleForTesting
void updateCategoryAndSuggestion(List<Tile> suggestions) {
final Activity activity = getActivity();
if (activity == null) {
return;
}
final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
CategoryKey.CATEGORY_HOMEPAGE);
mSummaryLoader.updateSummaryToCache(category);
if (suggestions != null) {
mAdapter.setCategoriesAndSuggestions(category, suggestions);
} else {
mAdapter.setCategory(category);
}
}
}

View File

@@ -1,102 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.dashboard.suggestions;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* Stores suggestion related statistics.
*/
public class EventStore {
public static final String TAG = "SuggestionEventStore";
public static final String EVENT_SHOWN = "shown";
public static final String EVENT_DISMISSED = "dismissed";
public static final String EVENT_CLICKED = "clicked";
public static final String METRIC_LAST_EVENT_TIME = "last_event_time";
public static final String METRIC_COUNT = "count";
private static final Set<String> EVENTS = new HashSet<String>(
Arrays.asList(new String[] {EVENT_SHOWN, EVENT_DISMISSED, EVENT_CLICKED}));
private static final Set<String> METRICS = new HashSet<String>(
Arrays.asList(new String[] {METRIC_LAST_EVENT_TIME, METRIC_COUNT}));
private final SharedPreferences mSharedPrefs;
public EventStore(Context context) {
mSharedPrefs = context.getSharedPreferences(TAG, Context.MODE_PRIVATE);
}
/**
* Writes individual log events.
* @param pkgName: Package for which this event is reported.
* @param eventType: Type of event (one of {@link #EVENTS}).
*/
public void writeEvent(String pkgName, String eventType) {
if (!EVENTS.contains(eventType)) {
Log.w(TAG, "Reported event type " + eventType + " is not a valid type!");
return;
}
final String lastTimePrefKey = getPrefKey(pkgName, eventType, METRIC_LAST_EVENT_TIME);
final String countPrefKey = getPrefKey(pkgName, eventType, METRIC_COUNT);
writePref(lastTimePrefKey, System.currentTimeMillis());
writePref(countPrefKey, readPref(countPrefKey, (long) 0) + 1);
}
/**
* Reads metric of the the reported events (e.g., counts).
* @param pkgName: Package for which this metric is queried.
* @param eventType: Type of event (one of {@link #EVENTS}).
* @param metricType: Type of the queried metric (one of {@link #METRICS}).
* @return the corresponding metric.
*/
public long readMetric(String pkgName, String eventType, String metricType) {
if (!EVENTS.contains(eventType)) {
Log.w(TAG, "Reported event type " + eventType + " is not a valid event!");
return 0;
} else if (!METRICS.contains(metricType)) {
Log.w(TAG, "Required stat type + " + metricType + " is not a valid stat!");
return 0;
}
return readPref(getPrefKey(pkgName, eventType, metricType), (long) 0);
}
private void writePref(String prefKey, long value) {
mSharedPrefs.edit().putLong(prefKey, value).apply();
}
private long readPref(String prefKey, Long defaultValue) {
return mSharedPrefs.getLong(prefKey, defaultValue);
}
private String getPrefKey(String pkgName, String eventType, String statType) {
return new StringBuilder()
.append("setting_suggestion_")
.append(pkgName)
.append("_")
.append(eventType)
.append("_")
.append(statType)
.toString();
}
}

View File

@@ -27,12 +27,10 @@ import android.view.ViewGroup;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.instrumentation.MetricsFeatureProvider;
import com.android.settings.dashboard.DashboardAdapter.DashboardItemHolder;
import com.android.settings.dashboard.DashboardAdapter.IconCache;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.drawer.Tile;
import java.util.List;
import java.util.Objects;
@@ -42,26 +40,20 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
@Deprecated // in favor of mSuggestionsV2
private final List<Tile> mSuggestions;
private final List<Suggestion> mSuggestionsV2;
private final List<Suggestion> mSuggestions;
private final IconCache mCache;
private final List<String> mSuggestionsShownLogged;
private final SuggestionControllerMixin mSuggestionControllerMixin;
public SuggestionAdapter(Context context, SuggestionControllerMixin suggestionControllerMixin,
List<Tile> suggestions, List<Suggestion> suggestionsV2,
List<String> suggestionsShownLogged) {
List<Suggestion> suggestions, List<String> suggestionsShownLogged) {
mContext = context;
mSuggestionControllerMixin = suggestionControllerMixin;
mSuggestions = suggestions;
mSuggestionsV2 = suggestionsV2;
mSuggestionsShownLogged = suggestionsShownLogged;
mCache = new IconCache(context);
final FeatureFactory factory = FeatureFactory.getFactory(context);
mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
setHasStableIds(true);
}
@@ -74,15 +66,11 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
@Override
public void onBindViewHolder(DashboardItemHolder holder, int position) {
if (mSuggestions != null) {
bindSuggestionTile(holder, position);
} else {
bindSuggestion(holder, position);
}
bindSuggestion(holder, position);
}
private void bindSuggestion(DashboardItemHolder holder, int position) {
final Suggestion suggestion = mSuggestionsV2.get(position);
final Suggestion suggestion = mSuggestions.get(position);
final String id = suggestion.getId();
if (!mSuggestionsShownLogged.contains(id)) {
mMetricsFeatureProvider.action(
@@ -101,7 +89,7 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
}
final View divider = holder.itemView.findViewById(R.id.divider);
if (divider != null) {
divider.setVisibility(position < mSuggestionsV2.size() - 1 ? View.VISIBLE : View.GONE);
divider.setVisibility(position < mSuggestions.size() - 1 ? View.VISIBLE : View.GONE);
}
View clickHandler = holder.itemView;
// If a view with @android:id/primary is defined, use that as the click handler
@@ -121,110 +109,32 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
});
}
/**
* @deprecated in favor {@link #bindSuggestion(DashboardItemHolder, int)}.
*/
@Deprecated
private void bindSuggestionTile(DashboardItemHolder holder, int position) {
final Tile suggestion = (Tile) mSuggestions.get(position);
final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier(
mContext, suggestion);
// This is for cases when a suggestion is dismissed and the next one comes to view
if (!mSuggestionsShownLogged.contains(suggestionId)) {
mMetricsFeatureProvider.action(
mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, suggestionId,
mSuggestionFeatureProvider.getLoggingTaggedData(mContext));
mSuggestionsShownLogged.add(suggestionId);
}
if (suggestion.remoteViews != null) {
final ViewGroup itemView = (ViewGroup) holder.itemView;
itemView.removeAllViews();
itemView.addView(suggestion.remoteViews.apply(itemView.getContext(), itemView));
} else {
holder.icon.setImageDrawable(mCache.getIcon(suggestion.icon));
holder.title.setText(suggestion.title);
if (!TextUtils.isEmpty(suggestion.summary)) {
holder.summary.setText(suggestion.summary);
holder.summary.setVisibility(View.VISIBLE);
} else {
holder.summary.setVisibility(View.GONE);
}
}
final View divider = holder.itemView.findViewById(R.id.divider);
if (divider != null) {
divider.setVisibility(position < mSuggestions.size() - 1 ? View.VISIBLE : View.GONE);
}
View clickHandler = holder.itemView;
// If a view with @android:id/primary is defined, use that as the click handler
// instead.
final View primaryAction = holder.itemView.findViewById(android.R.id.primary);
if (primaryAction != null) {
clickHandler = primaryAction;
}
clickHandler.setOnClickListener(v -> {
mMetricsFeatureProvider.action(mContext,
MetricsEvent.ACTION_SETTINGS_SUGGESTION, suggestionId,
mSuggestionFeatureProvider.getLoggingTaggedData(mContext));
((SettingsActivity) mContext).startSuggestion(suggestion.intent);
});
}
@Override
public long getItemId(int position) {
if (mSuggestions != null) {
return Objects.hash(mSuggestions.get(position).title);
} else {
return Objects.hash(mSuggestionsV2.get(position).getId());
}
return Objects.hash(mSuggestions.get(position).getId());
}
@Override
public int getItemViewType(int position) {
if (mSuggestions != null) {
Tile suggestion = getSuggestion(position);
return suggestion.remoteViews != null
? R.layout.suggestion_tile_remote_container
: R.layout.suggestion_tile;
final Suggestion suggestion = getSuggestion(position);
if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
return R.layout.suggestion_tile_with_button;
} else {
final Suggestion suggestion = getSuggestionsV2(position);
if ((suggestion.getFlags() & Suggestion.FLAG_HAS_BUTTON) != 0) {
return R.layout.suggestion_tile_with_button;
} else {
return R.layout.suggestion_tile;
}
return R.layout.suggestion_tile;
}
}
@Override
public int getItemCount() {
if (mSuggestions != null) {
return mSuggestions.size();
} else {
return mSuggestionsV2.size();
}
return mSuggestions.size();
}
public Tile getSuggestion(int position) {
public Suggestion getSuggestion(int position) {
final long itemId = getItemId(position);
if (mSuggestions == null) {
return null;
}
for (Tile tile : mSuggestions) {
if (Objects.hash(tile.title) == itemId) {
return tile;
}
}
return null;
}
public Suggestion getSuggestionsV2(int position) {
final long itemId = getItemId(position);
if (mSuggestionsV2 == null) {
return null;
}
for (Suggestion suggestion : mSuggestionsV2) {
for (Suggestion suggestion : mSuggestions) {
if (Objects.hash(suggestion.getId()) == itemId) {
return suggestion;
}
@@ -232,13 +142,8 @@ public class SuggestionAdapter extends RecyclerView.Adapter<DashboardItemHolder>
return null;
}
public void removeSuggestion(Tile suggestion) {
public void removeSuggestion(Suggestion suggestion) {
mSuggestions.remove(suggestion);
notifyDataSetChanged();
}
public void removeSuggestion(Suggestion suggestion) {
mSuggestionsV2.remove(suggestion);
notifyDataSetChanged();
}
}

View File

@@ -23,27 +23,10 @@ import android.support.v7.widget.helper.ItemTouchHelper;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionParser;
public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback {
public interface Callback {
/**
* @deprecated in favor of {@link #getSuggestionAt(int)}
* Returns suggestion tile data from the callback
*/
@Deprecated
Tile getSuggestionForPosition(int position);
/**
* @deprecated in favor of {@link #onSuggestionDismissed(Suggestion)}
* Called when a suggestion is dismissed.
*/
@Deprecated
void onSuggestionDismissed(Tile suggestion);
/**
* Returns suggestion tile data from the callback
*/
@@ -58,20 +41,13 @@ public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback
private final Context mContext;
private final SuggestionFeatureProvider mSuggestionFeatureProvider;
private final SuggestionControllerMixin mSuggestionMixin;
/**
* @deprecated in favor of the new Suggestion backend.
*/
@Deprecated
private final SuggestionParser mSuggestionParser;
private final Callback mCallback;
public SuggestionDismissController(Context context, RecyclerView recyclerView,
SuggestionControllerMixin suggestionMixin, SuggestionParser parser, Callback callback) {
SuggestionControllerMixin suggestionMixin, Callback callback) {
super(0, ItemTouchHelper.START | ItemTouchHelper.END);
mSuggestionMixin = suggestionMixin;
mContext = context;
mSuggestionParser = parser;
mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
.getSuggestionFeatureProvider(context);
mCallback = callback;
@@ -103,13 +79,7 @@ public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback
}
final int position = viewHolder.getAdapterPosition();
final Suggestion suggestionV2 = mCallback.getSuggestionAt(position);
if (suggestionV2 != null) {
mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionMixin, suggestionV2);
mCallback.onSuggestionDismissed(suggestionV2);
} else {
final Tile suggestion = mCallback.getSuggestionForPosition(position);
mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionParser, suggestion);
mCallback.onSuggestionDismissed(suggestion);
}
mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionMixin, suggestionV2);
mCallback.onSuggestionDismissed(suggestionV2);
}
}

View File

@@ -24,7 +24,6 @@ import android.support.annotation.NonNull;
import android.util.Pair;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionParser;
import java.util.List;
@@ -33,14 +32,6 @@ public interface SuggestionFeatureProvider {
/**
* Whether or not the whole suggestion feature is enabled.
*
* @deprecated in favor of {@link #isSuggestionV2Enabled(Context)}
*/
@Deprecated
boolean isSuggestionEnabled(Context context);
/**
* Whether or not the suggestion v2 feature is enabled.
*/
boolean isSuggestionV2Enabled(Context context);
@@ -62,44 +53,17 @@ public interface SuggestionFeatureProvider {
*/
SharedPreferences getSharedPrefs(Context context);
/**
* Ranks the list of suggestions in place.
*
* @param suggestions List of suggestion Tiles
* @param suggestionIds List of suggestion ids corresponding to the suggestion tiles.
* @deprecated in favor of SettingsIntelligence
*/
@Deprecated
void rankSuggestions(final List<Tile> suggestions, List<String> suggestionIds);
/**
* Only keep top few suggestions from exclusive suggestions.
*/
void filterExclusiveSuggestions(List<Tile> suggestions);
/**
* Dismisses a suggestion.
*
* @deprecated in favor of {@link #dismissSuggestion(Context, SuggestionControllerMixin,
* Suggestion)}
*/
@Deprecated
void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion);
/**
* Dismisses a suggestion.
*/
void dismissSuggestion(Context context, SuggestionControllerMixin suggestionMixin,
Suggestion suggestion);
/**
* Returns an identifier for the suggestion
*
* @deprecated in favor or {@link Suggestion#getId()}
*/
@Deprecated
String getSuggestionIdentifier(Context context, Tile suggestion);
/**
* Returns common tagged data for suggestion logging.
*/

View File

@@ -16,19 +16,15 @@
package com.android.settings.dashboard.suggestions;
import static com.android.settings.core.FeatureFlags.SUGGESTIONS_V2;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.provider.Settings.Secure;
import android.service.settings.suggestions.Suggestion;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
@@ -54,7 +50,6 @@ import com.android.settings.support.NewDeviceIntroSuggestionActivity;
import com.android.settings.wallpaper.WallpaperSuggestionActivity;
import com.android.settings.wifi.WifiCallingSuggestionActivity;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionParser;
import java.util.List;
@@ -65,23 +60,13 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
private static final String SHARED_PREF_FILENAME = "suggestions";
private final SuggestionRanker mSuggestionRanker;
private final MetricsFeatureProvider mMetricsFeatureProvider;
@Override
public boolean isSuggestionEnabled(Context context) {
final ActivityManager am =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowRamDevice = am.isLowRamDevice();
return !isLowRamDevice && !isV2Enabled(context);
}
@Override
public boolean isSuggestionV2Enabled(Context context) {
final ActivityManager am =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowRamDevice = am.isLowRamDevice();
return !isLowRamDevice && isV2Enabled(context);
return !am.isLowRamDevice();
}
@Override
@@ -91,10 +76,6 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
"com.android.settings.intelligence.suggestions.SuggestionService");
}
private static boolean isV2Enabled(Context context) {
return FeatureFlagUtils.isEnabled(context, SUGGESTIONS_V2);
}
@Override
public boolean isSmartSuggestionEnabled(Context context) {
return false;
@@ -143,17 +124,10 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
public SuggestionFeatureProviderImpl(Context context) {
final Context appContext = context.getApplicationContext();
mSuggestionRanker = new SuggestionRanker(
new SuggestionFeaturizer(new EventStore(appContext)));
mMetricsFeatureProvider = FeatureFactory.getFactory(appContext)
.getMetricsFeatureProvider();
}
@Override
public void rankSuggestions(final List<Tile> suggestions, List<String> suggestionIds) {
mSuggestionRanker.rankSuggestions(suggestions, suggestionIds);
}
@Override
public void filterExclusiveSuggestions(List<Tile> suggestions) {
if (suggestions == null) {
@@ -165,26 +139,6 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
}
}
@Override
public void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion) {
if (parser == null || suggestion == null || context == null) {
return;
}
final Pair<Integer, Object>[] taggedData = getLoggingTaggedData(context);
mMetricsFeatureProvider.action(
context, MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
getSuggestionIdentifier(context, suggestion),
taggedData);
if (!parser.dismissSuggestion(suggestion)) {
return;
}
context.getPackageManager().setComponentEnabledSetting(
suggestion.intent.getComponent(),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
@Override
public void dismissSuggestion(Context context, SuggestionControllerMixin mixin,
Suggestion suggestion) {
@@ -197,25 +151,10 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
mixin.dismissSuggestion(suggestion);
}
@Override
public String getSuggestionIdentifier(Context context, Tile suggestion) {
if (suggestion.intent == null || suggestion.intent.getComponent() == null
|| context == null) {
return "unknown_suggestion";
}
String packageName = suggestion.intent.getComponent().getPackageName();
if (packageName.equals(context.getPackageName())) {
// Since Settings provides several suggestions, fill in the class instead of the
// package for these.
packageName = suggestion.intent.getComponent().getClassName();
}
return packageName;
}
@Override
public Pair<Integer, Object>[] getLoggingTaggedData(Context context) {
final boolean isSmartSuggestionEnabled = isSmartSuggestionEnabled(context);
return new Pair[]{Pair.create(
return new Pair[] {Pair.create(
MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED,
isSmartSuggestionEnabled ? 1 : 0)};
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.dashboard.suggestions;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Creates a set of interaction features (i.e., metrics) to represent each setting suggestion. These
* features currently include normalized time from previous events (shown, dismissed and clicked)
* for any particular suggestion and also counts of these events. These features are used as signals
* to find the best ranking for suggestion items.
*/
public class SuggestionFeaturizer {
// Key of the features used for ranking.
public static final String FEATURE_IS_SHOWN = "is_shown";
public static final String FEATURE_IS_DISMISSED = "is_dismissed";
public static final String FEATURE_IS_CLICKED = "is_clicked";
public static final String FEATURE_TIME_FROM_LAST_SHOWN = "time_from_last_shown";
public static final String FEATURE_TIME_FROM_LAST_DISMISSED = "time_from_last_dismissed";
public static final String FEATURE_TIME_FROM_LAST_CLICKED = "time_from_last_clicked";
public static final String FEATURE_SHOWN_COUNT = "shown_count";
public static final String FEATURE_DISMISSED_COUNT = "dismissed_count";
public static final String FEATURE_CLICKED_COUNT = "clicked_count";
// The following numbers are estimated from histograms.
public static final double TIME_NORMALIZATION_FACTOR = 2e10;
public static final double COUNT_NORMALIZATION_FACTOR = 500;
private final EventStore mEventStore;
/**
* Constructor
*
* @param eventStore An instance of {@code EventStore} which maintains the recorded suggestion
* events.
*/
public SuggestionFeaturizer(EventStore eventStore) {
mEventStore = eventStore;
}
/**
* Extracts the features for each package name.
*
* @param pkgNames: List of package names for which features are queried.
* @return A Map containing the features, keyed by the package names. Each map value contains
* another map with key-value pairs of the features.
*/
public Map<String, Map<String, Double>> featurize(List<String> pkgNames) {
Map<String, Map<String, Double>> features = new HashMap<>();
Long curTimeMs = System.currentTimeMillis();
for (String pkgName : pkgNames) {
Map<String, Double> featureMap = new HashMap<>();
features.put(pkgName, featureMap);
Long lastShownTime = mEventStore
.readMetric(pkgName, EventStore.EVENT_SHOWN, EventStore.METRIC_LAST_EVENT_TIME);
Long lastDismissedTime = mEventStore.readMetric(pkgName, EventStore.EVENT_DISMISSED,
EventStore.METRIC_LAST_EVENT_TIME);
Long lastClickedTime = mEventStore.readMetric(pkgName, EventStore.EVENT_CLICKED,
EventStore.METRIC_LAST_EVENT_TIME);
featureMap.put(FEATURE_IS_SHOWN, booleanToDouble(lastShownTime > 0));
featureMap.put(FEATURE_IS_DISMISSED, booleanToDouble(lastDismissedTime > 0));
featureMap.put(FEATURE_IS_CLICKED, booleanToDouble(lastClickedTime > 0));
featureMap.put(FEATURE_TIME_FROM_LAST_SHOWN,
normalizedTimeDiff(curTimeMs, lastShownTime));
featureMap.put(FEATURE_TIME_FROM_LAST_DISMISSED,
normalizedTimeDiff(curTimeMs, lastDismissedTime));
featureMap.put(FEATURE_TIME_FROM_LAST_CLICKED,
normalizedTimeDiff(curTimeMs, lastClickedTime));
featureMap.put(FEATURE_SHOWN_COUNT, normalizedCount(mEventStore
.readMetric(pkgName, EventStore.EVENT_SHOWN, EventStore.METRIC_COUNT)));
featureMap.put(FEATURE_DISMISSED_COUNT, normalizedCount(mEventStore
.readMetric(pkgName, EventStore.EVENT_DISMISSED, EventStore.METRIC_COUNT)));
featureMap.put(FEATURE_CLICKED_COUNT, normalizedCount(mEventStore
.readMetric(pkgName, EventStore.EVENT_CLICKED, EventStore.METRIC_COUNT)));
}
return features;
}
private static double booleanToDouble(boolean bool) {
return bool ? 1 : 0;
}
private static double normalizedTimeDiff(long curTimeMs, long preTimeMs) {
return Math.min(1, (curTimeMs - preTimeMs) / TIME_NORMALIZATION_FACTOR);
}
private static double normalizedCount(long count) {
return Math.min(1, count / COUNT_NORMALIZATION_FACTOR);
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.dashboard.suggestions;
import com.android.settingslib.drawer.Tile;
import android.support.annotation.VisibleForTesting;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SuggestionRanker {
private static final String TAG = "SuggestionRanker";
// The following coefficients form a linear model, which mixes the features to obtain a
// relevance metric for ranking the suggestion items. This model is learned with off-line data
// by training a binary classifier to detect the clicked items. The higher the obtained
// relevance metric, the higher chance of getting clicked.
private static final Map<String, Double> WEIGHTS = new HashMap<String, Double>() {{
put(SuggestionFeaturizer.FEATURE_IS_SHOWN, 5.05140842519);
put(SuggestionFeaturizer.FEATURE_IS_DISMISSED, 2.29641455171);
put(SuggestionFeaturizer.FEATURE_IS_CLICKED, -2.98812233623);
put(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_SHOWN, 5.02807250202);
put(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_DISMISSED, 2.49589700842);
put(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_CLICKED, -4.3377039948);
put(SuggestionFeaturizer.FEATURE_SHOWN_COUNT, -2.35993512546);
}};
private final SuggestionFeaturizer mSuggestionFeaturizer;
private final Map<Tile, Double> relevanceMetrics;
Comparator<Tile> suggestionComparator = new Comparator<Tile>() {
@Override
public int compare(Tile suggestion1, Tile suggestion2) {
return relevanceMetrics.get(suggestion1) < relevanceMetrics.get(suggestion2) ? 1 : -1;
}
};
public SuggestionRanker(SuggestionFeaturizer suggestionFeaturizer) {
mSuggestionFeaturizer = suggestionFeaturizer;
relevanceMetrics = new HashMap<Tile, Double>();
}
public void rankSuggestions(final List<Tile> suggestions, List<String> suggestionIds) {
relevanceMetrics.clear();
Map<String, Map<String, Double>> features = mSuggestionFeaturizer.featurize(suggestionIds);
for (int i = 0; i < suggestionIds.size(); i++) {
relevanceMetrics.put(suggestions.get(i),
getRelevanceMetric(features.get(suggestionIds.get(i))));
}
Collections.sort(suggestions, suggestionComparator);
}
@VisibleForTesting
double getRelevanceMetric(Map<String, Double> features) {
double sum = 0;
if (features == null) {
return sum;
}
for (String feature : WEIGHTS.keySet()) {
sum += WEIGHTS.get(feature) * features.get(feature);
}
return sum;
}
}

View File

@@ -28,13 +28,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
@@ -46,7 +43,6 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.dashboard.conditional.ConditionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionAdapter;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -94,12 +90,6 @@ public class DashboardAdapterTest {
MockitoAnnotations.initMocks(this);
mFactory = FakeFeatureFactory.setupForTest();
when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true);
when(mFactory.suggestionsFeatureProvider
.getSuggestionIdentifier(any(Context.class), any(Tile.class)))
.thenAnswer(invocation -> {
final Object[] args = invocation.getArguments();
return ((Tile) args[1]).intent.getComponent().getPackageName();
});
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getQuantityString(any(int.class), any(int.class), any()))
@@ -108,14 +98,14 @@ public class DashboardAdapterTest {
mConditionList = new ArrayList<>();
mConditionList.add(mCondition);
when(mCondition.shouldShow()).thenReturn(true);
mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null, null);
mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null);
mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1);
when(mView.getTag()).thenReturn(mCondition);
}
@Test
public void testSuggestionsLogs_nullSuggestionsList_shouldNotCrash() {
setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5"));
setupSuggestions(makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4", "pkg5"));
mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData);
// set suggestions to null
@@ -131,9 +121,9 @@ public class DashboardAdapterTest {
@Test
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
final DashboardAdapter adapter =
spy(new DashboardAdapter(mContext, null, null, null, null, null));
final List<Tile> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3");
adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
spy(new DashboardAdapter(mContext, null, null, null, null));
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3");
adapter.setSuggestions(suggestions);
final RecyclerView data = mock(RecyclerView.class);
when(data.getResources()).thenReturn(mResources);
@@ -150,7 +140,7 @@ public class DashboardAdapterTest {
final DashboardData dashboardData = adapter.mDashboardData;
reset(adapter); // clear interactions tracking
final Tile suggestionToRemove = suggestions.get(1);
final Suggestion suggestionToRemove = suggestions.get(1);
adapter.onSuggestionDismissed(suggestionToRemove);
assertThat(adapter.mDashboardData).isEqualTo(dashboardData);
@@ -166,12 +156,11 @@ public class DashboardAdapterTest {
when(itemView.findViewById(R.id.data)).thenReturn(data);
final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
final List<Tile> suggestions =
makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4");
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1", "pkg2", "pkg3", "pkg4");
final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */,
null /* conditions */, null /* suggestionParser */,
null /* conditions */,
null /* suggestionControllerMixin */, null /* callback */));
adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
adapter.setSuggestions(suggestions);
adapter.onBindConditionAndSuggestion(
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
// default mode, only displaying 2 suggestions
@@ -180,16 +169,16 @@ public class DashboardAdapterTest {
// verify operations that access the lists will not cause ConcurrentModificationException
assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(1);
adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
adapter.setSuggestions(suggestions);
// should not crash
}
@Test
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
DashboardAdapter adapter =
spy(new DashboardAdapter(mContext, null, null, null, null, null));
final List<Tile> suggestions = makeSuggestions("pkg1");
adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
spy(new DashboardAdapter(mContext, null, null, null, null));
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
adapter.setSuggestions(suggestions);
final DashboardData dashboardData = adapter.mDashboardData;
reset(adapter); // clear interactions tracking
@@ -199,22 +188,6 @@ public class DashboardAdapterTest {
verify(adapter).notifyDashboardDataChanged(any());
}
@Test
public void testSetCategoriesAndSuggestions_iconTinted() {
TypedArray mockTypedArray = mock(TypedArray.class);
doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class));
doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt());
List<Tile> packages = makeSuggestions("pkg1");
Icon mockIcon = mock(Icon.class);
packages.get(0).isIconTintable = true;
packages.get(0).icon = mockIcon;
mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, packages);
verify(mockIcon).setTint(eq(0x89000000));
}
@Test
public void testSetCategories_iconTinted() {
TypedArray mockTypedArray = mock(TypedArray.class);
@@ -233,49 +206,14 @@ public class DashboardAdapterTest {
verify(mockIcon).setTint(eq(0x89000000));
}
@Test
public void testSetCategoriesAndSuggestions_limitSuggestionSize() {
List<Tile> packages =
makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5", "pkg6", "pkg7");
mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, packages);
assertThat(mDashboardAdapter.mDashboardData.getSuggestions().size())
.isEqualTo(DashboardAdapter.MAX_SUGGESTION_TO_SHOW);
}
@Test
public void testBindConditionAndSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null, null);
final List<Tile> suggestions = makeSuggestions("pkg1");
final DashboardCategory category = new DashboardCategory();
category.addTile(mock(Tile.class));
mDashboardAdapter.setCategoriesAndSuggestions(category, suggestions);
final RecyclerView data = mock(RecyclerView.class);
when(data.getResources()).thenReturn(mResources);
when(data.getContext()).thenReturn(mContext);
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
final View itemView = mock(View.class);
when(itemView.findViewById(R.id.data)).thenReturn(data);
final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
mDashboardAdapter.onBindConditionAndSuggestion(
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
verify(data).setAdapter(any(SuggestionAdapter.class));
// should not crash
}
@Test
public void testBindConditionAndSuggestion_v2_shouldSetSuggestionAdapterAndNoCrash() {
mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null, null);
mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null);
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
final DashboardCategory category = new DashboardCategory();
category.addTile(mock(Tile.class));
mDashboardAdapter.setSuggestionsV2(suggestions);
mDashboardAdapter.setSuggestions(suggestions);
final RecyclerView data = mock(RecyclerView.class);
when(data.getResources()).thenReturn(mResources);
@@ -293,50 +231,6 @@ public class DashboardAdapterTest {
// should not crash
}
@Test
public void testBindConditionAndSuggestion_emptySuggestion_shouldSetConditionAdpater() {
final Bundle savedInstance = new Bundle();
savedInstance.putInt(DashboardAdapter.STATE_SUGGESTION_CONDITION_MODE,
DashboardData.HEADER_MODE_FULLY_EXPANDED);
mDashboardAdapter = new DashboardAdapter(mContext, savedInstance, mConditionList,
null /* SuggestionParser */, null /* suggestionControllerMixin */,
null /* SuggestionDismissController.Callback */);
final List<Tile> suggestions = new ArrayList<>();
final DashboardCategory category = new DashboardCategory();
category.addTile(mock(Tile.class));
mDashboardAdapter.setCategoriesAndSuggestions(category, suggestions);
final RecyclerView data = mock(RecyclerView.class);
when(data.getResources()).thenReturn(mResources);
when(data.getContext()).thenReturn(mContext);
when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class));
final View itemView = mock(View.class);
when(itemView.findViewById(R.id.data)).thenReturn(data);
final DashboardAdapter.SuggestionAndConditionContainerHolder holder =
new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView);
mDashboardAdapter.onBindConditionAndSuggestion(
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
verify(data).setAdapter(any(ConditionAdapter.class));
}
/**
* @deprecated in favor of {@link #makeSuggestionsV2(String...)}
*/
@Deprecated
private List<Tile> makeSuggestions(String... pkgNames) {
final List<Tile> suggestions = new ArrayList<>();
for (String pkgName : pkgNames) {
Tile suggestion = new Tile();
suggestion.intent = new Intent("action");
suggestion.intent.setComponent(new ComponentName(pkgName, "cls"));
suggestions.add(suggestion);
}
return suggestions;
}
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
final List<Suggestion> suggestions = new ArrayList<>();
for (String pkgName : pkgNames) {
@@ -348,13 +242,11 @@ public class DashboardAdapterTest {
return suggestions;
}
private void setupSuggestions(List<Tile> suggestions) {
mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, suggestions);
private void setupSuggestions(List<Suggestion> suggestions) {
final Context context = RuntimeEnvironment.application;
mDashboardAdapter.setSuggestions(suggestions);
mSuggestionHolder = new DashboardAdapter.SuggestionAndConditionHeaderHolder(
LayoutInflater.from(context).inflate(
R.layout.suggestion_condition_header, new RelativeLayout(context), true));
}
}

View File

@@ -23,10 +23,11 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.PendingIntent;
import android.service.settings.suggestions.Suggestion;
import android.support.annotation.NonNull;
import android.support.v7.util.DiffUtil;
import android.support.v7.util.ListUpdateCallback;
import android.widget.RemoteViews;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.conditional.AirplaneModeCondition;
@@ -61,11 +62,10 @@ public class DashboardDataTest {
@Mock
private Tile mTestCategoryTile;
@Mock
private Tile mTestSuggestion;
@Mock
private Condition mTestCondition;
@Mock
private Condition mSecondCondition; // condition used to test insert in DiffUtil
private Suggestion mTestSuggestion;
@Before
public void SetUp() {
@@ -74,8 +74,11 @@ public class DashboardDataTest {
mDashboardCategory = new DashboardCategory();
// Build suggestions
final List<Tile> suggestions = new ArrayList<>();
mTestSuggestion.title = TEST_SUGGESTION_TITLE;
final List<Suggestion> suggestions = new ArrayList<>();
mTestSuggestion = new Suggestion.Builder("pkg")
.setTitle(TEST_SUGGESTION_TITLE)
.setPendingIntent(mock(PendingIntent.class))
.build();
suggestions.add(mTestSuggestion);
// Build oneItemConditions
@@ -227,8 +230,7 @@ public class DashboardDataTest {
final List<Condition> oneItemConditions = new ArrayList<>();
when(mTestCondition.shouldShow()).thenReturn(true);
oneItemConditions.add(mTestCondition);
final List<Tile> suggestions = new ArrayList<>();
mTestSuggestion.title = TEST_SUGGESTION_TITLE;
final List<Suggestion> suggestions = new ArrayList<>();
suggestions.add(mTestSuggestion);
final DashboardData oldData = new DashboardData.Builder()
@@ -261,20 +263,16 @@ public class DashboardDataTest {
public void testDiffUtil_typeSuggestedContainer_ResultDataNothingChanged() {
//Build testResultData
final List<ListUpdateResult.ResultData> testResultData = new ArrayList<>();
testResultData.add(new ListUpdateResult.ResultData(
ListUpdateResult.ResultData.TYPE_OPERATION_CHANGE, 0, 1));
Tile tile = new Tile();
tile.remoteViews = mock(RemoteViews.class);
DashboardData prevData = new DashboardData.Builder()
.setConditions(null)
.setCategory(null)
.setSuggestions(Arrays.asList(tile))
.setSuggestions(Arrays.asList(mTestSuggestion))
.build();
DashboardData currentData = new DashboardData.Builder()
.setConditions(null)
.setCategory(null)
.setSuggestions(Arrays.asList(tile))
.setSuggestions(Arrays.asList(mTestSuggestion))
.build();
testDiffUtil(prevData, currentData, testResultData);
}

View File

@@ -16,25 +16,6 @@
package com.android.settings.dashboard;
import android.app.Activity;
import android.support.v7.widget.LinearLayoutManager;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.conditional.ConditionManager;
import com.android.settings.dashboard.conditional.FocusRecyclerView;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
@@ -45,6 +26,27 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.LinearLayoutManager;
import com.android.settings.TestConfig;
import com.android.settings.dashboard.conditional.ConditionManager;
import com.android.settings.dashboard.conditional.FocusRecyclerView;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -63,11 +65,13 @@ public class DashboardSummaryTest {
@Mock
private SummaryLoader mSummaryLoader;
private Context mContext;
private DashboardSummary mSummary;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mSummary = spy(new DashboardSummary());
ReflectionHelpers.setField(mSummary, "mAdapter", mAdapter);
ReflectionHelpers.setField(mSummary, "mDashboardFeatureProvider",
@@ -79,9 +83,10 @@ public class DashboardSummaryTest {
}
@Test
public void updateCategoryAndSuggestion_shouldGetCategoryFromFeatureProvider() {
public void updateCategory_shouldGetCategoryFromFeatureProvider() {
doReturn(mock(Activity.class)).when(mSummary).getActivity();
mSummary.updateCategoryAndSuggestion(null);
mSummary.onAttach(mContext);
mSummary.updateCategory();
verify(mSummaryLoader).updateSummaryToCache(nullable(DashboardCategory.class));
verify(mDashboardFeatureProvider).getTilesForCategory(CategoryKey.CATEGORY_HOMEPAGE);
@@ -135,7 +140,7 @@ public class DashboardSummaryTest {
@Test
public void onSuggestionDismissed_shouldNotRebuildUI() {
mSummary.onSuggestionDismissed(mock(Tile.class));
mSummary.onSuggestionDismissed(mock(Suggestion.class));
verify(mSummary, never()).rebuildUI();
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.dashboard.suggestions;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class EventStoreTest {
private EventStore mEventStore;
@Before
public void setUp() {
mEventStore = new EventStore(RuntimeEnvironment.application);
}
@Test
public void testWriteRead() {
mEventStore.writeEvent("pkg", EventStore.EVENT_SHOWN);
long timeMs = System.currentTimeMillis();
assertThat(mEventStore.readMetric("pkg", EventStore.EVENT_SHOWN, EventStore.METRIC_COUNT))
.isEqualTo(1);
assertThat(Math.abs(timeMs - mEventStore
.readMetric("pkg", EventStore.EVENT_SHOWN, EventStore.METRIC_LAST_EVENT_TIME)) < 10000)
.isTrue();
}
@Test
public void testWriteRead_shouldHaveLatestValues() {
mEventStore.writeEvent("pkg", EventStore.EVENT_DISMISSED);
mEventStore.writeEvent("pkg", EventStore.EVENT_DISMISSED);
assertThat(
mEventStore.readMetric("pkg", EventStore.EVENT_DISMISSED, EventStore.METRIC_COUNT))
.isEqualTo(2);
}
@Test
public void testWriteRead_shouldReturnDefaultIfNotAvailable() {
assertThat(mEventStore.readMetric("pkg", EventStore.EVENT_SHOWN, EventStore.METRIC_COUNT))
.isEqualTo(0);
assertThat(
mEventStore
.readMetric("pkg", EventStore.EVENT_SHOWN, EventStore.METRIC_LAST_EVENT_TIME))
.isEqualTo(0);
}
}

View File

@@ -16,28 +16,18 @@
package com.android.settings.dashboard.suggestions;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Icon;
import android.service.settings.suggestions.Suggestion;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
@@ -56,7 +46,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication;
import java.util.ArrayList;
import java.util.List;
@@ -111,37 +100,18 @@ public class SuggestionAdapterTest {
@Test
public void getItemCount_shouldReturnListSize() {
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
mOneSuggestion, null /* suggestionV2 */, new ArrayList<>());
mOneSuggestionV2, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
mTwoSuggestions, null /* suggestionV2 */, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
}
@Test
public void getItemCount_v2_shouldReturnListSize() {
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
null /* suggestions */, mOneSuggestionV2, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(1);
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
null /* suggestions */, mTwoSuggestionsV2, new ArrayList<>());
mTwoSuggestionsV2, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemCount()).isEqualTo(2);
}
@Test
public void getItemViewType_shouldReturnSuggestionTile() {
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
mOneSuggestion, null /* suggestionV2 */, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemViewType(0))
.isEqualTo(R.layout.suggestion_tile);
}
@Test
public void getItemViewType_v2_shouldReturnSuggestionTile() {
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
null /* suggestions */, mOneSuggestionV2, new ArrayList<>());
mOneSuggestionV2, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemViewType(0))
.isEqualTo(R.layout.suggestion_tile);
}
@@ -155,32 +125,19 @@ public class SuggestionAdapterTest {
.setSummary("456")
.build());
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
null /* suggestions */, suggestions, new ArrayList<>());
suggestions, new ArrayList<>());
assertThat(mSuggestionAdapter.getItemViewType(0))
.isEqualTo(R.layout.suggestion_tile_with_button);
}
@Test
public void onBindViewHolder_shouldSetListener() {
final View view = spy(LayoutInflater.from(mContext).inflate(
R.layout.suggestion_tile, new LinearLayout(mContext), true));
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
mOneSuggestion, null /* suggestionV2 */, new ArrayList<>());
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
verify(view).setOnClickListener(any(View.OnClickListener.class));
}
@Test
public void onBindViewHolder_shouldLog() {
final View view = spy(LayoutInflater.from(mContext).inflate(
R.layout.suggestion_tile, new LinearLayout(mContext), true));
mSuggestionHolder = new DashboardAdapter.DashboardItemHolder(view);
mSuggestionAdapter = new SuggestionAdapter(mContext, mSuggestionControllerMixin,
null /* suggestionV1*/, mOneSuggestionV2, new ArrayList<>());
mOneSuggestionV2, new ArrayList<>());
// Bind twice
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
@@ -193,53 +150,10 @@ public class SuggestionAdapterTest {
}
@Test
public void onBindViewHolder_shouldInflateRemoteView() {
List<Tile> packages = makeSuggestions("pkg1");
RemoteViews remoteViews = mock(RemoteViews.class);
TextView textView = new TextView(RuntimeEnvironment.application);
doReturn(textView).when(remoteViews).apply(any(Context.class), any(ViewGroup.class));
packages.get(0).remoteViews = remoteViews;
setupSuggestions(mActivity, packages, null);
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
assertThat(textView.getParent()).isSameAs(mSuggestionHolder.itemView);
mSuggestionHolder.itemView.performClick();
verify(mActivity).startSuggestion(any(Intent.class));
}
@Test
public void onBindViewHolder_primaryViewShouldHandleClick() {
Context context =
new ContextThemeWrapper(RuntimeEnvironment.application, R.style.Theme_Settings);
List<Tile> packages = makeSuggestions("pkg1");
RemoteViews remoteViews = mock(RemoteViews.class);
FrameLayout layout = new FrameLayout(context);
Button primary = new Button(context);
primary.setId(android.R.id.primary);
layout.addView(primary);
doReturn(layout).when(remoteViews).apply(any(Context.class), any(ViewGroup.class));
packages.get(0).remoteViews = remoteViews;
setupSuggestions(mActivity, packages, null /* suggestionV2 */);
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
mSuggestionHolder.itemView.performClick();
assertThat(ShadowApplication.getInstance().getNextStartedActivity()).isNull();
verify(mActivity, never()).startSuggestion(any(Intent.class));
primary.performClick();
verify(mActivity).startSuggestion(any(Intent.class));
}
@Test
public void onBindViewHolder_v2_itemViewShouldHandleClick()
public void onBindViewHolder_itemViewShouldHandleClick()
throws PendingIntent.CanceledException {
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
setupSuggestions(mActivity, null /* suggestionV1 */, suggestions);
setupSuggestions(mActivity, suggestions);
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
mSuggestionHolder.itemView.performClick();
@@ -249,64 +163,21 @@ public class SuggestionAdapterTest {
}
@Test
public void onBindViewHolder_viewsShouldClearOnRebind() {
Context context =
new ContextThemeWrapper(RuntimeEnvironment.application, R.style.Theme_Settings);
List<Tile> packages = makeSuggestions("pkg1");
RemoteViews remoteViews = mock(RemoteViews.class);
FrameLayout layout = new FrameLayout(context);
Button primary = new Button(context);
primary.setId(android.R.id.primary);
layout.addView(primary);
doReturn(layout).when(remoteViews).apply(any(Context.class), any(ViewGroup.class));
packages.get(0).remoteViews = remoteViews;
setupSuggestions(mActivity, packages, null /* suggestionV2 */);
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
mSuggestionAdapter.onBindViewHolder(mSuggestionHolder, 0);
ViewGroup itemView = (ViewGroup) mSuggestionHolder.itemView;
assertThat(itemView.getChildCount()).isEqualTo(1);
}
@Test
public void getSuggestionsV2_shouldReturnSuggestionWhenMatch() {
public void getSuggestions_shouldReturnSuggestionWhenMatch() {
final List<Suggestion> suggestionsV2 = makeSuggestionsV2("pkg1");
setupSuggestions(mActivity, null /* suggestionV1 */, suggestionsV2);
setupSuggestions(mActivity, suggestionsV2);
assertThat(mSuggestionAdapter.getSuggestion(0)).isNull();
assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNotNull();
List<Tile> suggestionsV1 = makeSuggestions("pkg1");
setupSuggestions(mActivity, suggestionsV1, null /* suggestionV2 */);
assertThat(mSuggestionAdapter.getSuggestionsV2(0)).isNull();
assertThat(mSuggestionAdapter.getSuggestion(0)).isNotNull();
}
private void setupSuggestions(Context context, List<Tile> suggestions,
List<Suggestion> suggestionsV2) {
private void setupSuggestions(Context context, List<Suggestion> suggestionsV2) {
mSuggestionAdapter = new SuggestionAdapter(context, mSuggestionControllerMixin,
suggestions, suggestionsV2, new ArrayList<>());
suggestionsV2, new ArrayList<>());
mSuggestionHolder = mSuggestionAdapter.onCreateViewHolder(
new FrameLayout(RuntimeEnvironment.application),
mSuggestionAdapter.getItemViewType(0));
}
private List<Tile> makeSuggestions(String... pkgNames) {
final List<Tile> suggestions = new ArrayList<>();
for (String pkgName : pkgNames) {
Tile suggestion = new Tile();
suggestion.intent = new Intent("action");
suggestion.intent.setComponent(new ComponentName(pkgName, "cls"));
suggestions.add(suggestion);
suggestion.icon = mock(Icon.class);
}
return suggestions;
}
private List<Suggestion> makeSuggestionsV2(String... pkgNames) {
final List<Suggestion> suggestions = new ArrayList<>();
for (String pkgName : pkgNames) {

View File

@@ -33,8 +33,6 @@ import com.android.settings.R;
import com.android.settings.TestConfig;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionParser;
import org.junit.Before;
import org.junit.Test;
@@ -53,8 +51,6 @@ public class SuggestionDismissControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private RecyclerView mRecyclerView;
@Mock
private SuggestionParser mSuggestionParser;
@Mock
private SuggestionControllerMixin mSuggestionControllerMixin;
@Mock
private SuggestionDismissController.Callback mCallback;
@@ -70,7 +66,7 @@ public class SuggestionDismissControllerTest {
when(mRecyclerView.getResources().getDimension(anyInt())).thenReturn(50F);
mController = new SuggestionDismissController(mContext, mRecyclerView,
mSuggestionControllerMixin, mSuggestionParser, mCallback);
mSuggestionControllerMixin, mCallback);
}
@Test
@@ -108,17 +104,6 @@ public class SuggestionDismissControllerTest {
@Test
public void onSwiped_shouldTriggerDismissSuggestion() {
final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
mController.onSwiped(vh, ItemTouchHelper.START);
verify(mFactory.suggestionsFeatureProvider).dismissSuggestion(
eq(mContext), eq(mSuggestionParser), nullable(Tile.class));
verify(mCallback).onSuggestionDismissed(nullable(Tile.class));
}
@Test
public void onSwiped_v2_shouldTriggerDismissSuggestion() {
final RecyclerView.ViewHolder vh = mock(RecyclerView.ViewHolder.class);
when(mCallback.getSuggestionAt(anyInt())).thenReturn(
new Suggestion.Builder("id").build());

View File

@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -30,34 +29,27 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings.Secure;
import android.service.settings.suggestions.Suggestion;
import android.util.FeatureFlagUtils;
import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.Settings.NightDisplaySuggestionActivity;
import com.android.settings.TestConfig;
import com.android.settings.core.FeatureFlags;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
import com.android.settings.testutils.shadow.ShadowSecureSettings;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.suggestions.SuggestionParser;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@@ -71,26 +63,21 @@ import java.util.List;
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = {
ShadowSecureSettings.class,
SettingsShadowResources.class,
SettingsShadowSystemProperties.class
})
public class SuggestionFeatureProviderImplTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private SuggestionParser mSuggestionParser;
@Mock
private SuggestionControllerMixin mSuggestionControllerMixin;
@Mock
private Tile mSuggestion;
private Suggestion mSuggestion;
@Mock
private ActivityManager mActivityManager;
@Mock
private PackageManager mPackageManager;
@Mock
private FingerprintManager mFingerprintManager;
@Captor
private ArgumentCaptor<Pair> mTaggedDataCaptor = ArgumentCaptor.forClass(Pair.class);
private FakeFeatureFactory mFactory;
private SuggestionFeatureProviderImpl mProvider;
@@ -103,20 +90,17 @@ public class SuggestionFeatureProviderImplTest {
// Explicit casting to object due to MockitoCast bug
when((Object) mContext.getSystemService(FingerprintManager.class))
.thenReturn(mFingerprintManager);
when(mSuggestion.getId()).thenReturn("test_id");
when(mContext.getApplicationContext()).thenReturn(RuntimeEnvironment.application);
when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager);
when(mActivityManager.isLowRamDevice()).thenReturn(false);
mSuggestion.intent = new Intent().setClassName("pkg", "cls");
mSuggestion.category = "category";
mProvider = new SuggestionFeatureProviderImpl(mContext);
}
@After
public void tearDown() {
SettingsShadowResources.reset();
SettingsShadowSystemProperties.clear();
}
@Test
@@ -129,29 +113,18 @@ public class SuggestionFeatureProviderImplTest {
public void isSuggestionEnabled_isLowMemoryDevice_shouldReturnFalse() {
when(mActivityManager.isLowRamDevice()).thenReturn(true);
assertThat(mProvider.isSuggestionEnabled(mContext)).isFalse();
assertThat(mProvider.isSuggestionV2Enabled(mContext)).isFalse();
}
@Test
public void isSuggestionV2Enabled_isNotLowMemoryDevice_sysPropOn_shouldReturnTrue() {
public void isSuggestionV2Enabled_isNotLowMemoryDevice_shouldReturnTrue() {
when(mActivityManager.isLowRamDevice()).thenReturn(false);
SettingsShadowSystemProperties.set(
FeatureFlagUtils.FFLAG_PREFIX + FeatureFlags.SUGGESTIONS_V2, "true");
assertThat(mProvider.isSuggestionV2Enabled(mContext)).isTrue();
}
@Test
public void dismissSuggestion_noParserOrSuggestion_noop() {
mProvider.dismissSuggestion(mContext, null, (Tile) null);
mProvider.dismissSuggestion(mContext, mSuggestionParser, null);
mProvider.dismissSuggestion(mContext, null, mSuggestion);
verifyZeroInteractions(mFactory.metricsFeatureProvider);
}
@Test
public void dismissSuggestion_noControllerOrSuggestion_noop() {
mProvider.dismissSuggestion(mContext, null, (Suggestion) null);
mProvider.dismissSuggestion(mContext, null, null);
mProvider.dismissSuggestion(mContext, mSuggestionControllerMixin, null);
mProvider.dismissSuggestion(mContext, null, new Suggestion.Builder("id").build());
@@ -159,82 +132,22 @@ public class SuggestionFeatureProviderImplTest {
verifyZeroInteractions(mSuggestionControllerMixin);
}
@Test
public void getSuggestionIdentifier_samePackage_returnClassName() {
final Tile suggestion = new Tile();
suggestion.intent = new Intent()
.setClassName(RuntimeEnvironment.application.getPackageName(), "123");
assertThat(mProvider.getSuggestionIdentifier(RuntimeEnvironment.application, suggestion))
.isEqualTo("123");
}
@Test
public void getSuggestionIdentifier_differentPackage_returnPackageName() {
final Tile suggestion = new Tile();
suggestion.intent = new Intent()
.setClassName(RuntimeEnvironment.application.getPackageName(), "123");
assertThat(mProvider.getSuggestionIdentifier(mContext, suggestion))
.isEqualTo(RuntimeEnvironment.application.getPackageName());
}
@Test
public void getSuggestionIdentifier_nullComponent_shouldNotCrash() {
final Tile suggestion = new Tile();
suggestion.intent = new Intent();
assertThat(mProvider.getSuggestionIdentifier(mContext, suggestion))
.isNotEmpty();
}
@Test
public void getSuggestionIdentifier_nullContext_shouldNotCrash() {
final Tile suggestion = new Tile();
suggestion.intent = new Intent()
.setClassName(RuntimeEnvironment.application.getPackageName(), "123");
assertThat(mProvider.getSuggestionIdentifier(null, suggestion))
.isNotEmpty();
}
@Test
public void dismissSuggestion_hasMoreDismissCount_shouldNotDisableComponent() {
when(mSuggestionParser.dismissSuggestion(any(Tile.class)))
.thenReturn(false);
mProvider.dismissSuggestion(mContext, mSuggestionParser, mSuggestion);
verify(mFactory.metricsFeatureProvider).action(
eq(mContext),
eq(MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION),
anyString(),
mTaggedDataCaptor.capture());
assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
verify(mContext, never()).getPackageManager();
}
@Test
public void dismissSuggestion_noContext_shouldDoNothing() {
mProvider.dismissSuggestion(null, mSuggestionParser, mSuggestion);
mProvider.dismissSuggestion(null, mSuggestionControllerMixin, mSuggestion);
verifyZeroInteractions(mFactory.metricsFeatureProvider);
}
@Test
public void dismissSuggestion_hasNoMoreDismissCount_shouldDisableComponent() {
when(mSuggestionParser.dismissSuggestion(any(Tile.class)))
.thenReturn(true);
mProvider.dismissSuggestion(mContext, mSuggestionParser, mSuggestion);
public void dismissSuggestion_shouldLogAndDismiss() {
mProvider.dismissSuggestion(mContext, mSuggestionControllerMixin, mSuggestion);
verify(mFactory.metricsFeatureProvider).action(
eq(mContext),
eq(MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION),
anyString(),
mTaggedDataCaptor.capture());
assertThat(mTaggedDataCaptor.getAllValues()).containsExactly(
Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0));
verify(mContext.getPackageManager())
.setComponentEnabledSetting(mSuggestion.intent.getComponent(),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
anyString());
verify(mSuggestionControllerMixin).dismissSuggestion(mSuggestion);
}
@Test

View File

@@ -1,109 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.dashboard.suggestions;
import java.util.Arrays;
import java.util.Map;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SuggestionFeaturizerTest {
private EventStore mEventStore;
private SuggestionFeaturizer mSuggestionFeaturizer;
@Before
public void setUp() {
mEventStore = new EventStore(RuntimeEnvironment.application);
mSuggestionFeaturizer = new SuggestionFeaturizer(mEventStore);
}
@Test
public void testFeaturize_singlePackage() {
mEventStore.writeEvent("pkg", EventStore.EVENT_DISMISSED);
mEventStore.writeEvent("pkg", EventStore.EVENT_SHOWN);
mEventStore.writeEvent("pkg", EventStore.EVENT_SHOWN);
Map<String, Double> features = mSuggestionFeaturizer.featurize(Arrays.asList("pkg"))
.get("pkg");
assertThat(features.get(SuggestionFeaturizer.FEATURE_IS_SHOWN)).isEqualTo(1.0);
assertThat(features.get(SuggestionFeaturizer.FEATURE_IS_DISMISSED)).isEqualTo(1.0);
assertThat(features.get(SuggestionFeaturizer.FEATURE_IS_CLICKED)).isEqualTo(0.0);
assertThat(features.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_SHOWN)).isLessThan(1.0);
assertThat(features.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_DISMISSED))
.isLessThan(1.0);
assertThat(features.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_CLICKED))
.isEqualTo(1.0);
assertThat(features.get(SuggestionFeaturizer.FEATURE_SHOWN_COUNT))
.isEqualTo(2.0 / SuggestionFeaturizer.COUNT_NORMALIZATION_FACTOR);
assertThat(features.get(SuggestionFeaturizer.FEATURE_DISMISSED_COUNT))
.isEqualTo(1.0 / SuggestionFeaturizer.COUNT_NORMALIZATION_FACTOR);
assertThat(features.get(SuggestionFeaturizer.FEATURE_CLICKED_COUNT)).isEqualTo(0.0);
}
@Test
public void testFeaturize_multiplePackages() {
mEventStore.writeEvent("pkg1", EventStore.EVENT_DISMISSED);
mEventStore.writeEvent("pkg2", EventStore.EVENT_SHOWN);
mEventStore.writeEvent("pkg1", EventStore.EVENT_SHOWN);
Map<String, Map<String, Double>> features = mSuggestionFeaturizer
.featurize(Arrays.asList("pkg1", "pkg2"));
Map<String, Double> features1 = features.get("pkg1");
Map<String, Double> features2 = features.get("pkg2");
assertThat(features1.get(SuggestionFeaturizer.FEATURE_IS_SHOWN)).isEqualTo(1.0);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_IS_DISMISSED)).isEqualTo(1.0);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_IS_CLICKED)).isEqualTo(0.0);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_SHOWN))
.isLessThan(1.0);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_DISMISSED))
.isLessThan(1.0);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_CLICKED))
.isEqualTo(1.0);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_SHOWN_COUNT))
.isEqualTo(1.0 / SuggestionFeaturizer.COUNT_NORMALIZATION_FACTOR);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_DISMISSED_COUNT))
.isEqualTo(1.0 / SuggestionFeaturizer.COUNT_NORMALIZATION_FACTOR);
assertThat(features1.get(SuggestionFeaturizer.FEATURE_CLICKED_COUNT)).isEqualTo(0.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_IS_SHOWN)).isEqualTo(1.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_IS_DISMISSED)).isEqualTo(0.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_IS_CLICKED)).isEqualTo(0.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_SHOWN))
.isLessThan(1.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_DISMISSED))
.isEqualTo(1.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_TIME_FROM_LAST_CLICKED))
.isEqualTo(1.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_SHOWN_COUNT))
.isEqualTo(1.0 / SuggestionFeaturizer.COUNT_NORMALIZATION_FACTOR);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_DISMISSED_COUNT)).isEqualTo(0.0);
assertThat(features2.get(SuggestionFeaturizer.FEATURE_CLICKED_COUNT)).isEqualTo(0.0);
}
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.dashboard.suggestions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settingslib.drawer.Tile;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.spy;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SuggestionRankerTest {
@Mock
private SuggestionRanker mSuggestionRanker;
@Mock
private SuggestionFeaturizer mSuggestionFeaturizer;
private Map<String, Map<String, Double>> mFeatures;
private List<String> mPkgNames;
private List<Tile> mSuggestions;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mPkgNames = Arrays.asList("pkg1", "pkg2", "pkg3");
mFeatures = new HashMap<String, Map<String, Double>>();
mFeatures.put("pkg1", new HashMap<String, Double>());
mFeatures.put("pkg2", new HashMap<String, Double>());
mFeatures.put("pkg3", new HashMap<String, Double>());
mSuggestions = new ArrayList<Tile>() {
{
add(new Tile());
add(new Tile());
add(new Tile());
}
};
mSuggestionFeaturizer = mock(SuggestionFeaturizer.class);
mSuggestionRanker = new SuggestionRanker(mSuggestionFeaturizer);
when(mSuggestionFeaturizer.featurize(mPkgNames)).thenReturn(mFeatures);
mSuggestionRanker = spy(mSuggestionRanker);
when(mSuggestionRanker.getRelevanceMetric(same(mFeatures.get("pkg1")))).thenReturn(0.9);
when(mSuggestionRanker.getRelevanceMetric(same(mFeatures.get("pkg2")))).thenReturn(0.1);
when(mSuggestionRanker.getRelevanceMetric(same(mFeatures.get("pkg3")))).thenReturn(0.5);
}
@Test
public void testRank() {
List<Tile> expectedOrderdList = new ArrayList<Tile>() {
{
add(mSuggestions.get(0)); // relevance = 0.9
add(mSuggestions.get(2)); // relevance = 0.5
add(mSuggestions.get(1)); // relevance = 0.1
}
};
mSuggestionRanker.rankSuggestions(mSuggestions, mPkgNames);
assertThat(mSuggestions).isEqualTo(expectedOrderdList);
}
}