diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java index f968cd0f040..e3e95be7f33 100644 --- a/src/com/android/settings/dashboard/DashboardAdapter.java +++ b/src/com/android/settings/dashboard/DashboardAdapter.java @@ -40,8 +40,8 @@ import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.conditional.Condition; import com.android.settings.dashboard.conditional.ConditionAdapterUtils; import com.android.settings.dashboard.suggestions.SuggestionDismissController; +import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.overlay.FeatureFactory; -import com.android.settingslib.SuggestionParser; import com.android.settingslib.drawer.DashboardCategory; import com.android.settingslib.drawer.Tile; @@ -60,6 +60,7 @@ public class DashboardAdapter extends RecyclerView.Adapter mSuggestionsShownLogged; private boolean mFirstFrameDrawn; @@ -97,17 +98,17 @@ public class DashboardAdapter extends RecyclerView.Adapter conditions) { List suggestions = null; List categories = null; int suggestionMode = DashboardData.SUGGESTION_MODE_DEFAULT; mContext = context; - mMetricsFeatureProvider = metricsFeatureProvider; - mDashboardFeatureProvider = FeatureFactory.getFactory(context) - .getDashboardFeatureProvider(context); + final FeatureFactory factory = FeatureFactory.getFactory(context); + mMetricsFeatureProvider = factory.getMetricsFeatureProvider(); + mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context); + mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context); mCache = new IconCache(context); setHasStableIds(true); @@ -120,7 +121,7 @@ public class DashboardAdapter extends RecyclerView.Adapter(); + mSuggestionsShownLogged = new ArrayList<>(); } mDashboardData = new DashboardData.Builder() @@ -173,11 +174,11 @@ public class DashboardAdapter extends RecyclerView.Adapter { + mMetricsFeatureProvider.action(mContext, + MetricsEvent.ACTION_SETTINGS_SUGGESTION, suggestionId); + ((SettingsActivity) mContext).startSuggestion(suggestion.intent); }); break; case R.layout.condition_card: @@ -260,13 +258,7 @@ public class DashboardAdapter extends RecyclerView.Adapter onExpandClick(v)); break; } } @@ -291,7 +283,8 @@ public class DashboardAdapter extends RecyclerView.Adapter { + final int suggestionMode; + if (moreSuggestions) { + suggestionMode = DashboardData.SUGGESTION_MODE_EXPANDED; - for (Tile suggestion : mDashboardData.getSuggestions()) { - String suggestionId = - DashboardAdapter.getSuggestionIdentifier(mContext, suggestion); - if (!mSuggestionsShownLogged.contains(suggestionId)) { - mMetricsFeatureProvider.action( - mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, - suggestionId); - mSuggestionsShownLogged.add(suggestionId); - } + for (Tile suggestion : mDashboardData.getSuggestions()) { + final String suggestionId = mSuggestionFeatureProvider.getSuggestionIdentifier( + mContext, suggestion); + if (!mSuggestionsShownLogged.contains(suggestionId)) { + mMetricsFeatureProvider.action( + mContext, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, + suggestionId); + mSuggestionsShownLogged.add(suggestionId); } - } else { - suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED; } - - DashboardData prevData = mDashboardData; - mDashboardData = new DashboardData.Builder(prevData) - .setSuggestionMode(suggestionMode) - .build(); - notifyDashboardDataChanged(prevData); + } else { + suggestionMode = DashboardData.SUGGESTION_MODE_COLLAPSED; } + + DashboardData prevData = mDashboardData; + mDashboardData = new DashboardData.Builder(prevData) + .setSuggestionMode(suggestionMode) + .build(); + notifyDashboardDataChanged(prevData); }); } diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java index 4e2baa9d1ff..7a5f3953f86 100644 --- a/src/com/android/settings/dashboard/DashboardSummary.java +++ b/src/com/android/settings/dashboard/DashboardSummary.java @@ -190,8 +190,7 @@ public class DashboardSummary extends InstrumentedFragment mDashboard.addItemDecoration(new DashboardDecorator(getContext())); mDashboard.setListener(this); Log.d(TAG, "adapter created"); - mAdapter = new DashboardAdapter(getContext(), mSuggestionParser, mMetricsFeatureProvider, - bundle, mConditionManager.getConditions()); + mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions()); mDashboard.setAdapter(mAdapter); mSuggestionDismissHandler = new SuggestionDismissController( getContext(), mDashboard, mSuggestionParser, mAdapter); @@ -245,8 +244,8 @@ public class DashboardSummary extends InstrumentedFragment if (isSmartSuggestionEnabled) { List suggestionIds = new ArrayList<>(suggestions.size()); for (Tile suggestion : suggestions) { - suggestionIds.add( - DashboardAdapter.getSuggestionIdentifier(context, suggestion)); + suggestionIds.add(mSuggestionFeatureProvider.getSuggestionIdentifier( + context, suggestion)); } // TODO: create a Suggestion class to maintain the id and other info mSuggestionFeatureProvider.rankSuggestions(suggestions, suggestionIds); diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java index 806d6f880f5..14f5e9ceb62 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProvider.java @@ -49,4 +49,9 @@ public interface SuggestionFeatureProvider { * Dismisses a suggestion. */ void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion); + + /** + * Returns an identifier for the suggestion + */ + String getSuggestionIdentifier(Context context, Tile suggestion); } diff --git a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java index 9231037e2f4..b82c28dd2ae 100644 --- a/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java +++ b/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImpl.java @@ -21,7 +21,6 @@ import android.content.pm.PackageManager; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.core.instrumentation.MetricsFeatureProvider; -import com.android.settings.dashboard.DashboardAdapter; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.SuggestionParser; import com.android.settingslib.drawer.Tile; @@ -50,9 +49,10 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider public SuggestionFeatureProviderImpl(Context context) { + final Context appContext = context.getApplicationContext(); mSuggestionRanker = new SuggestionRanker( - new SuggestionFeaturizer(new EventStore(context.getApplicationContext()))); - mMetricsFeatureProvider = FeatureFactory.getFactory(context) + new SuggestionFeaturizer(new EventStore(appContext))); + mMetricsFeatureProvider = FeatureFactory.getFactory(appContext) .getMetricsFeatureProvider(); } @@ -68,7 +68,7 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider } mMetricsFeatureProvider.action( context, MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION, - DashboardAdapter.getSuggestionIdentifier(context, suggestion)); + getSuggestionIdentifier(context, suggestion)); final boolean isSmartSuggestionEnabled = isSmartSuggestionEnabled(context); if (!parser.dismissSuggestion(suggestion, isSmartSuggestionEnabled)) { @@ -81,4 +81,18 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider parser.markCategoryDone(suggestion.category); } + @Override + public String getSuggestionIdentifier(Context context, Tile suggestion) { + if (suggestion.intent == null || suggestion.intent.getComponent() == 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; + } + } diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java index 7f0229a8b5d..d7daa03a1fa 100644 --- a/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/DashboardAdapterTest.java @@ -21,35 +21,32 @@ import android.content.Intent; import android.content.res.Resources; import android.view.View; import android.widget.FrameLayout; + import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; -import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.dashboard.conditional.Condition; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.SettingsShadowResources; import com.android.settings.testutils.shadow.ShadowDynamicIndexableContentMonitor; -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.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.robolectric.annotation.Config; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; import java.util.ArrayList; import java.util.List; import static com.google.common.truth.Truth.assertThat; -import org.mockito.Matchers; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -64,20 +61,19 @@ import static org.mockito.Mockito.when; }) public class DashboardAdapterTest { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private Context mContext; @Mock private View mView; @Mock private Condition mCondition; @Mock - private MetricsFeatureProvider mMetricsFeatureProvider; - @Mock private Resources mResources; - @Mock - private DashboardData mDashboardData; @Captor private ArgumentCaptor mActionCategoryCaptor = ArgumentCaptor.forClass(Integer.class); @Captor private ArgumentCaptor mActionPackageCaptor = ArgumentCaptor.forClass(String.class); + private FakeFeatureFactory mFactory; private DashboardAdapter mDashboardAdapter; private DashboardAdapter.DashboardItemHolder mSuggestionHolder; private DashboardData.SuggestionHeaderData mSuggestionHeaderData; @@ -85,15 +81,20 @@ public class DashboardAdapterTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - Context context = RuntimeEnvironment.application; - context = spy(context); - when(context.getResources()).thenReturn(mResources); - when(mResources - .getQuantityString(any(int.class), any(int.class), Matchers.anyVararg())) + FakeFeatureFactory.setupForTest(mContext); + mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); + 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())) .thenReturn(""); - FakeFeatureFactory.setupForTest(context); - mDashboardAdapter = new DashboardAdapter(context, null, mMetricsFeatureProvider, - null, null); + + mDashboardAdapter = new DashboardAdapter(mContext, null, null); mSuggestionHeaderData = new DashboardData.SuggestionHeaderData(true, 1, 0); when(mView.getTag()).thenReturn(mCondition); } @@ -109,7 +110,7 @@ public class DashboardAdapterTest { @Test public void testSuggestionsLogs_NotExpanded() { setUpSuggestions(makeSuggestions(new String[]{"pkg1", "pkg2", "pkg3"})); - verify(mMetricsFeatureProvider, times(2)).action( + verify(mFactory.metricsFeatureProvider, times(2)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg2"}; @@ -117,15 +118,15 @@ public class DashboardAdapterTest { MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION }; - assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages); assertThat(mActionCategoryCaptor.getAllValues().toArray()).isEqualTo(expectedActions); + assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages); } @Test public void testSuggestionsLogs_NotExpandedAndPaused() { setUpSuggestions(makeSuggestions(new String[]{"pkg1", "pkg2", "pkg3"})); mDashboardAdapter.onPause(); - verify(mMetricsFeatureProvider, times(4)).action( + verify(mFactory.metricsFeatureProvider, times(4)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg1", "pkg2"}; @@ -134,8 +135,8 @@ public class DashboardAdapterTest { MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION}; - assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages); assertThat(mActionCategoryCaptor.getAllValues().toArray()).isEqualTo(expectedActions); + assertThat(mActionPackageCaptor.getAllValues().toArray()).isEqualTo(expectedPackages); } @Test @@ -144,7 +145,7 @@ public class DashboardAdapterTest { mDashboardAdapter.onBindSuggestionHeader( mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); - verify(mMetricsFeatureProvider, times(3)).action( + verify(mFactory.metricsFeatureProvider, times(3)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg3"}; @@ -164,7 +165,7 @@ public class DashboardAdapterTest { mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); mDashboardAdapter.onPause(); - verify(mMetricsFeatureProvider, times(6)).action( + verify(mFactory.metricsFeatureProvider, times(6)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3"}; @@ -187,7 +188,7 @@ public class DashboardAdapterTest { mDashboardAdapter.onBindSuggestionHeader( mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); - verify(mMetricsFeatureProvider, times(7)).action( + verify(mFactory.metricsFeatureProvider, times(7)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{ @@ -213,7 +214,7 @@ public class DashboardAdapterTest { mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); mDashboardAdapter.onPause(); - verify(mMetricsFeatureProvider, times(10)).action( + verify(mFactory.metricsFeatureProvider, times(10)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{ @@ -240,7 +241,7 @@ public class DashboardAdapterTest { mDashboardAdapter.onBindSuggestionHeader( mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); - verify(mMetricsFeatureProvider, times(1)).action( + verify(mFactory.metricsFeatureProvider, times(1)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1"}; @@ -258,7 +259,7 @@ public class DashboardAdapterTest { mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); mDashboardAdapter.onPause(); - verify(mMetricsFeatureProvider, times(2)).action( + verify(mFactory.metricsFeatureProvider, times(2)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg1"}; @@ -277,7 +278,7 @@ public class DashboardAdapterTest { mDashboardAdapter.onBindSuggestionHeader( mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); - verify(mMetricsFeatureProvider, times(3)).action( + verify(mFactory.metricsFeatureProvider, times(3)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg1", "pkg1"}; @@ -298,7 +299,7 @@ public class DashboardAdapterTest { mSuggestionHolder, mSuggestionHeaderData); mSuggestionHolder.itemView.callOnClick(); mDashboardAdapter.onPause(); - verify(mMetricsFeatureProvider, times(4)).action( + verify(mFactory.metricsFeatureProvider, times(4)).action( any(Context.class), mActionCategoryCaptor.capture(), mActionPackageCaptor.capture()); String[] expectedPackages = new String[]{"pkg1", "pkg1", "pkg1", "pkg1"}; @@ -313,23 +314,18 @@ public class DashboardAdapterTest { } private List makeSuggestions(String[] pkgNames) { - List suggestions = new ArrayList(); + final List suggestions = new ArrayList<>(); for (String pkgName : pkgNames) { - suggestions.add(makeSuggestion(pkgName, "cls")); + Tile suggestion = new Tile(); + suggestion.intent = new Intent("action"); + suggestion.intent.setComponent(new ComponentName(pkgName, "cls")); + suggestions.add(suggestion); } return suggestions; } - private Tile makeSuggestion(String pkgName, String className) { - Tile suggestion = new Tile(); - suggestion.intent = new Intent("action"); - suggestion.intent.setComponent(new ComponentName(pkgName, className)); - return suggestion; - } - private void setUpSuggestions(List suggestions) { - mDashboardAdapter.setCategoriesAndSuggestions( - new ArrayList(), suggestions); + mDashboardAdapter.setCategoriesAndSuggestions(new ArrayList<>(), suggestions); mSuggestionHolder = mDashboardAdapter.onCreateViewHolder( new FrameLayout(RuntimeEnvironment.application), mDashboardAdapter.getItemViewType(0)); diff --git a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java index 912947ab3d8..b36aef24ccf 100644 --- a/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java +++ b/tests/robotests/src/com/android/settings/dashboard/suggestions/SuggestionFeatureProviderImplTest.java @@ -36,6 +36,7 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; +import static com.google.common.truth.Truth.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyString; @@ -80,6 +81,32 @@ public class SuggestionFeatureProviderImplTest { verifyZeroInteractions(mFactory.metricsFeatureProvider); } + @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 dismissSuggestion_hasMoreDismissCount_shouldNotDisableComponent() { when(mSuggestionParser.dismissSuggestion(any(Tile.class), anyBoolean()))