1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.wallet.ui;
18 
19 import static android.view.View.GONE;
20 import static android.view.View.VISIBLE;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyBoolean;
26 import static org.mockito.ArgumentMatchers.anyInt;
27 import static org.mockito.Mockito.spy;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 import static org.mockito.Mockito.when;
31 
32 import android.app.PendingIntent;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.graphics.Bitmap;
37 import android.graphics.drawable.Drawable;
38 import android.graphics.drawable.Icon;
39 import android.os.Handler;
40 import android.service.quickaccesswallet.GetWalletCardsError;
41 import android.service.quickaccesswallet.GetWalletCardsResponse;
42 import android.service.quickaccesswallet.QuickAccessWalletClient;
43 import android.service.quickaccesswallet.QuickAccessWalletService;
44 import android.service.quickaccesswallet.WalletCard;
45 import android.testing.TestableLooper;
46 
47 import androidx.test.ext.junit.runners.AndroidJUnit4;
48 import androidx.test.filters.SmallTest;
49 
50 import com.android.internal.logging.UiEventLogger;
51 import com.android.keyguard.KeyguardUpdateMonitor;
52 import com.android.systemui.SysuiTestCase;
53 import com.android.systemui.plugins.ActivityStarter;
54 import com.android.systemui.plugins.FalsingManager;
55 import com.android.systemui.settings.UserTracker;
56 import com.android.systemui.statusbar.policy.KeyguardStateController;
57 
58 import com.google.common.util.concurrent.MoreExecutors;
59 
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.ArgumentCaptor;
64 import org.mockito.Captor;
65 import org.mockito.Mock;
66 import org.mockito.MockitoAnnotations;
67 
68 import java.util.Collections;
69 import java.util.List;
70 
71 @RunWith(AndroidJUnit4.class)
72 @TestableLooper.RunWithLooper
73 @SmallTest
74 public class WalletScreenControllerTest extends SysuiTestCase {
75 
76     private static final int CARD_CAROUSEL_WIDTH = 10;
77     private static final String CARD_ID_1 = "card_id_1";
78     private static final String CARD_ID_2 = "card_id_2";
79     private static final CharSequence SHORTCUT_SHORT_LABEL = "View all";
80     private static final CharSequence SHORTCUT_LONG_LABEL = "Add a payment method";
81     private static final CharSequence SERVICE_LABEL = "Wallet app";
82     private final Drawable mWalletLogo = mContext.getDrawable(android.R.drawable.ic_lock_lock);
83     private final Intent mWalletIntent = new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
84             .setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
85 
86     private WalletView mWalletView;
87 
88     @Mock
89     QuickAccessWalletClient mWalletClient;
90     @Mock
91     ActivityStarter mActivityStarter;
92     @Mock
93     UserTracker mUserTracker;
94     @Mock
95     FalsingManager mFalsingManager;
96     @Mock
97     KeyguardUpdateMonitor mKeyguardUpdateMonitor;
98     @Mock
99     KeyguardStateController mKeyguardStateController;
100     @Mock
101     UiEventLogger mUiEventLogger;
102     @Captor
103     ArgumentCaptor<PendingIntent> mIntentCaptor;
104     @Captor
105     ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
106     @Captor
107     ArgumentCaptor<List<WalletCardViewInfo>> mPaymentCardDataCaptor;
108     private WalletScreenController mController;
109     private TestableLooper mTestableLooper;
110 
111     @Before
setUp()112     public void setUp() {
113         MockitoAnnotations.initMocks(this);
114         mTestableLooper = TestableLooper.get(this);
115         when(mUserTracker.getUserContext()).thenReturn(mContext);
116         mWalletView = spy(new WalletView(mContext));
117         mWalletView.getCardCarousel().setExpectedViewWidth(CARD_CAROUSEL_WIDTH);
118         when(mWalletClient.getLogo()).thenReturn(mWalletLogo);
119         when(mWalletClient.getShortcutLongLabel()).thenReturn(SHORTCUT_LONG_LABEL);
120         when(mWalletClient.getShortcutShortLabel()).thenReturn(SHORTCUT_SHORT_LABEL);
121         when(mWalletClient.getServiceLabel()).thenReturn(SERVICE_LABEL);
122         when(mWalletClient.createWalletIntent()).thenReturn(mWalletIntent);
123         when(mKeyguardStateController.isUnlocked()).thenReturn(true);
124         when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
125         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
126         mController = new WalletScreenController(
127                 mContext,
128                 mWalletView,
129                 mWalletClient,
130                 mActivityStarter,
131                 MoreExecutors.directExecutor(),
132                 new Handler(mTestableLooper.getLooper()),
133                 mUserTracker,
134                 mFalsingManager,
135                 mKeyguardUpdateMonitor,
136                 mKeyguardStateController,
137                 mUiEventLogger);
138     }
139 
140     @Test
queryCards_deviceLocked_udfpsEnabled_hideUnlockButton()141     public void queryCards_deviceLocked_udfpsEnabled_hideUnlockButton() {
142         when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
143         when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
144         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
145         GetWalletCardsResponse response =
146                 new GetWalletCardsResponse(
147                         Collections.singletonList(createWalletCard(mContext)), 0);
148 
149         mController.queryWalletCards();
150         mTestableLooper.processAllMessages();
151 
152         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
153 
154         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
155                 mCallbackCaptor.getValue();
156 
157         assertEquals(mController, callback);
158 
159         callback.onWalletCardsRetrieved(response);
160         mTestableLooper.processAllMessages();
161 
162         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
163         assertEquals(GONE, mWalletView.getActionButton().getVisibility());
164     }
165 
166     @Test
queryCards_deviceLocked_udfpsNotEnabled_showUnlockButton()167     public void queryCards_deviceLocked_udfpsNotEnabled_showUnlockButton() {
168         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
169         GetWalletCardsResponse response =
170                 new GetWalletCardsResponse(
171                         Collections.singletonList(createLockedWalletCard(mContext)), 0);
172 
173         mController.queryWalletCards();
174         mTestableLooper.processAllMessages();
175 
176         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
177 
178         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
179                 mCallbackCaptor.getValue();
180 
181         assertEquals(mController, callback);
182 
183         callback.onWalletCardsRetrieved(response);
184         mTestableLooper.processAllMessages();
185 
186         verify(mUiEventLogger).log(WalletUiEvent.QAW_IMPRESSION);
187 
188         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
189         assertEquals(VISIBLE, mWalletView.getActionButton().getVisibility());
190     }
191 
192     @Test
queryCards_hasCards_showCarousel_activeCard()193     public void queryCards_hasCards_showCarousel_activeCard() {
194         GetWalletCardsResponse response =
195                 new GetWalletCardsResponse(
196                         Collections.singletonList(createWalletCard(mContext)), 0);
197 
198         mController.queryWalletCards();
199         mTestableLooper.processAllMessages();
200 
201         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
202 
203         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
204                 mCallbackCaptor.getValue();
205 
206         assertEquals(mController, callback);
207 
208         callback.onWalletCardsRetrieved(response);
209         mTestableLooper.processAllMessages();
210 
211         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
212         assertEquals("Hold to reader", mWalletView.getCardLabel().getText().toString());
213         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
214 
215         verify(mUiEventLogger, times(1)).log(WalletUiEvent.QAW_IMPRESSION);
216     }
217 
218     @Test
queryCards_hasCards_showCarousel_pendingActivationCard_parseLabel()219     public void queryCards_hasCards_showCarousel_pendingActivationCard_parseLabel() {
220         GetWalletCardsResponse response =
221                 new GetWalletCardsResponse(
222                         Collections.singletonList(createNonActiveWalletCard(mContext)), 0);
223 
224         mController.queryWalletCards();
225         mTestableLooper.processAllMessages();
226 
227         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
228 
229         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
230                 mCallbackCaptor.getValue();
231 
232         assertEquals(mController, callback);
233 
234         callback.onWalletCardsRetrieved(response);
235         mTestableLooper.processAllMessages();
236 
237         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
238         assertEquals("Not set up", mWalletView.getCardLabel().getText().toString());
239         assertEquals("Verify now", mWalletView.getActionButton().getText().toString());
240         assertEquals(VISIBLE, mWalletView.getActionButton().getVisibility());
241         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
242     }
243 
244     @Test
queryCards_hasCards_showCarousel_badCard_parseLabel_notCrash()245     public void queryCards_hasCards_showCarousel_badCard_parseLabel_notCrash() {
246         GetWalletCardsResponse response =
247                 new GetWalletCardsResponse(
248                         Collections.singletonList(createCrazyWalletCard(mContext, true)), 0);
249 
250         mController.queryWalletCards();
251         mTestableLooper.processAllMessages();
252 
253         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
254 
255         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
256                 mCallbackCaptor.getValue();
257 
258         assertEquals(mController, callback);
259 
260         callback.onWalletCardsRetrieved(response);
261         mTestableLooper.processAllMessages();
262 
263         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
264         assertEquals("This\nis\ncrazy!!", mWalletView.getCardLabel().getText().toString());
265         assertEquals(GONE, mWalletView.getActionButton().getVisibility());
266         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
267     }
268 
269     @Test
queryCards_hasCards_showCarousel_badCard_noLabel_notCrash()270     public void queryCards_hasCards_showCarousel_badCard_noLabel_notCrash() {
271         GetWalletCardsResponse response =
272                 new GetWalletCardsResponse(
273                         Collections.singletonList(createCrazyWalletCard(mContext, false)), 0);
274 
275         mController.queryWalletCards();
276         mTestableLooper.processAllMessages();
277 
278         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
279 
280         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
281                 mCallbackCaptor.getValue();
282 
283         assertEquals(mController, callback);
284 
285         callback.onWalletCardsRetrieved(response);
286         mTestableLooper.processAllMessages();
287 
288         assertEquals(VISIBLE, mWalletView.getCardCarouselContainer().getVisibility());
289         assertEquals("", mWalletView.getCardLabel().getText().toString());
290         assertEquals(GONE, mWalletView.getActionButton().getVisibility());
291         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
292     }
293 
294     @Test
queryCards_hasCards_showCarousel_invalidSelectedIndex_notCrash()295     public void queryCards_hasCards_showCarousel_invalidSelectedIndex_notCrash() {
296         GetWalletCardsResponse response =
297                 new GetWalletCardsResponse(
298                         Collections.singletonList(createCrazyWalletCard(mContext, true)), 8);
299 
300         mController.queryWalletCards();
301         mTestableLooper.processAllMessages();
302 
303         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
304 
305         QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback =
306                 mCallbackCaptor.getValue();
307 
308         assertEquals(mController, callback);
309 
310         callback.onWalletCardsRetrieved(response);
311         mTestableLooper.processAllMessages();
312 
313         assertEquals(GONE, mWalletView.getCardCarousel().getVisibility());
314         assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
315         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
316     }
317 
318     @Test
queryCards_noCards_showEmptyState()319     public void queryCards_noCards_showEmptyState() {
320         GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
321 
322         mController.queryWalletCards();
323         mTestableLooper.processAllMessages();
324 
325         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
326 
327         mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
328         mTestableLooper.processAllMessages();
329 
330         assertEquals(GONE, mWalletView.getCardCarousel().getVisibility());
331         assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
332         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
333         assertTrue(mWalletView.getAppButton().hasOnClickListeners());
334     }
335 
336     @Test
queryCards_error_showErrorView()337     public void queryCards_error_showErrorView() {
338         String errorMessage = "getWalletCardsError";
339         GetWalletCardsError error = new GetWalletCardsError(createIcon(), errorMessage);
340 
341         mController.queryWalletCards();
342         mTestableLooper.processAllMessages();
343 
344         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
345 
346         mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
347         mTestableLooper.processAllMessages();
348 
349         assertEquals(GONE, mWalletView.getCardCarouselContainer().getVisibility());
350         assertEquals(GONE, mWalletView.getEmptyStateView().getVisibility());
351         assertEquals(VISIBLE, mWalletView.getErrorView().getVisibility());
352         assertEquals(errorMessage, mWalletView.getErrorView().getText().toString());
353     }
354 
355     @Test
onKeyguardFadingAwayChanged_queryCards()356     public void onKeyguardFadingAwayChanged_queryCards() {
357         mController.onKeyguardFadingAwayChanged();
358 
359         verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
360         assertEquals(mController, mCallbackCaptor.getValue());
361     }
362 
363     @Test
onCardSelected()364     public void onCardSelected() {
365         mController.onCardSelected(createCardViewInfo(createWalletCard(mContext)));
366 
367         assertEquals(CARD_ID_1, mController.mSelectedCardId);
368     }
369 
370     @Test
logOnCardChanged()371     public void logOnCardChanged() {
372         mController.onCardSelected(createCardViewInfo(createWalletCard(mContext)));
373         mController.onCardSelected(createCardViewInfo(createNonActiveWalletCard(mContext)));
374 
375         verify(mUiEventLogger, times(1)).log(WalletUiEvent.QAW_CHANGE_CARD);
376     }
377 
378     @Test
onCardClicked_startIntent()379     public void onCardClicked_startIntent() {
380         WalletCardViewInfo walletCardViewInfo = createCardViewInfo(createWalletCard(mContext));
381 
382         mController.onCardClicked(walletCardViewInfo);
383 
384         verify(mActivityStarter).startPendingIntentDismissingKeyguard(mIntentCaptor.capture());
385 
386         Intent actualIntent = mIntentCaptor.getValue().getIntent();
387 
388         assertEquals(mWalletIntent.getAction(), actualIntent.getAction());
389         assertEquals(mWalletIntent.getComponent(), actualIntent.getComponent());
390 
391         verify(mUiEventLogger, times(1)).log(WalletUiEvent.QAW_CLICK_CARD);
392     }
393 
394     @Test
onCardClicked_deviceLocked_logUnlockEvent()395     public void onCardClicked_deviceLocked_logUnlockEvent() {
396         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
397         WalletCardViewInfo walletCardViewInfo = createCardViewInfo(createWalletCard(mContext));
398 
399         mController.onCardClicked(walletCardViewInfo);
400 
401         verify(mUiEventLogger, times(1))
402                 .log(WalletUiEvent.QAW_UNLOCK_FROM_CARD_CLICK);
403         verify(mUiEventLogger, times(1)).log(WalletUiEvent.QAW_CLICK_CARD);
404     }
405 
406     @Test
onWalletCardsRetrieved_cardDataEmpty_intentIsNull_hidesWallet()407     public void onWalletCardsRetrieved_cardDataEmpty_intentIsNull_hidesWallet() {
408         when(mWalletClient.createWalletIntent()).thenReturn(null);
409         GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.emptyList(), 0);
410 
411         mController.onWalletCardsRetrieved(response);
412         mTestableLooper.processAllMessages();
413 
414         assertEquals(GONE, mWalletView.getVisibility());
415     }
416 
417     @Test
onWalletCardsRetrieved_cardDataEmpty_logoIsNull_hidesWallet()418     public void onWalletCardsRetrieved_cardDataEmpty_logoIsNull_hidesWallet() {
419         when(mWalletClient.getLogo()).thenReturn(null);
420         GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.emptyList(), 0);
421 
422         mController.onWalletCardsRetrieved(response);
423         mTestableLooper.processAllMessages();
424 
425         assertEquals(GONE, mWalletView.getVisibility());
426     }
427 
428     @Test
onWalletCardsRetrieved_cardDataEmpty_labelIsEmpty_hidesWallet()429     public void onWalletCardsRetrieved_cardDataEmpty_labelIsEmpty_hidesWallet() {
430         when(mWalletClient.getShortcutLongLabel()).thenReturn("");
431         GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.emptyList(), 0);
432 
433         mController.onWalletCardsRetrieved(response);
434         mTestableLooper.processAllMessages();
435 
436         assertEquals(GONE, mWalletView.getVisibility());
437     }
438 
439     @Test
onWalletCardsRetrieved_cardDataAllUnknown_showsAllCards()440     public void onWalletCardsRetrieved_cardDataAllUnknown_showsAllCards() {
441         List<WalletCard> walletCardList = List.of(
442                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_UNKNOWN),
443                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_UNKNOWN),
444                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_UNKNOWN));
445         GetWalletCardsResponse response = new GetWalletCardsResponse(walletCardList, 0);
446         mController.onWalletCardsRetrieved(response);
447         mTestableLooper.processAllMessages();
448 
449         verify(mWalletView).showCardCarousel(mPaymentCardDataCaptor.capture(), anyInt(),
450                 anyBoolean(),
451                 anyBoolean());
452         List<WalletCardViewInfo> paymentCardData = mPaymentCardDataCaptor.getValue();
453         assertEquals(paymentCardData.size(), walletCardList.size());
454     }
455 
456     @Test
onWalletCardsRetrieved_cardDataDifferentTypes_onlyShowsPayment()457     public void onWalletCardsRetrieved_cardDataDifferentTypes_onlyShowsPayment() {
458         List<WalletCard> walletCardList = List.of(createWalletCardWithType(mContext,
459                         WalletCard.CARD_TYPE_UNKNOWN),
460                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_PAYMENT),
461                 createWalletCardWithType(mContext, WalletCard.CARD_TYPE_NON_PAYMENT)
462         );
463         GetWalletCardsResponse response = new GetWalletCardsResponse(walletCardList, 0);
464         mController.onWalletCardsRetrieved(response);
465         mTestableLooper.processAllMessages();
466 
467         verify(mWalletView).showCardCarousel(mPaymentCardDataCaptor.capture(), anyInt(),
468                 anyBoolean(),
469                 anyBoolean());
470         List<WalletCardViewInfo> paymentCardData = mPaymentCardDataCaptor.getValue();
471         assertEquals(paymentCardData.size(), 1);
472     }
473 
createNonActiveWalletCard(Context context)474     private WalletCard createNonActiveWalletCard(Context context) {
475         PendingIntent pendingIntent =
476                 PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
477         return new WalletCard.Builder(CARD_ID_2, createIcon(), "•••• 5678", pendingIntent)
478                 .setCardIcon(createIcon())
479                 .setCardLabel("Not set up\nVerify now")
480                 .build();
481     }
482 
createLockedWalletCard(Context context)483     private WalletCard createLockedWalletCard(Context context) {
484         PendingIntent pendingIntent =
485                 PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
486         return new WalletCard.Builder(CARD_ID_2, createIcon(), "•••• 5679", pendingIntent)
487                 .setCardIcon(createIcon())
488                 .setCardLabel("Locked\nUnlock to pay")
489                 .build();
490     }
491 
createWalletCard(Context context)492     private WalletCard createWalletCard(Context context) {
493         PendingIntent pendingIntent =
494                 PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
495         return new WalletCard.Builder(CARD_ID_1, createIcon(), "•••• 1234", pendingIntent)
496                 .setCardIcon(createIcon())
497                 .setCardLabel("Hold to reader")
498                 .build();
499     }
500 
createWalletCardWithType(Context context, int cardType)501     private WalletCard createWalletCardWithType(Context context, int cardType) {
502         PendingIntent pendingIntent =
503                 PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
504         return new WalletCard.Builder(CARD_ID_1, cardType, createIcon(), "•••• 1234", pendingIntent)
505                 .setCardIcon(createIcon())
506                 .setCardLabel("Hold to reader")
507                 .build();
508     }
509 
createCrazyWalletCard(Context context, boolean hasLabel)510     private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) {
511         PendingIntent pendingIntent =
512                 PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
513         return new WalletCard.Builder("BadCard", createIcon(), "•••• 1234", pendingIntent)
514                 .setCardIcon(null)
515                 .setCardLabel(hasLabel ? "This\nis\ncrazy!!" : null)
516                 .build();
517     }
518 
createIcon()519     private static Icon createIcon() {
520         return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888));
521     }
522 
createCardViewInfo(WalletCard walletCard)523     private WalletCardViewInfo createCardViewInfo(WalletCard walletCard) {
524         return new WalletScreenController.QAWalletCardViewInfo(
525                 mContext, walletCard);
526     }
527 }
528