Plumb SuggestionControllerMixin to DismissController.

- The SuggestionControllerMixin is needed to dismiss a suggestion.
- When swipe a suggestion, SuggestionControllerMixin is called, which
  then calls API in SuggestionService to dismiss a suggestion.

Bug: 65065268
Test: robotests
Change-Id: I6a0c5823d60b995ab4a36b1c91777f5cd31a500d
This commit is contained in:
Fan Zhang
2017-09-25 16:44:28 -07:00
parent 9aae9d5d80
commit cd7b13bba5
10 changed files with 80 additions and 21 deletions

View File

@@ -47,6 +47,8 @@ import com.android.settings.dashboard.DashboardData.SuggestionConditionHeaderDat
import com.android.settings.dashboard.conditional.Condition; import com.android.settings.dashboard.conditional.Condition;
import com.android.settings.dashboard.conditional.ConditionAdapter; import com.android.settings.dashboard.conditional.ConditionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionAdapter; import com.android.settings.dashboard.suggestions.SuggestionAdapter;
import com.android.settings.dashboard.suggestions.SuggestionController;
import com.android.settings.dashboard.suggestions.SuggestionControllerMixin;
import com.android.settings.dashboard.suggestions.SuggestionDismissController; import com.android.settings.dashboard.suggestions.SuggestionDismissController;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider; import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionLogHelper; import com.android.settings.dashboard.suggestions.SuggestionLogHelper;
@@ -76,6 +78,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
private final IconCache mCache; private final IconCache mCache;
private final Context mContext; private final Context mContext;
private final SuggestionControllerMixin mSuggestionControllerMixin;
private final MetricsFeatureProvider mMetricsFeatureProvider; private final MetricsFeatureProvider mMetricsFeatureProvider;
private final DashboardFeatureProvider mDashboardFeatureProvider; private final DashboardFeatureProvider mDashboardFeatureProvider;
private final SuggestionFeatureProvider mSuggestionFeatureProvider; private final SuggestionFeatureProvider mSuggestionFeatureProvider;
@@ -100,6 +103,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
public DashboardAdapter(Context context, Bundle savedInstanceState, public DashboardAdapter(Context context, Bundle savedInstanceState,
List<Condition> conditions, SuggestionParser suggestionParser, List<Condition> conditions, SuggestionParser suggestionParser,
SuggestionControllerMixin suggestionControllerMixin,
SuggestionDismissController.Callback callback) { SuggestionDismissController.Callback callback) {
// @deprecated In favor of suggestionsV2 below. // @deprecated In favor of suggestionsV2 below.
@@ -110,6 +114,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
mContext = context; mContext = context;
final FeatureFactory factory = FeatureFactory.getFactory(context); final FeatureFactory factory = FeatureFactory.getFactory(context);
mSuggestionControllerMixin = suggestionControllerMixin;
mMetricsFeatureProvider = factory.getMetricsFeatureProvider(); mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context); mDashboardFeatureProvider = factory.getDashboardFeatureProvider(context);
mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context); mSuggestionFeatureProvider = factory.getSuggestionFeatureProvider(context);
@@ -488,7 +493,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
mDashboardData.getItemEntityByPosition(position), mDashboardData.getItemEntityByPosition(position),
null, mSuggestionsShownLogged); null, mSuggestionsShownLogged);
mSuggestionDismissHandler = new SuggestionDismissController(mContext, mSuggestionDismissHandler = new SuggestionDismissController(mContext,
holder.data, mSuggestionParser, mCallback); holder.data, mSuggestionControllerMixin, mSuggestionParser, mCallback);
holder.data.setAdapter(mSuggestionAdapter); holder.data.setAdapter(mSuggestionAdapter);
} else if (suggestionsV2 != null && suggestionsV2.size() > 0) { } else if (suggestionsV2 != null && suggestionsV2.size() > 0) {
conditionOnly = false; conditionOnly = false;
@@ -496,7 +501,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
(List<Suggestion>) mDashboardData.getItemEntityByPosition(position), (List<Suggestion>) mDashboardData.getItemEntityByPosition(position),
mSuggestionsShownLogged); mSuggestionsShownLogged);
mSuggestionDismissHandler = new SuggestionDismissController(mContext, mSuggestionDismissHandler = new SuggestionDismissController(mContext,
holder.data, null /* parser */, mCallback); holder.data, mSuggestionControllerMixin, null /* parser */, mCallback);
holder.data.setAdapter(mSuggestionAdapter); holder.data.setAdapter(mSuggestionAdapter);
} }
} }

View File

@@ -91,6 +91,7 @@ public class DashboardSummary extends InstrumentedFragment
mSuggestionFeatureProvider = FeatureFactory.getFactory(context) mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
.getSuggestionFeatureProvider(context); .getSuggestionFeatureProvider(context);
if (mSuggestionFeatureProvider.isSuggestionV2Enabled(context)) { if (mSuggestionFeatureProvider.isSuggestionV2Enabled(context)) {
Log.d(TAG, "Suggestion v2 is enabled, creating SuggestionControllerMixin");
mSuggestionControllerMixin = new SuggestionControllerMixin(context, this /* host */, mSuggestionControllerMixin = new SuggestionControllerMixin(context, this /* host */,
getLifecycle()); getLifecycle());
} }
@@ -207,7 +208,8 @@ public class DashboardSummary extends InstrumentedFragment
mDashboard.setHasFixedSize(true); mDashboard.setHasFixedSize(true);
mDashboard.setListener(this); mDashboard.setListener(this);
mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(), mAdapter = new DashboardAdapter(getContext(), bundle, mConditionManager.getConditions(),
mSuggestionParser, this /* SuggestionDismissController.Callback */); mSuggestionParser, mSuggestionControllerMixin,
this /* SuggestionDismissController.Callback */);
mDashboard.setAdapter(mAdapter); mDashboard.setAdapter(mAdapter);
mDashboard.setItemAnimator(new DashboardItemAnimator()); mDashboard.setItemAnimator(new DashboardItemAnimator());
mSummaryLoader.setSummaryConsumer(mAdapter); mSummaryLoader.setSummaryConsumer(mAdapter);
@@ -224,7 +226,7 @@ public class DashboardSummary extends InstrumentedFragment
@VisibleForTesting @VisibleForTesting
void rebuildUI() { void rebuildUI() {
if (!mSuggestionFeatureProvider.isSuggestionEnabled(getContext())) { if (!mSuggestionFeatureProvider.isSuggestionEnabled(getContext())) {
Log.d(TAG, "Suggestion feature is disabled, skipping suggestion entirely"); Log.d(TAG, "Suggestion v1 feature is disabled, skipping suggestion v1");
updateCategory(); updateCategory();
} else { } else {
new SuggestionLoader().execute(); new SuggestionLoader().execute();

View File

@@ -110,6 +110,18 @@ public class SuggestionController {
} }
} }
public void dismissSuggestions(Suggestion suggestion) {
if (!isReady()) {
Log.w(TAG, "SuggestionController not ready, cannot dismiss " + suggestion.getId());
return;
}
try {
mRemoteService.dismissSuggestion(suggestion);
} catch (RemoteException e) {
Log.w(TAG, "Error when calling dismissSuggestion()", e);
}
}
/** /**
* Whether or not the manager is ready * Whether or not the manager is ready
*/ */

View File

@@ -111,4 +111,8 @@ public class SuggestionControllerMixin implements SuggestionController.ServiceCo
public void onLoaderReset(Loader<List<Suggestion>> loader) { public void onLoaderReset(Loader<List<Suggestion>> loader) {
} }
public void dismissSuggestion(Suggestion suggestion) {
mSuggestionController.dismissSuggestions(suggestion);
}
} }

View File

@@ -57,6 +57,7 @@ public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback
private final Context mContext; private final Context mContext;
private final SuggestionFeatureProvider mSuggestionFeatureProvider; private final SuggestionFeatureProvider mSuggestionFeatureProvider;
private final SuggestionControllerMixin mSuggestionMixin;
/** /**
* @deprecated in favor of the new Suggestion backend. * @deprecated in favor of the new Suggestion backend.
@@ -66,8 +67,9 @@ public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback
private final Callback mCallback; private final Callback mCallback;
public SuggestionDismissController(Context context, RecyclerView recyclerView, public SuggestionDismissController(Context context, RecyclerView recyclerView,
SuggestionParser parser, Callback callback) { SuggestionControllerMixin suggestionMixin, SuggestionParser parser, Callback callback) {
super(0, ItemTouchHelper.START | ItemTouchHelper.END); super(0, ItemTouchHelper.START | ItemTouchHelper.END);
mSuggestionMixin = suggestionMixin;
mContext = context; mContext = context;
mSuggestionParser = parser; mSuggestionParser = parser;
mSuggestionFeatureProvider = FeatureFactory.getFactory(context) mSuggestionFeatureProvider = FeatureFactory.getFactory(context)
@@ -102,7 +104,7 @@ public class SuggestionDismissController extends ItemTouchHelper.SimpleCallback
final int position = viewHolder.getAdapterPosition(); final int position = viewHolder.getAdapterPosition();
final Suggestion suggestionV2 = mCallback.getSuggestionAt(position); final Suggestion suggestionV2 = mCallback.getSuggestionAt(position);
if (suggestionV2 != null) { if (suggestionV2 != null) {
mSuggestionFeatureProvider.dismissSuggestion(mContext, suggestionV2); mSuggestionFeatureProvider.dismissSuggestion(mContext, mSuggestionMixin, suggestionV2);
mCallback.onSuggestionDismissed(suggestionV2); mCallback.onSuggestionDismissed(suggestionV2);
} else { } else {
final Tile suggestion = mCallback.getSuggestionForPosition(position); final Tile suggestion = mCallback.getSuggestionForPosition(position);

View File

@@ -79,7 +79,8 @@ public interface SuggestionFeatureProvider {
/** /**
* Dismisses a suggestion. * Dismisses a suggestion.
* *
* @deprecated in favor of {@link #dismissSuggestion(Context, Suggestion)} * @deprecated in favor of {@link #dismissSuggestion(Context, SuggestionControllerMixin,
* Suggestion)}
*/ */
@Deprecated @Deprecated
void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion); void dismissSuggestion(Context context, SuggestionParser parser, Tile suggestion);
@@ -87,7 +88,8 @@ public interface SuggestionFeatureProvider {
/** /**
* Dismisses a suggestion. * Dismisses a suggestion.
*/ */
void dismissSuggestion(Context context, Suggestion suggestion); void dismissSuggestion(Context context, SuggestionControllerMixin suggestionMixin,
Suggestion suggestion);
/** /**
* Returns an identifier for the suggestion * Returns an identifier for the suggestion

View File

@@ -173,14 +173,15 @@ public class SuggestionFeatureProviderImpl implements SuggestionFeatureProvider
} }
@Override @Override
public void dismissSuggestion(Context context, Suggestion suggestion) { public void dismissSuggestion(Context context, SuggestionControllerMixin mixin,
if (suggestion == null || context == null) { Suggestion suggestion) {
if (mixin == null || suggestion == null || context == null) {
return; return;
} }
mMetricsFeatureProvider.action( mMetricsFeatureProvider.action(
context, MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION, context, MetricsProto.MetricsEvent.ACTION_SETTINGS_DISMISS_SUGGESTION,
suggestion.getId()); suggestion.getId());
// TODO: Call SettingsIntelligence to dismiss suggestion. mixin.dismissSuggestion(suggestion);
} }
@Override @Override

View File

@@ -120,7 +120,7 @@ public class DashboardAdapterTest {
mConditionList = new ArrayList<>(); mConditionList = new ArrayList<>();
mConditionList.add(mCondition); mConditionList.add(mCondition);
when(mCondition.shouldShow()).thenReturn(true); when(mCondition.shouldShow()).thenReturn(true);
mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null); mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null, null);
mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1); mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1);
when(mView.getTag()).thenReturn(mCondition); when(mView.getTag()).thenReturn(mCondition);
} }
@@ -419,7 +419,7 @@ public class DashboardAdapterTest {
@Test @Test
public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() { public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() {
final DashboardAdapter adapter = final DashboardAdapter adapter =
spy(new DashboardAdapter(mContext, null, null, null, null)); spy(new DashboardAdapter(mContext, null, null, null, null, null));
final List<Tile> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3"); final List<Tile> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3");
adapter.setCategoriesAndSuggestions(null /* category */, suggestions); adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
@@ -457,7 +457,8 @@ public class DashboardAdapterTest {
final List<Tile> suggestions = final List<Tile> suggestions =
makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4"); makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4");
final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */, final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */,
null /* conditions */, null /* suggestionParser */, null /* callback */)); null /* conditions */, null /* suggestionParser */,
null /* suggestionControllerMixin */, null /* callback */));
adapter.setCategoriesAndSuggestions(null /* category */, suggestions); adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
adapter.onBindConditionAndSuggestion( adapter.onBindConditionAndSuggestion(
holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION); holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION);
@@ -474,7 +475,7 @@ public class DashboardAdapterTest {
@Test @Test
public void testSuggestionDismissed_onlySuggestion_updateDashboardData() { public void testSuggestionDismissed_onlySuggestion_updateDashboardData() {
DashboardAdapter adapter = DashboardAdapter adapter =
spy(new DashboardAdapter(mContext, null, null, null, null)); spy(new DashboardAdapter(mContext, null, null, null, null, null));
final List<Tile> suggestions = makeSuggestions("pkg1"); final List<Tile> suggestions = makeSuggestions("pkg1");
adapter.setCategoriesAndSuggestions(null /* category */, suggestions); adapter.setCategoriesAndSuggestions(null /* category */, suggestions);
final DashboardData dashboardData = adapter.mDashboardData; final DashboardData dashboardData = adapter.mDashboardData;
@@ -534,7 +535,7 @@ public class DashboardAdapterTest {
@Test @Test
public void testBindConditionAndSuggestion_shouldSetSuggestionAdapterAndNoCrash() { public void testBindConditionAndSuggestion_shouldSetSuggestionAdapterAndNoCrash() {
mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null); mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null, null);
final List<Tile> suggestions = makeSuggestions("pkg1"); final List<Tile> suggestions = makeSuggestions("pkg1");
final DashboardCategory category = mock(DashboardCategory.class); final DashboardCategory category = mock(DashboardCategory.class);
final List<Tile> tiles = new ArrayList<>(); final List<Tile> tiles = new ArrayList<>();
@@ -561,7 +562,7 @@ public class DashboardAdapterTest {
@Test @Test
public void testBindConditionAndSuggestion_v2_shouldSetSuggestionAdapterAndNoCrash() { public void testBindConditionAndSuggestion_v2_shouldSetSuggestionAdapterAndNoCrash() {
mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null); mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null, null);
final List<Suggestion> suggestions = makeSuggestionsV2("pkg1"); final List<Suggestion> suggestions = makeSuggestionsV2("pkg1");
final DashboardCategory category = mock(DashboardCategory.class); final DashboardCategory category = mock(DashboardCategory.class);
final List<Tile> tiles = new ArrayList<>(); final List<Tile> tiles = new ArrayList<>();
@@ -592,7 +593,8 @@ public class DashboardAdapterTest {
savedInstance.putInt(DashboardAdapter.STATE_SUGGESTION_CONDITION_MODE, savedInstance.putInt(DashboardAdapter.STATE_SUGGESTION_CONDITION_MODE,
DashboardData.HEADER_MODE_FULLY_EXPANDED); DashboardData.HEADER_MODE_FULLY_EXPANDED);
mDashboardAdapter = new DashboardAdapter(mContext, savedInstance, mConditionList, mDashboardAdapter = new DashboardAdapter(mContext, savedInstance, mConditionList,
null /* SuggestionParser */, null /* SuggestionDismissController.Callback */); null /* SuggestionParser */, null /* suggestionControllerMixin */,
null /* SuggestionDismissController.Callback */);
final List<Tile> suggestions = new ArrayList<>(); final List<Tile> suggestions = new ArrayList<>();
final DashboardCategory category = mock(DashboardCategory.class); final DashboardCategory category = mock(DashboardCategory.class);

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.service.settings.suggestions.Suggestion;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper; import android.support.v7.widget.helper.ItemTouchHelper;
@@ -55,6 +56,8 @@ public class SuggestionDismissControllerTest {
@Mock @Mock
private SuggestionParser mSuggestionParser; private SuggestionParser mSuggestionParser;
@Mock @Mock
private SuggestionControllerMixin mSuggestionControllerMixin;
@Mock
private SuggestionDismissController.Callback mCallback; private SuggestionDismissController.Callback mCallback;
private FakeFeatureFactory mFactory; private FakeFeatureFactory mFactory;
@@ -69,7 +72,7 @@ public class SuggestionDismissControllerTest {
when(mRecyclerView.getResources().getDimension(anyInt())).thenReturn(50F); when(mRecyclerView.getResources().getDimension(anyInt())).thenReturn(50F);
mController = new SuggestionDismissController(mContext, mRecyclerView, mController = new SuggestionDismissController(mContext, mRecyclerView,
mSuggestionParser, mCallback); mSuggestionControllerMixin, mSuggestionParser, mCallback);
} }
@Test @Test
@@ -114,4 +117,17 @@ public class SuggestionDismissControllerTest {
eq(mContext), eq(mSuggestionParser), nullable(Tile.class)); eq(mContext), eq(mSuggestionParser), nullable(Tile.class));
verify(mCallback).onSuggestionDismissed(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());
mController.onSwiped(vh, ItemTouchHelper.START);
verify(mFactory.suggestionsFeatureProvider).dismissSuggestion(
eq(mContext), eq(mSuggestionControllerMixin), nullable(Suggestion.class));
verify(mCallback).onSuggestionDismissed(nullable(Suggestion.class));
}
} }

View File

@@ -35,11 +35,12 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager;
import android.provider.Settings.Secure; import android.provider.Settings.Secure;
import android.service.settings.suggestions.Suggestion;
import android.util.FeatureFlagUtils; import android.util.FeatureFlagUtils;
import android.util.Pair; import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Settings.AmbientDisplayPickupSuggestionActivity; import com.android.settings.Settings.AmbientDisplayPickupSuggestionActivity;
import com.android.settings.Settings.AmbientDisplaySuggestionActivity; import com.android.settings.Settings.AmbientDisplaySuggestionActivity;
@@ -95,6 +96,8 @@ public class SuggestionFeatureProviderImplTest {
@Mock @Mock
private SuggestionParser mSuggestionParser; private SuggestionParser mSuggestionParser;
@Mock @Mock
private SuggestionControllerMixin mSuggestionControllerMixin;
@Mock
private Tile mSuggestion; private Tile mSuggestion;
@Mock @Mock
private ActivityManager mActivityManager; private ActivityManager mActivityManager;
@@ -325,13 +328,23 @@ public class SuggestionFeatureProviderImplTest {
@Test @Test
public void dismissSuggestion_noParserOrSuggestion_noop() { public void dismissSuggestion_noParserOrSuggestion_noop() {
mProvider.dismissSuggestion(mContext, null, null); mProvider.dismissSuggestion(mContext, null, (Tile) null);
mProvider.dismissSuggestion(mContext, mSuggestionParser, null); mProvider.dismissSuggestion(mContext, mSuggestionParser, null);
mProvider.dismissSuggestion(mContext, null, mSuggestion); mProvider.dismissSuggestion(mContext, null, mSuggestion);
verifyZeroInteractions(mFactory.metricsFeatureProvider); verifyZeroInteractions(mFactory.metricsFeatureProvider);
} }
@Test
public void dismissSuggestion_noControllerOrSuggestion_noop() {
mProvider.dismissSuggestion(mContext, null, (Suggestion) null);
mProvider.dismissSuggestion(mContext, mSuggestionControllerMixin, null);
mProvider.dismissSuggestion(mContext, null, new Suggestion.Builder("id").build());
verifyZeroInteractions(mFactory.metricsFeatureProvider);
verifyZeroInteractions(mSuggestionControllerMixin);
}
@Test @Test
public void getSuggestionIdentifier_samePackage_returnClassName() { public void getSuggestionIdentifier_samePackage_returnClassName() {
final Tile suggestion = new Tile(); final Tile suggestion = new Tile();