From 8833128a37ac5f54f7933be857e8e73ed961568d Mon Sep 17 00:00:00 2001 From: Silin Huang Date: Mon, 3 May 2021 12:15:38 -0700 Subject: [PATCH] Support non-active tokens in QuickAccessWallet. 1. Individual card states (active, pendingActivation, suspended) have their own header icon and label. When scrolling between cards that have different states, the icon and label will fade in/out. 2. The pendingActivation cards will have a verification button underneath the card view; the button text is parsed from cards label. 3. Add an overflow menu for wallet view, it will navigate to Lock Screen Settings page. (TODO in next CL) 4. Update the way we render header icon: because gmscore now provide new version of icons, the programmatical tweaks no longer needed on the SysUI side. 5. Add Falsing prevention for the locked state wallet view: cardImage/actionButton/appButton 6. Other UI polish: change the Wallet Tile secondary label to card last4 digits, etc. demo: https://drive.google.com/file/d/1syNnPFdIGh24sHWU2So-wsPxZTIg7xI9/view?usp=sharing&resourcekey=0-Iw3ZotiOJlE8jPImK7QQhQ Test: manual Test: atest Bug: 186496392 Bug: 150332483 Fix: 184057200 Fix: 184905963 Change-Id: I84f487154104ffe1636c98ef1538bf99102c0442 --- .../drawable/circle_wallet_secondary_56dp.xml | 23 ------ .../SystemUI/res/layout/wallet_fullscreen.xml | 7 ++ .../wallet_activity_options_menu.xml} | 19 ++--- packages/SystemUI/res/values/dimens.xml | 1 + packages/SystemUI/res/values/strings.xml | 6 +- .../qs/tiles/QuickAccessWalletTile.java | 19 +++-- .../systemui/wallet/ui/WalletActivity.java | 46 ++++++++++-- .../wallet/ui/WalletCardCarousel.java | 11 +-- .../wallet/ui/WalletCardViewInfo.java | 7 ++ .../wallet/ui/WalletScreenController.java | 31 ++++---- .../systemui/wallet/ui/WalletView.java | 73 +++++++++++-------- .../qs/tiles/QuickAccessWalletTileTest.java | 6 +- .../wallet/ui/WalletScreenControllerTest.java | 69 +++++++++++++++--- 13 files changed, 197 insertions(+), 121 deletions(-) delete mode 100644 packages/SystemUI/res/drawable/circle_wallet_secondary_56dp.xml rename packages/SystemUI/res/{drawable/circle_wallet_primary_56dp.xml => menu/wallet_activity_options_menu.xml} (64%) diff --git a/packages/SystemUI/res/drawable/circle_wallet_secondary_56dp.xml b/packages/SystemUI/res/drawable/circle_wallet_secondary_56dp.xml deleted file mode 100644 index 1634e2db33225..0000000000000 --- a/packages/SystemUI/res/drawable/circle_wallet_secondary_56dp.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - diff --git a/packages/SystemUI/res/layout/wallet_fullscreen.xml b/packages/SystemUI/res/layout/wallet_fullscreen.xml index b47c2f2e1821a..bbb180f720a32 100644 --- a/packages/SystemUI/res/layout/wallet_fullscreen.xml +++ b/packages/SystemUI/res/layout/wallet_fullscreen.xml @@ -19,6 +19,13 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false"> + - - - - - - + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b76a4cea10044..9d832913020ac 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1477,6 +1477,7 @@ 36sp + 56dp 16dp 24dp 2dp diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index bbb551996cd84..8a342009ccfd8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1656,14 +1656,14 @@ Show all Unlock to pay - - Ready - Set up payment + Not set up Unlock to use There was a problem getting your cards, please try again later + + Lock screen settings Work profile diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index be40423df3fab..d1b74cd7a505e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -75,7 +75,7 @@ public class QuickAccessWalletTile extends QSTileImpl { private final FeatureFlags mFeatureFlags; @VisibleForTesting Drawable mCardViewDrawable; - private boolean mHasCard; + private WalletCard mSelectedCard; @Inject public QuickAccessWalletTile( @@ -125,7 +125,7 @@ public class QuickAccessWalletTile extends QSTileImpl { view == null ? null : ActivityLaunchAnimator.Controller.fromView(view); mUiHandler.post(() -> { - if (mHasCard) { + if (mSelectedCard != null) { Intent intent = new Intent(mContext, WalletActivity.class) .setAction(Intent.ACTION_VIEW) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); @@ -157,15 +157,14 @@ public class QuickAccessWalletTile extends QSTileImpl { state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen); boolean isDeviceLocked = !mKeyguardStateController.isUnlocked(); if (mQuickAccessWalletClient.isWalletServiceAvailable()) { - if (mHasCard) { + if (mSelectedCard != null) { if (isDeviceLocked) { state.state = Tile.STATE_INACTIVE; state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_device_locked); } else { state.state = Tile.STATE_ACTIVE; - state.secondaryLabel = - mContext.getString(R.string.wallet_secondary_label_active); + state.secondaryLabel = mSelectedCard.getContentDescription(); } } else { state.state = Tile.STATE_INACTIVE; @@ -208,7 +207,7 @@ public class QuickAccessWalletTile extends QSTileImpl { mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height); int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); GetWalletCardsRequest request = - new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 2); + new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1); mQuickAccessWalletClient.getWalletCards(mExecutor, request, mCardRetriever); } @@ -222,7 +221,7 @@ public class QuickAccessWalletTile extends QSTileImpl { if (cards.isEmpty()) { Log.d(TAG, "No wallet cards exist."); mCardViewDrawable = null; - mHasCard = false; + mSelectedCard = null; refreshState(); return; } @@ -231,8 +230,8 @@ public class QuickAccessWalletTile extends QSTileImpl { Log.d(TAG, "Selected card index out of bounds."); return; } - mCardViewDrawable = cards.get(selectedIndex).getCardImage().loadDrawable(mContext); - mHasCard = true; + mSelectedCard = cards.get(selectedIndex); + mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext); refreshState(); } @@ -240,7 +239,7 @@ public class QuickAccessWalletTile extends QSTileImpl { public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) { Log.w(TAG, "Error retrieve wallet cards"); mCardViewDrawable = null; - mHasCard = false; + mSelectedCard = null; refreshState(); } } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java index f884687daad4b..92e68f36d4917 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java @@ -16,13 +16,14 @@ package com.android.systemui.wallet.ui; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Handler; import android.service.quickaccesswallet.QuickAccessWalletClient; +import android.view.Menu; import android.view.MenuItem; import android.view.Window; +import android.view.WindowManager; +import android.widget.Toolbar; import androidx.annotation.NonNull; @@ -30,6 +31,7 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -50,6 +52,7 @@ public class WalletActivity extends LifecycleActivity { private final ActivityStarter mActivityStarter; private final Executor mExecutor; private final Handler mHandler; + private final FalsingManager mFalsingManager; private final UserTracker mUserTracker; private WalletScreenController mWalletScreenController; @@ -61,6 +64,7 @@ public class WalletActivity extends LifecycleActivity { ActivityStarter activityStarter, @Background Executor executor, @Main Handler handler, + FalsingManager falsingManager, UserTracker userTracker) { mQuickAccessWalletClient = quickAccessWalletClient; mKeyguardStateController = keyguardStateController; @@ -68,19 +72,23 @@ public class WalletActivity extends LifecycleActivity { mActivityStarter = activityStarter; mExecutor = executor; mHandler = handler; + mFalsingManager = falsingManager; mUserTracker = userTracker; } @Override protected void onCreate(Bundle savedInstanceState) { - getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.quick_access_wallet); - getWindow().getDecorView().setBackgroundColor(getColor(R.color.wallet_white)); + Toolbar toolbar = findViewById(R.id.action_bar); + if (toolbar != null) { + setActionBar(toolbar); + } setTitle(""); - getActionBar().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeAsUpIndicator(R.drawable.ic_close); getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close); @@ -93,15 +101,28 @@ public class WalletActivity extends LifecycleActivity { mExecutor, mHandler, mUserTracker, + mFalsingManager, mKeyguardStateController); walletView.getAppButton().setOnClickListener( - v -> mActivityStarter.startActivity( - mQuickAccessWalletClient.createWalletIntent(), true)); + v -> { + if (!mKeyguardStateController.isUnlocked() + && mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } + mActivityStarter.startActivity( + mQuickAccessWalletClient.createWalletIntent(), true); + finish(); + }); // Click the action button to re-render the screen when the device is unlocked. if (!mKeyguardStateController.isUnlocked()) { walletView.getActionButton().setOnClickListener( - v -> mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false)); + v -> { + if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } + mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false); + }); } } @@ -117,12 +138,21 @@ public class WalletActivity extends LifecycleActivity { mWalletScreenController.queryWalletCards(); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.wallet_activity_options_menu, menu); + return super.onCreateOptionsMenu(menu); + } + @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { int itemId = item.getItemId(); if (itemId == android.R.id.home) { finish(); return true; + } else if (itemId == R.id.wallet_lockscreen_settings) { + // TODO(b/186496392): Navigate to Lock Screen Settings page when the item is clicked. + return true; } return super.onOptionsItemSelected(item); } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java index 21e55496d3e62..4200241603ea6 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java @@ -205,8 +205,8 @@ public class WalletCardCarousel extends RecyclerView { boolean setData(List data, int selectedIndex) { boolean wasEmpty = mWalletCardCarouselAdapter.getItemCount() == 0; mWalletCardCarouselAdapter.setData(data); + scrollToPosition(selectedIndex); if (wasEmpty) { - scrollToPosition(selectedIndex); mNumCardsToAnimate = numCardsOnScreen(data.size(), selectedIndex); mCardAnimationStartPosition = Math.max(selectedIndex - 1, 0); } @@ -427,15 +427,6 @@ public class WalletCardCarousel extends RecyclerView { mSelectionListener.onCardClicked(cardViewInfo); } }); - if (mNumCardsToAnimate > 0 && (position - mCardAnimationStartPosition < 2)) { - mNumCardsToAnimate--; - int startDelay = (position - mCardAnimationStartPosition) * CARD_ANIM_ALPHA_DELAY - + mExtraAnimationDelay; - viewHolder.itemView.setAlpha(0f); - viewHolder.itemView.animate().alpha(1f) - .setStartDelay(Math.max(0, startDelay)) - .setDuration(CARD_ANIM_ALPHA_DURATION).start(); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java index 669d6664b305f..3d37320f3d325 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardViewInfo.java @@ -16,6 +16,7 @@ package com.android.systemui.wallet.ui; +import android.app.PendingIntent; import android.graphics.drawable.Drawable; import androidx.annotation.NonNull; @@ -47,4 +48,10 @@ interface WalletCardViewInfo { */ @NonNull CharSequence getLabel(); + + /** + * Pending intent upon the card is clicked. + */ + @NonNull + PendingIntent getPendingIntent(); } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java index d1a2c8a49a955..ec62981cae736 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java @@ -16,6 +16,7 @@ package com.android.systemui.wallet.ui; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -41,6 +42,7 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -57,7 +59,6 @@ public class WalletScreenController implements KeyguardStateController.Callback { private static final String TAG = "WalletScreenCtrl"; - private static final String PREFS_HAS_CARDS = "has_cards"; private static final String PREFS_WALLET_VIEW_HEIGHT = "wallet_view_height"; private static final int MAX_CARDS = 10; private static final long SELECTION_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(30); @@ -72,6 +73,7 @@ public class WalletScreenController implements private final SharedPreferences mPrefs; private final WalletView mWalletView; private final WalletCardCarousel mCardCarousel; + private final FalsingManager mFalsingManager; @VisibleForTesting String mSelectedCardId; @VisibleForTesting boolean mIsDismissed; @@ -85,12 +87,14 @@ public class WalletScreenController implements Executor executor, Handler handler, UserTracker userTracker, + FalsingManager falsingManager, KeyguardStateController keyguardStateController) { mContext = context; mWalletClient = walletClient; mActivityStarter = activityStarter; mExecutor = executor; mHandler = handler; + mFalsingManager = falsingManager; mKeyguardStateController = keyguardStateController; mPrefs = userTracker.getUserContext().getSharedPreferences(TAG, Context.MODE_PRIVATE); mWalletView = walletView; @@ -102,12 +106,6 @@ public class WalletScreenController implements if (mCardCarousel != null) { mCardCarousel.setSelectionListener(this); } - - if (!mPrefs.getBoolean(PREFS_HAS_CARDS, false)) { - // The empty state view is shown preemptively when cards were not returned last time - // to decrease perceived latency. - showEmptyStateView(); - } } /** @@ -136,8 +134,6 @@ public class WalletScreenController implements mWalletView.showCardCarousel( data, response.getSelectedIndex(), !mKeyguardStateController.isUnlocked()); } - // The empty state view will not be shown preemptively next time if cards were returned - mPrefs.edit().putBoolean(PREFS_HAS_CARDS, !data.isEmpty()).apply(); removeMinHeightAndRecordHeightOnLayout(); }); } @@ -167,7 +163,6 @@ public class WalletScreenController implements } switch (event.getEventType()) { case WalletServiceEvent.TYPE_NFC_PAYMENT_STARTED: - onDismissed(); break; case WalletServiceEvent.TYPE_WALLET_CARDS_UPDATED: queryWalletCards(); @@ -207,14 +202,17 @@ public class WalletScreenController implements @Override public void onCardClicked(@NonNull WalletCardViewInfo cardInfo) { + if (!mKeyguardStateController.isUnlocked() + && mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + return; + } if (!(cardInfo instanceof QAWalletCardViewInfo) || ((QAWalletCardViewInfo) cardInfo).mWalletCard == null || ((QAWalletCardViewInfo) cardInfo).mWalletCard.getPendingIntent() == null) { return; } mActivityStarter.startActivity( - ((QAWalletCardViewInfo) cardInfo).mWalletCard.getPendingIntent().getIntent(), - true); + ((QAWalletCardViewInfo) cardInfo).mWalletCard.getPendingIntent().getIntent(), true); } @Override @@ -235,7 +233,8 @@ public class WalletScreenController implements mWalletView.show(); mWalletView.hideErrorMessage(); - int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size); + int iconSizePx = + mContext.getResources().getDimensionPixelSize(R.dimen.wallet_view_header_icon_size); GetWalletCardsRequest request = new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS); mWalletClient.getWalletCards(mExecutor, request, this); @@ -248,6 +247,7 @@ public class WalletScreenController implements mIsDismissed = true; mSelectedCardId = null; mHandler.removeCallbacks(mSelectionRunnable); + mFalsingManager.cleanup(); mWalletClient.notifyWalletDismissed(); mWalletClient.removeWalletServiceEventListener(this); mWalletView.animateDismissal(); @@ -342,5 +342,10 @@ public class WalletScreenController implements public CharSequence getLabel() { return mWalletCard.getCardLabel(); } + + @Override + public PendingIntent getPendingIntent() { + return mWalletCard.getPendingIntent(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java index a3c26998e2c08..44074f706183a 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java @@ -22,13 +22,12 @@ import static com.android.systemui.wallet.ui.WalletCardCarousel.CARD_ANIM_ALPHA_ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; +import android.app.PendingIntent; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.ViewGroup; import android.view.animation.AnimationUtils; @@ -46,10 +45,10 @@ import java.util.List; /** Layout for the wallet screen. */ 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_OUT_ANIMATION_DURATION = 200; private static final int CARD_LABEL_ANIM_DELAY = 133; - private static final int CONTACTLESS_ICON_SIZE = 90; private final WalletCardCarousel mCardCarousel; private final ImageView mIcon; @@ -65,6 +64,8 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard private final TextView mErrorView; private final ViewGroup mEmptyStateView; private CharSequence mCenterCardText; + private Drawable mCenterCardIcon; + private boolean mIsDeviceLocked = false; public WalletView(Context context) { this(context, null); @@ -104,16 +105,21 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard @Override public void onCardScroll(WalletCardViewInfo centerCard, WalletCardViewInfo nextCard, float percentDistanceFromCenter) { - CharSequence centerCardText = centerCard.getLabel(); + CharSequence centerCardText = getLabelText(centerCard); + Drawable centerCardIcon = centerCard.getIcon(); if (!TextUtils.equals(mCenterCardText, centerCardText)) { mCenterCardText = centerCardText; + mCenterCardIcon = centerCardIcon; mCardLabel.setText(centerCardText); + mIcon.setImageDrawable(centerCardIcon); } - if (TextUtils.equals(centerCardText, nextCard.getLabel())) { + renderActionButton(centerCard, mIsDeviceLocked); + if (TextUtils.equals(centerCardText, getLabelText(nextCard))) { mCardLabel.setAlpha(1f); } else { mCardLabel.setAlpha(percentDistanceFromCenter); mIcon.setAlpha(percentDistanceFromCenter); + mActionButton.setAlpha(percentDistanceFromCenter); } } @@ -128,10 +134,11 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard */ void showCardCarousel( List data, int selectedIndex, boolean isDeviceLocked) { + mIsDeviceLocked = isDeviceLocked; boolean shouldAnimate = mCardCarousel.setData(data, selectedIndex); mCardCarouselContainer.setVisibility(VISIBLE); mErrorView.setVisibility(GONE); - renderHeaderIconAndActionButton(data.get(0), isDeviceLocked); + 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; @@ -239,39 +246,45 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard return mCardCarouselContainer; } + @VisibleForTesting + TextView getCardLabel() { + return mCardLabel; + } + private void renderHeaderIconAndActionButton(WalletCardViewInfo walletCard, boolean isLocked) { - Drawable icon = resizeDrawable(getResources(), walletCard.getIcon()); - renderHeaderIcon(icon, isLocked); - if (isLocked) { + mIcon.setImageDrawable(walletCard.getIcon()); + mIcon.setVisibility(VISIBLE); + renderActionButton(walletCard, isLocked); + } + + private void renderActionButton(WalletCardViewInfo walletCard, boolean isDeviceLocked) { + CharSequence actionButtonText = getActionButtonText(walletCard); + if (isDeviceLocked) { mActionButton.setVisibility(VISIBLE); mActionButton.setText(R.string.wallet_action_button_label_unlock); + } else if (actionButtonText != null) { + mActionButton.setText(actionButtonText); + mActionButton.setVisibility(VISIBLE); + mActionButton.setOnClickListener(v -> { + try { + walletCard.getPendingIntent().send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Error sending pending intent for wallet card"); + } + }); } else { mActionButton.setVisibility(GONE); } } - private void renderHeaderIcon(@Nullable Drawable icon, boolean isLocked) { - if (icon == null) { - mIcon.setVisibility(INVISIBLE); - return; - } - icon.setTint(mContext.getColor(isLocked ? R.color.GM2_grey_800 : R.color.GM2_blue_600)); - mIcon.setImageDrawable(icon); - mIcon.setVisibility(VISIBLE); - mIcon.setBackground( - mContext.getDrawable( - isLocked - ? R.drawable.circle_wallet_secondary_56dp - : R.drawable.circle_wallet_primary_56dp)); + private static CharSequence getLabelText(WalletCardViewInfo card) { + String[] rawLabel = card.getLabel().toString().split("\\n"); + return rawLabel.length == 2 ? rawLabel[0] : card.getLabel(); } @Nullable - private static Drawable resizeDrawable(Resources resources, @Nullable Drawable drawable) { - if (drawable == null) { - return null; - } - Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); - return new BitmapDrawable(resources, Bitmap.createScaledBitmap( - bitmap, CONTACTLESS_ICON_SIZE, CONTACTLESS_ICON_SIZE, true)); + private static CharSequence getActionButtonText(WalletCardViewInfo card) { + String[] rawLabel = card.getLabel().toString().split("\\n"); + return rawLabel.length == 2 ? rawLabel[1] : null; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 5cdad05e43f52..9d463d4584790 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -284,7 +284,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { assertEquals(Tile.STATE_ACTIVE, state.state); assertEquals( - mContext.getString(R.string.wallet_secondary_label_active), + "•••• 1234", state.secondaryLabel); assertNotNull(state.stateDescription); assertNotNull(state.sideViewDrawable); @@ -332,7 +332,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { assertEquals( mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height), request.getCardHeightPx()); - assertEquals(2, request.getMaxCards()); + assertEquals(1, request.getMaxCards()); assertThat(mCallbackCaptor.getValue()).isInstanceOf( QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class); } @@ -423,6 +423,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { private WalletCard createWalletCard(Context context) { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); - return new WalletCard.Builder(CARD_ID, CARD_IMAGE, "description", pendingIntent).build(); + return new WalletCard.Builder(CARD_ID, CARD_IMAGE, "•••• 1234", pendingIntent).build(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java index 6f6ef72610205..ac5da17c63585 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java @@ -20,12 +20,13 @@ import static android.view.View.GONE; import static android.view.View.VISIBLE; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.app.PendingIntent; @@ -50,6 +51,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -72,7 +74,8 @@ public class WalletScreenControllerTest extends SysuiTestCase { private static final int MAX_CARDS = 10; private static final int CARD_CAROUSEL_WIDTH = 10; - private static final String CARD_ID = "card_id"; + private static final String CARD_ID_1 = "card_id_1"; + private static final String CARD_ID_2 = "card_id_2"; private static final CharSequence SHORTCUT_SHORT_LABEL = "View all"; private static final CharSequence SHORTCUT_LONG_LABEL = "Add a payment method"; private static final CharSequence SERVICE_LABEL = "Wallet app"; @@ -89,6 +92,8 @@ public class WalletScreenControllerTest extends SysuiTestCase { @Mock UserTracker mUserTracker; @Mock + FalsingManager mFalsingManager; + @Mock KeyguardStateController mKeyguardStateController; @Captor ArgumentCaptor mIntentCaptor; @@ -122,11 +127,12 @@ public class WalletScreenControllerTest extends SysuiTestCase { MoreExecutors.directExecutor(), new Handler(mTestableLooper.getLooper()), mUserTracker, + mFalsingManager, mKeyguardStateController); } @Test - public void queryCards_hasCards_showCarousel() { + public void queryCards_hasCards_showCarousel_activeCard() { GetWalletCardsResponse response = new GetWalletCardsResponse( Collections.singletonList(createWalletCard(mContext)), 0); @@ -145,6 +151,33 @@ public class WalletScreenControllerTest extends SysuiTestCase { mTestableLooper.processAllMessages(); assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility()); + assertEquals("Hold to reader", mWalletView.getCardLabel().getText().toString()); + assertEquals(GONE, mWalletView.getErrorView().getVisibility()); + } + + @Test + public void queryCards_hasCards_showCarousel_pendingActivationCard_parseLabel() { + GetWalletCardsResponse response = + new GetWalletCardsResponse( + Collections.singletonList(createNonActiveWalletCard(mContext)), 0); + + mController.queryWalletCards(); + mTestableLooper.processAllMessages(); + + verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + + QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback = + mCallbackCaptor.getValue(); + + assertEquals(mController, callback); + + callback.onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + + assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility()); + assertEquals("Not set up", mWalletView.getCardLabel().getText().toString()); + assertEquals("Verify now", mWalletView.getActionButton().getText().toString()); + assertEquals(VISIBLE, mWalletView.getActionButton().getVisibility()); assertEquals(GONE, mWalletView.getErrorView().getVisibility()); } @@ -185,7 +218,7 @@ public class WalletScreenControllerTest extends SysuiTestCase { } @Test - public void onWalletServiceEvent_nfcPaymentStart_dismiss() { + public void onWalletServiceEvent_nfcPaymentStart_doNothing() { WalletServiceEvent event = new WalletServiceEvent(WalletServiceEvent.TYPE_NFC_PAYMENT_STARTED); @@ -193,8 +226,8 @@ public class WalletScreenControllerTest extends SysuiTestCase { mTestableLooper.processAllMessages(); assertNull(mController.mSelectedCardId); - assertTrue(mController.mIsDismissed); - verify(mWalletClient).notifyWalletDismissed(); + assertFalse(mController.mIsDismissed); + verifyZeroInteractions(mWalletClient); } @Test @@ -228,14 +261,14 @@ public class WalletScreenControllerTest extends SysuiTestCase { @Test public void onCardSelected() { - mController.onCardSelected(createCardViewInfo()); + mController.onCardSelected(createCardViewInfo(createWalletCard(mContext))); - assertEquals(CARD_ID, mController.mSelectedCardId); + assertEquals(CARD_ID_1, mController.mSelectedCardId); } @Test public void onCardClicked_startIntent() { - WalletCardViewInfo walletCardViewInfo = createCardViewInfo(); + WalletCardViewInfo walletCardViewInfo = createCardViewInfo(createWalletCard(mContext)); mController.onCardClicked(walletCardViewInfo); @@ -278,18 +311,30 @@ public class WalletScreenControllerTest extends SysuiTestCase { assertEquals(GONE, mWalletView.getVisibility()); } + private WalletCard createNonActiveWalletCard(Context context) { + PendingIntent pendingIntent = + PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); + return new WalletCard.Builder(CARD_ID_2, createIcon(), "•••• 5678", pendingIntent) + .setCardIcon(createIcon()) + .setCardLabel("Not set up\nVerify now") + .build(); + } + private WalletCard createWalletCard(Context context) { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); - return new WalletCard.Builder(CARD_ID, createIcon(), "description", pendingIntent).build(); + return new WalletCard.Builder(CARD_ID_1, createIcon(), "•••• 1234", pendingIntent) + .setCardIcon(createIcon()) + .setCardLabel("Hold to reader") + .build(); } private static Icon createIcon() { return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888)); } - private WalletCardViewInfo createCardViewInfo() { + private WalletCardViewInfo createCardViewInfo(WalletCard walletCard) { return new WalletScreenController.QAWalletCardViewInfo( - mContext, createWalletCard(mContext)); + mContext, walletCard); } }