Merge "Fix the janky transition of contextual cards." into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-05-29 11:51:51 +00:00
committed by Android (Google) Code Review
5 changed files with 80 additions and 5 deletions

View File

@@ -23,6 +23,7 @@ import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import androidx.slice.Slice;
import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer; import com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer;
@@ -66,6 +67,7 @@ public class ContextualCard {
private final int mViewType; private final int mViewType;
private final boolean mIsPendingDismiss; private final boolean mIsPendingDismiss;
private final boolean mHasInlineAction; private final boolean mHasInlineAction;
private final Slice mSlice;
public String getName() { public String getName() {
return mName; return mName;
@@ -127,6 +129,10 @@ public class ContextualCard {
return mHasInlineAction; return mHasInlineAction;
} }
public Slice getSlice() {
return mSlice;
}
public Builder mutate() { public Builder mutate() {
return mBuilder; return mBuilder;
} }
@@ -147,6 +153,7 @@ public class ContextualCard {
mViewType = builder.mViewType; mViewType = builder.mViewType;
mIsPendingDismiss = builder.mIsPendingDismiss; mIsPendingDismiss = builder.mIsPendingDismiss;
mHasInlineAction = builder.mHasInlineAction; mHasInlineAction = builder.mHasInlineAction;
mSlice = builder.mSlice;
} }
ContextualCard(Cursor c) { ContextualCard(Cursor c) {
@@ -179,6 +186,8 @@ public class ContextualCard {
mBuilder.setIsPendingDismiss(mIsPendingDismiss); mBuilder.setIsPendingDismiss(mIsPendingDismiss);
mHasInlineAction = false; mHasInlineAction = false;
mBuilder.setHasInlineAction(mHasInlineAction); mBuilder.setHasInlineAction(mHasInlineAction);
mSlice = null;
mBuilder.setSlice(mSlice);
} }
@Override @Override
@@ -225,6 +234,7 @@ public class ContextualCard {
private int mViewType; private int mViewType;
private boolean mIsPendingDismiss; private boolean mIsPendingDismiss;
private boolean mHasInlineAction; private boolean mHasInlineAction;
private Slice mSlice;
public Builder setName(String name) { public Builder setName(String name) {
mName = name; mName = name;
@@ -296,6 +306,14 @@ public class ContextualCard {
return this; return this;
} }
/**
* Cache a slice created at pre-check time for later usage.
*/
public Builder setSlice(Slice slice) {
mSlice = slice;
return this;
}
public ContextualCard build() { public ContextualCard build() {
return new ContextualCard(this); return new ContextualCard(this);
} }

View File

@@ -96,14 +96,17 @@ public class EligibleCardChecker implements Callable<ContextualCard> {
final Slice slice = bindSlice(uri); final Slice slice = bindSlice(uri);
if (isSliceToggleable(slice)) {
mCard = card.mutate().setHasInlineAction(true).build();
}
if (slice == null || slice.hasHint(HINT_ERROR)) { if (slice == null || slice.hasHint(HINT_ERROR)) {
Log.w(TAG, "Failed to bind slice, not eligible for display " + uri); Log.w(TAG, "Failed to bind slice, not eligible for display " + uri);
return false; return false;
} }
mCard = card.mutate().setSlice(slice).build();
if (isSliceToggleable(slice)) {
mCard = card.mutate().setHasInlineAction(true).build();
}
return true; return true;
} }

View File

@@ -47,6 +47,7 @@ import com.android.settings.homepage.contextualcards.CardContentProvider;
import com.android.settings.homepage.contextualcards.ContextualCard; import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardRenderer; import com.android.settings.homepage.contextualcards.ContextualCardRenderer;
import com.android.settings.homepage.contextualcards.ControllerRendererPool; import com.android.settings.homepage.contextualcards.ControllerRendererPool;
import com.android.settings.homepage.contextualcards.slices.SliceFullCardRendererHelper.SliceViewHolder;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import java.util.Map; import java.util.Map;
@@ -102,6 +103,11 @@ public class SliceContextualCardRenderer implements ContextualCardRenderer, Life
return; return;
} }
// Show cached slice first before slice binding completed to avoid jank.
if (holder.getItemViewType() != VIEW_TYPE_HALF_WIDTH) {
((SliceViewHolder) holder).sliceView.setSlice(card.getSlice());
}
LiveData<Slice> sliceLiveData = mSliceLiveDataMap.get(uri); LiveData<Slice> sliceLiveData = mSliceLiveDataMap.get(uri);
if (sliceLiveData == null) { if (sliceLiveData == null) {

View File

@@ -114,6 +114,17 @@ public class EligibleCardCheckerTest {
.isFalse(); .isFalse();
} }
@Test
public void isCardEligibleToDisplay_sliceNotNull_cacheSliceToCard() {
final ContextualWifiSlice wifiSlice = new ContextualWifiSlice(mContext);
final Slice slice = wifiSlice.getSlice();
doReturn(slice).when(mEligibleCardChecker).bindSlice(any(Uri.class));
mEligibleCardChecker.isCardEligibleToDisplay(getContextualCard(TEST_SLICE_URI));
assertThat(mEligibleCardChecker.mCard.getSlice()).isNotNull();
}
private ContextualCard getContextualCard(Uri sliceUri) { private ContextualCard getContextualCard(Uri sliceUri) {
return new ContextualCard.Builder() return new ContextualCard.Builder()
.setName("test_card") .setName("test_card")

View File

@@ -17,10 +17,12 @@
package com.android.settings.homepage.contextualcards.slices; package com.android.settings.homepage.contextualcards.slices;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH; import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_STICKY;
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.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.app.Activity; import android.app.Activity;
@@ -39,6 +41,7 @@ import com.android.settings.R;
import com.android.settings.homepage.contextualcards.ContextualCard; import com.android.settings.homepage.contextualcards.ContextualCard;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment; import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.homepage.contextualcards.ControllerRendererPool; import com.android.settings.homepage.contextualcards.ControllerRendererPool;
import com.android.settings.wifi.slice.ContextualWifiSlice;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -81,7 +84,7 @@ public class SliceContextualCardRendererTest {
@Test @Test
public void bindView_invalidScheme_sliceShouldBeNull() { public void bindView_invalidScheme_sliceShouldBeNull() {
final Uri sliceUri = Uri.parse("contet://com.android.settings.slices/action/flashlight"); final Uri sliceUri = Uri.parse("contet://com.android.settings.slices/action/flashlight");
RecyclerView.ViewHolder viewHolder = getSliceViewHolder(); final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
mRenderer.bindView(viewHolder, buildContextualCard(sliceUri)); mRenderer.bindView(viewHolder, buildContextualCard(sliceUri));
@@ -90,6 +93,29 @@ public class SliceContextualCardRendererTest {
.isNull(); .isNull();
} }
@Test
public void bindView_viewTypeFullWidth_shouldSetCachedSlice() {
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
assertThat(
((SliceFullCardRendererHelper.SliceViewHolder) viewHolder).sliceView.getSlice())
.isNotNull();
}
@Test
public void bindView_viewTypeSticky_shouldSetCachedSlice() {
final RecyclerView.ViewHolder viewHolder = spy(getStickyViewHolder());
doReturn(VIEW_TYPE_STICKY).when(viewHolder).getItemViewType();
mRenderer.bindView(viewHolder, buildContextualCard(TEST_SLICE_URI));
assertThat(
((SliceFullCardRendererHelper.SliceViewHolder) viewHolder).sliceView.getSlice())
.isNotNull();
}
@Test @Test
public void bindView_newSliceLiveData_shouldAddDataToMap() { public void bindView_newSliceLiveData_shouldAddDataToMap() {
mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI)); mRenderer.bindView(getSliceViewHolder(), buildContextualCard(TEST_SLICE_URI));
@@ -246,12 +272,23 @@ public class SliceContextualCardRendererTest {
return mRenderer.createViewHolder(view, VIEW_TYPE_FULL_WIDTH); return mRenderer.createViewHolder(view, VIEW_TYPE_FULL_WIDTH);
} }
private RecyclerView.ViewHolder getStickyViewHolder() {
final RecyclerView recyclerView = new RecyclerView(mActivity);
recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
final View view = LayoutInflater.from(mActivity).inflate(VIEW_TYPE_STICKY, recyclerView,
false);
return mRenderer.createViewHolder(view, VIEW_TYPE_STICKY);
}
private ContextualCard buildContextualCard(Uri sliceUri) { private ContextualCard buildContextualCard(Uri sliceUri) {
final Slice slice = new ContextualWifiSlice(mActivity).getSlice();
return new ContextualCard.Builder() return new ContextualCard.Builder()
.setName("test_name") .setName("test_name")
.setCardType(ContextualCard.CardType.SLICE) .setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(sliceUri) .setSliceUri(sliceUri)
.setViewType(VIEW_TYPE_FULL_WIDTH) .setViewType(VIEW_TYPE_FULL_WIDTH)
.setSlice(slice)
.build(); .build();
} }
} }