Wallet view UI tweaks.

1. Increase the card view bottom margin to avoid weird card shadow.
before: https://hsv.googleplex.com/6352365370212352
after: https://hsv.googleplex.com/5745851513176064
2. Center the card image in card view.
3. Add fade-in animation for the card carousel UI elements when card
carousel is shown; clean up the unnecessary animation for empty state
view -> non-empty state view.
see demo video:
https://drive.google.com/file/d/1Eih4p1uzw_To_SUQsWGYpkd4M85WX2C9/view?usp=sharing&resourcekey=0-oD7hpOg7uKW6xeCmVNvS0Q

Test: manual
Fix: 184905963
Change-Id: Ied2fffef1f2d9bd6bfcfed0985d39cbed3f52cb0
This commit is contained in:
Silin Huang
2021-05-06 00:31:02 -07:00
parent 208f294d4e
commit b30ccb3dff
4 changed files with 51 additions and 81 deletions

View File

@@ -24,6 +24,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="@dimen/card_margin"
android:layout_marginBottom="@dimen/card_margin"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="@android:color/transparent"
app:cardElevation="12dp">
@@ -32,16 +33,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="fitXY"/>
<ImageView
android:id="@+id/add_card_logo"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_gravity="center"
android:drawable="@drawable/ic_qs_plus"
android:contentDescription="@null"
android:scaleType="fitCenter"
android:visibility="gone"/>
</com.android.systemui.wallet.ui.WalletCardView>
</FrameLayout>

View File

@@ -61,7 +61,7 @@ public class WalletCardCarousel extends RecyclerView {
static final int CARD_ANIM_ALPHA_DELAY = 50;
private final Rect mSystemGestureExclusionZone = new Rect();
private WalletCardCarouselAdapter mWalletCardCarouselAdapter;
private final WalletCardCarouselAdapter mWalletCardCarouselAdapter;
private int mExpectedViewWidth;
private int mCardMarginPx;
private int mCardWidthPx;
@@ -79,12 +79,6 @@ public class WalletCardCarousel extends RecyclerView {
// also be used in DotIndicatorDecoration.
float mEdgeToCenterDistance = Float.MAX_VALUE;
private float mCardCenterToScreenCenterDistancePx = Float.MAX_VALUE;
// When card data is loaded, this many cards should be animated as data is bound to them.
private int mNumCardsToAnimate;
// When card data is loaded, this is the position of the leftmost card to be animated.
private int mCardAnimationStartPosition;
// When card data is loaded, the animations may be delayed so that other animations can complete
private int mExtraAnimationDelay;
interface OnSelectionListener {
/**
@@ -179,10 +173,6 @@ public class WalletCardCarousel extends RecyclerView {
}
}
void setExtraAnimationDelay(int extraAnimationDelay) {
mExtraAnimationDelay = extraAnimationDelay;
}
void setSelectionListener(OnSelectionListener selectionListener) {
mSelectionListener = selectionListener;
}
@@ -200,19 +190,14 @@ public class WalletCardCarousel extends RecyclerView {
}
/**
* Set card data. Returns true if carousel was empty, indicating that views will be animated
* Returns true if the data set is changed.
*/
boolean setData(List<WalletCardViewInfo> data, int selectedIndex) {
boolean wasEmpty = mWalletCardCarouselAdapter.getItemCount() == 0;
mWalletCardCarouselAdapter.setData(data);
boolean setData(List<WalletCardViewInfo> data, int selectedIndex, boolean hasLockStateChanged) {
boolean hasDataChanged = mWalletCardCarouselAdapter.setData(data, hasLockStateChanged);
scrollToPosition(selectedIndex);
if (wasEmpty) {
mNumCardsToAnimate = numCardsOnScreen(data.size(), selectedIndex);
mCardAnimationStartPosition = Math.max(selectedIndex - 1, 0);
}
WalletCardViewInfo selectedCard = data.get(selectedIndex);
mCardScrollListener.onCardScroll(selectedCard, selectedCard, 0);
return wasEmpty;
return hasDataChanged;
}
@Override
@@ -221,19 +206,6 @@ public class WalletCardCarousel extends RecyclerView {
mSelectionListener.onCardSelected(mWalletCardCarouselAdapter.mData.get(position));
}
/**
* The number of cards shown on screen when one of the cards is position in the center. This is
* also the num
*/
private static int numCardsOnScreen(int numCards, int selectedIndex) {
if (numCards <= 2) {
return numCards;
}
// When there are 3 or more cards, 3 cards will be shown unless the first or last card is
// centered on screen.
return selectedIndex > 0 && selectedIndex < (numCards - 1) ? 3 : 2;
}
/**
* The padding pushes the first and last cards in the list to the center when they are
* selected.
@@ -439,9 +411,29 @@ public class WalletCardCarousel extends RecyclerView {
return mData.get(position).getCardId().hashCode();
}
void setData(List<WalletCardViewInfo> data) {
private boolean setData(List<WalletCardViewInfo> data, boolean hasLockedStateChanged) {
List<WalletCardViewInfo> oldData = mData;
mData = data;
notifyDataSetChanged();
if (hasLockedStateChanged || !isUiEquivalent(oldData, data)) {
notifyDataSetChanged();
return true;
}
return false;
}
private boolean isUiEquivalent(
List<WalletCardViewInfo> oldData, List<WalletCardViewInfo> newData) {
if (oldData.size() != newData.size()) {
return false;
}
for (int i = 0; i < newData.size(); i++) {
WalletCardViewInfo oldItem = oldData.get(i);
WalletCardViewInfo newItem = newData.get(i);
if (!oldItem.isUiEquivalent(newItem)) {
return false;
}
}
return true;
}
}

View File

@@ -54,4 +54,11 @@ interface WalletCardViewInfo {
*/
@NonNull
PendingIntent getPendingIntent();
default boolean isUiEquivalent(WalletCardViewInfo other) {
if (other == null) {
return false;
}
return getCardId().equals(other.getCardId());
}
}

View File

@@ -19,8 +19,6 @@ package com.android.systemui.wallet.ui;
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DELAY;
import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_DURATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
@@ -29,6 +27,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -46,9 +45,8 @@ import java.util.List;
public class WalletView extends FrameLayout implements WalletCardCarousel.OnCardScrollListener {
private static final String TAG = "WalletView";
private static final int CAROUSEL_IN_ANIMATION_DURATION = 300;
private static final int CAROUSEL_IN_ANIMATION_DURATION = 100;
private static final int CAROUSEL_OUT_ANIMATION_DURATION = 200;
private static final int CARD_LABEL_ANIM_DELAY = 133;
private final WalletCardCarousel mCardCarousel;
private final ImageView mIcon;
@@ -57,14 +55,12 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
private final Button mAppButton;
// Displays underneath the carousel, allow user to unlock device, verify card, etc.
private final Button mActionButton;
private final Interpolator mInInterpolator;
private final Interpolator mOutInterpolator;
private final float mAnimationTranslationX;
private final ViewGroup mCardCarouselContainer;
private final TextView mErrorView;
private final ViewGroup mEmptyStateView;
private CharSequence mCenterCardText;
private Drawable mCenterCardIcon;
private boolean mIsDeviceLocked = false;
public WalletView(Context context) {
@@ -83,8 +79,6 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
mActionButton = requireViewById(R.id.wallet_action_button);
mErrorView = requireViewById(R.id.error_view);
mEmptyStateView = requireViewById(R.id.wallet_empty_state);
mInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mOutInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.accelerate_cubic);
mAnimationTranslationX = mCardCarousel.getCardWidthPx() / 4f;
@@ -109,7 +103,6 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
Drawable centerCardIcon = centerCard.getIcon();
if (!TextUtils.equals(mCenterCardText, centerCardText)) {
mCenterCardText = centerCardText;
mCenterCardIcon = centerCardIcon;
mCardLabel.setText(centerCardText);
mIcon.setImageDrawable(centerCardIcon);
}
@@ -134,39 +127,15 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
*/
void showCardCarousel(
List<WalletCardViewInfo> data, int selectedIndex, boolean isDeviceLocked) {
boolean shouldAnimate =
mCardCarousel.setData(data, selectedIndex, mIsDeviceLocked != isDeviceLocked);
mIsDeviceLocked = isDeviceLocked;
boolean shouldAnimate = mCardCarousel.setData(data, selectedIndex);
mCardCarouselContainer.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mEmptyStateView.setVisibility(GONE);
renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
if (shouldAnimate) {
// If the empty state is visible, animate it away and delay the card carousel animation
int emptyStateAnimDelay = 0;
if (mEmptyStateView.getVisibility() == VISIBLE) {
emptyStateAnimDelay = CARD_ANIM_ALPHA_DURATION;
mEmptyStateView.animate()
.alpha(0)
.setDuration(emptyStateAnimDelay)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mEmptyStateView.setVisibility(GONE);
}
})
.start();
}
mCardLabel.setAlpha(0f);
mCardLabel.animate().alpha(1f)
.setStartDelay(CARD_LABEL_ANIM_DELAY + emptyStateAnimDelay)
.setDuration(CARD_ANIM_ALPHA_DURATION)
.start();
mCardCarousel.setExtraAnimationDelay(emptyStateAnimDelay);
mCardCarousel.setTranslationX(mAnimationTranslationX);
mCardCarousel.animate().translationX(0)
.setInterpolator(mInInterpolator)
.setDuration(CAROUSEL_IN_ANIMATION_DURATION)
.setStartDelay(emptyStateAnimDelay)
.start();
animateViewsShown(mIcon, mCardLabel, mActionButton);
}
}
@@ -277,6 +246,15 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
}
}
private static void animateViewsShown(View... uiElements) {
for (View view : uiElements) {
if (view.getVisibility() == VISIBLE) {
view.setAlpha(0f);
view.animate().alpha(1f).setDuration(CAROUSEL_IN_ANIMATION_DURATION).start();
}
}
}
private static CharSequence getLabelText(WalletCardViewInfo card) {
String[] rawLabel = card.getLabel().toString().split("\\n");
return rawLabel.length == 2 ? rawLabel[0] : card.getLabel();