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:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,4 +54,11 @@ interface WalletCardViewInfo {
|
||||
*/
|
||||
@NonNull
|
||||
PendingIntent getPendingIntent();
|
||||
|
||||
default boolean isUiEquivalent(WalletCardViewInfo other) {
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
return getCardId().equals(other.getCardId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user