Reset dismissal UI to the baseline when exiting the page.
Flip the card back to slice view if users exit the page instead of getting the dismissal UI preserved. We should also reset the UI to the baseline if users dismiss a card, so when the view is reused, slice view can then be displayed correctly. Change-Id: If5448c57dec44ef5c1d3472358d4ddc9398538b8 Fixes: 119820168 Test: robotests
This commit is contained in:
@@ -69,9 +69,9 @@ public class SliceContextualCardController implements ContextualCardController {
|
|||||||
dbHelper.markContextualCardAsDismissed(mContext, card.getName());
|
dbHelper.markContextualCardAsDismissed(mContext, card.getName());
|
||||||
});
|
});
|
||||||
showFeedbackDialog(card);
|
showFeedbackDialog(card);
|
||||||
final ContextualCardFeatureProvider contexualCardFeatureProvider =
|
final ContextualCardFeatureProvider contextualCardFeatureProvider =
|
||||||
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
|
FeatureFactory.getFactory(mContext).getContextualCardFeatureProvider();
|
||||||
contexualCardFeatureProvider.logContextualCardDismiss(mContext, card);
|
contextualCardFeatureProvider.logContextualCardDismiss(mContext, card);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -28,8 +28,11 @@ import android.widget.ViewFlipper;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.lifecycle.Lifecycle;
|
||||||
|
import androidx.lifecycle.LifecycleObserver;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.OnLifecycleEvent;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.slice.Slice;
|
import androidx.slice.Slice;
|
||||||
import androidx.slice.SliceItem;
|
import androidx.slice.SliceItem;
|
||||||
@@ -51,13 +54,15 @@ import java.util.Set;
|
|||||||
* Card renderer for {@link ContextualCard} built as slices.
|
* Card renderer for {@link ContextualCard} built as slices.
|
||||||
*/
|
*/
|
||||||
public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
||||||
SliceView.OnSliceActionListener {
|
SliceView.OnSliceActionListener, LifecycleObserver {
|
||||||
public static final int VIEW_TYPE = R.layout.homepage_slice_tile;
|
public static final int VIEW_TYPE = R.layout.homepage_slice_tile;
|
||||||
|
|
||||||
private static final String TAG = "SliceCardRenderer";
|
private static final String TAG = "SliceCardRenderer";
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final Map<String, LiveData<Slice>> mSliceLiveDataMap;
|
final Map<String, LiveData<Slice>> mSliceLiveDataMap;
|
||||||
|
@VisibleForTesting
|
||||||
|
final Set<SliceViewHolder> mFlippedCardSet;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final LifecycleOwner mLifecycleOwner;
|
private final LifecycleOwner mLifecycleOwner;
|
||||||
@@ -71,6 +76,8 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
|||||||
mSliceLiveDataMap = new ArrayMap<>();
|
mSliceLiveDataMap = new ArrayMap<>();
|
||||||
mControllerRendererPool = controllerRendererPool;
|
mControllerRendererPool = controllerRendererPool;
|
||||||
mCardSet = new ArraySet<>();
|
mCardSet = new ArraySet<>();
|
||||||
|
mFlippedCardSet = new ArraySet<>();
|
||||||
|
mLifecycleOwner.getLifecycle().addObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,20 +129,23 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initDismissalActions(SliceViewHolder cardHolder, ContextualCard card) {
|
private void initDismissalActions(SliceViewHolder cardHolder, ContextualCard card) {
|
||||||
final ViewFlipper viewFlipper = cardHolder.itemView.findViewById(R.id.viewFlipper);
|
|
||||||
cardHolder.sliceView.setOnLongClickListener(v -> {
|
cardHolder.sliceView.setOnLongClickListener(v -> {
|
||||||
viewFlipper.showNext();
|
cardHolder.viewFlipper.showNext();
|
||||||
|
mFlippedCardSet.add(cardHolder);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
final Button btnKeep = cardHolder.itemView.findViewById(R.id.keep);
|
final Button btnKeep = cardHolder.itemView.findViewById(R.id.keep);
|
||||||
btnKeep.setOnClickListener(v -> {
|
btnKeep.setOnClickListener(v -> {
|
||||||
viewFlipper.showPrevious();
|
cardHolder.resetCard();
|
||||||
|
mFlippedCardSet.remove(cardHolder);
|
||||||
});
|
});
|
||||||
|
|
||||||
final Button btnRemove = cardHolder.itemView.findViewById(R.id.remove);
|
final Button btnRemove = cardHolder.itemView.findViewById(R.id.remove);
|
||||||
btnRemove.setOnClickListener(v -> {
|
btnRemove.setOnClickListener(v -> {
|
||||||
mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card);
|
mControllerRendererPool.getController(mContext, card.getCardType()).onDismissed(card);
|
||||||
|
cardHolder.resetCard();
|
||||||
|
mFlippedCardSet.remove(cardHolder);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,12 +168,24 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
||||||
|
public void onStop() {
|
||||||
|
mFlippedCardSet.stream().forEach(holder -> holder.resetCard());
|
||||||
|
mFlippedCardSet.clear();
|
||||||
|
}
|
||||||
|
|
||||||
public static class SliceViewHolder extends RecyclerView.ViewHolder {
|
public static class SliceViewHolder extends RecyclerView.ViewHolder {
|
||||||
public final SliceView sliceView;
|
public final SliceView sliceView;
|
||||||
|
public final ViewFlipper viewFlipper;
|
||||||
|
|
||||||
public SliceViewHolder(View view) {
|
public SliceViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
sliceView = view.findViewById(R.id.slice_view);
|
sliceView = view.findViewById(R.id.slice_view);
|
||||||
|
viewFlipper = view.findViewById(R.id.viewFlipper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetCard() {
|
||||||
|
viewFlipper.setDisplayedChild(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ package com.android.settings.homepage.contextualcards.slices;
|
|||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
@@ -51,10 +52,14 @@ import org.robolectric.android.controller.ActivityController;
|
|||||||
@RunWith(SettingsRobolectricTestRunner.class)
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
public class SliceContextualCardRendererTest {
|
public class SliceContextualCardRendererTest {
|
||||||
|
|
||||||
|
private static final String TEST_SLICE_URI = "content://test/test";
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private LiveData<Slice> mSliceLiveData;
|
private LiveData<Slice> mSliceLiveData;
|
||||||
@Mock
|
@Mock
|
||||||
private ControllerRendererPool mControllerRendererPool;
|
private ControllerRendererPool mControllerRendererPool;
|
||||||
|
@Mock
|
||||||
|
private SliceContextualCardController mController;
|
||||||
|
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
private SliceContextualCardRenderer mRenderer;
|
private SliceContextualCardRenderer mRenderer;
|
||||||
@@ -75,10 +80,9 @@ public class SliceContextualCardRendererTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindView_shouldSetScrollableToFalse() {
|
public void bindView_shouldSetScrollableToFalse() {
|
||||||
final String sliceUri = "content://com.android.settings.slices/action/flashlight";
|
|
||||||
RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
|
|
||||||
mRenderer.bindView(viewHolder, buildContextualCard(sliceUri));
|
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
((SliceContextualCardRenderer.SliceViewHolder) viewHolder).sliceView.isScrollable
|
((SliceContextualCardRenderer.SliceViewHolder) viewHolder).sliceView.isScrollable
|
||||||
@@ -99,64 +103,107 @@ public class SliceContextualCardRendererTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindView_newSliceLiveData_shouldAddDataToMap() {
|
public void bindView_newSliceLiveData_shouldAddDataToMap() {
|
||||||
final String sliceUri = "content://com.android.settings.slices/action/flashlight";
|
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(sliceUri));
|
|
||||||
|
|
||||||
assertThat(mRenderer.mSliceLiveDataMap.size()).isEqualTo(1);
|
assertThat(mRenderer.mSliceLiveDataMap.size()).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindView_sliceLiveDataShouldObserveSliceView() {
|
public void bindView_sliceLiveDataShouldObserveSliceView() {
|
||||||
final String sliceUri = "content://com.android.settings.slices/action/flashlight";
|
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(sliceUri));
|
assertThat(mRenderer.mSliceLiveDataMap.get(TEST_SLICE_URI).hasObservers()).isTrue();
|
||||||
|
|
||||||
assertThat(mRenderer.mSliceLiveDataMap.get(sliceUri).hasObservers()).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindView_sliceLiveDataShouldRemoveObservers() {
|
public void bindView_sliceLiveDataShouldRemoveObservers() {
|
||||||
final String sliceUri = "content://com.android.settings.slices/action/flashlight";
|
mRenderer.mSliceLiveDataMap.put(TEST_SLICE_URI, mSliceLiveData);
|
||||||
mRenderer.mSliceLiveDataMap.put(sliceUri, mSliceLiveData);
|
|
||||||
|
|
||||||
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(sliceUri));
|
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
verify(mSliceLiveData).removeObservers(mLifecycleOwner);
|
verify(mSliceLiveData).removeObservers(mLifecycleOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void longClick_shouldFlipCard() {
|
public void longClick_shouldFlipCard() {
|
||||||
final String sliceUri = "content://com.android.settings.slices/action/flashlight";
|
|
||||||
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||||
final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper);
|
final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper);
|
||||||
final View dismissalView = viewHolder.itemView.findViewById(R.id.dismissal_view);
|
final View dismissalView = viewHolder.itemView.findViewById(R.id.dismissal_view);
|
||||||
mRenderer.bindView(viewHolder, buildContextualCard(sliceUri));
|
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
assertThat(card).isNotNull();
|
|
||||||
card.performLongClick();
|
card.performLongClick();
|
||||||
|
|
||||||
assertThat(viewFlipper.getCurrentView()).isEqualTo(dismissalView);
|
assertThat(viewFlipper.getCurrentView()).isEqualTo(dismissalView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void longClick_shouldAddViewHolderToSet() {
|
||||||
|
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
|
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||||
|
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
|
card.performLongClick();
|
||||||
|
|
||||||
|
assertThat(mRenderer.mFlippedCardSet).contains(viewHolder);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void viewClick_keepCard_shouldFlipBackToSlice() {
|
public void viewClick_keepCard_shouldFlipBackToSlice() {
|
||||||
final String sliceUri = "content://com.android.settings.slices/action/flashlight";
|
|
||||||
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||||
final Button btnKeep = viewHolder.itemView.findViewById(R.id.keep);
|
final Button btnKeep = viewHolder.itemView.findViewById(R.id.keep);
|
||||||
final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper);
|
final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper);
|
||||||
mRenderer.bindView(viewHolder, buildContextualCard(sliceUri));
|
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
assertThat(card).isNotNull();
|
|
||||||
card.performLongClick();
|
card.performLongClick();
|
||||||
assertThat(btnKeep).isNotNull();
|
|
||||||
btnKeep.performClick();
|
btnKeep.performClick();
|
||||||
|
|
||||||
assertThat(viewFlipper.getCurrentView()).isInstanceOf(SliceView.class);
|
assertThat(viewFlipper.getCurrentView()).isInstanceOf(SliceView.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void viewClick_keepCard_shouldRemoveViewHolderFromSet() {
|
||||||
|
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
|
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||||
|
final Button btnKeep = viewHolder.itemView.findViewById(R.id.keep);
|
||||||
|
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
|
card.performLongClick();
|
||||||
|
btnKeep.performClick();
|
||||||
|
|
||||||
|
assertThat(mRenderer.mFlippedCardSet).doesNotContain(viewHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void viewClick_removeCard_shouldRemoveViewHolderFromSet() {
|
||||||
|
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
|
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||||
|
final Button btnRemove = viewHolder.itemView.findViewById(R.id.remove);
|
||||||
|
final ContextualCard contextualCard = buildContextualCard(TEST_SLICE_URI);
|
||||||
|
mRenderer.bindView(viewHolder, contextualCard);
|
||||||
|
doReturn(mController).when(mControllerRendererPool).getController(mActivity,
|
||||||
|
ContextualCard.CardType.SLICE);
|
||||||
|
|
||||||
|
card.performLongClick();
|
||||||
|
btnRemove.performClick();
|
||||||
|
|
||||||
|
assertThat(mRenderer.mFlippedCardSet).doesNotContain(viewHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_cardIsFlipped_shouldFlipBack() {
|
||||||
|
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
|
||||||
|
final View card = viewHolder.itemView.findViewById(R.id.slice_view);
|
||||||
|
final ViewFlipper viewFlipper = viewHolder.itemView.findViewById(R.id.viewFlipper);
|
||||||
|
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
|
||||||
|
|
||||||
|
card.performLongClick();
|
||||||
|
mRenderer.onStop();
|
||||||
|
|
||||||
|
assertThat(viewFlipper.getCurrentView()).isInstanceOf(SliceView.class);
|
||||||
|
}
|
||||||
|
|
||||||
private RecyclerView.ViewHolder getSliceViewHolder() {
|
private RecyclerView.ViewHolder getSliceViewHolder() {
|
||||||
final int viewType = mRenderer.getViewType(false /* isHalfWidth */);
|
final int viewType = mRenderer.getViewType(false /* isHalfWidth */);
|
||||||
final RecyclerView recyclerView = new RecyclerView(mActivity);
|
final RecyclerView recyclerView = new RecyclerView(mActivity);
|
||||||
@@ -169,6 +216,7 @@ public class SliceContextualCardRendererTest {
|
|||||||
private ContextualCard buildContextualCard(String sliceUri) {
|
private ContextualCard buildContextualCard(String sliceUri) {
|
||||||
return new ContextualCard.Builder()
|
return new ContextualCard.Builder()
|
||||||
.setName("test_name")
|
.setName("test_name")
|
||||||
|
.setCardType(ContextualCard.CardType.SLICE)
|
||||||
.setSliceUri(Uri.parse(sliceUri))
|
.setSliceUri(Uri.parse(sliceUri))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user