Polish Wallet empty state view.

Another change is a bug fix that do not use the cached "mWalletEnabled"
on cards querying, because that param is for the wallet preference only.
We use "cardsRetriever"s to handle querying cards on wallet
feature/service not available.
Also register the wallet service event listener to WalletActivity, this
enables the wallet activity finish after the user makes an NFC payment
tap.

Fixes: 189869634
Fixes: 187970986
Fixes: 188972317
Test: manual- see demo:
https: //drive.google.com/file/d/1I-vwffhRqR2HDHR8wQqitOqzH0tWjMEI/view?usp=sharing&resourcekey=0-3BZsJl_c7jzW7muwNG9hmA
Change-Id: If907cb5d6c89878d2a5a6a282284e3bf77f7188f
This commit is contained in:
Silin Huang
2021-06-02 08:58:58 -07:00
parent e0fa7da5aa
commit cb639a9e4b
11 changed files with 51 additions and 111 deletions

View File

@@ -22,7 +22,7 @@
android:id="@+id/wallet_empty_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="16dp"
android:layout_marginHorizontal="48dp"
android:layout_marginTop="48dp"
android:background="@drawable/wallet_empty_state_bg"
android:orientation="vertical"

View File

@@ -54,9 +54,12 @@
android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Wallet.TextAppearance"
android:layout_marginHorizontal="48dp"
android:textColor="?androidprv:attr/textColorPrimary"
android:textAlignment="center"/>
<include layout="@layout/wallet_empty_state"/>
<com.android.systemui.wallet.ui.WalletCardCarousel
android:id="@+id/card_carousel"
android:layout_width="match_parent"
@@ -97,8 +100,6 @@
android:layout_marginVertical="24dp"/>
</LinearLayout>
<include layout="@layout/wallet_empty_state"/>
<TextView
android:id="@+id/error_view"
android:layout_width="match_parent"

View File

@@ -1653,6 +1653,8 @@
<!-- Wallet strings -->
<!-- Wallet empty state, title [CHAR LIMIT=32] -->
<string name="wallet_title">Wallet</string>
<!-- Wallet empty state label. [CHAR LIMIT=NONE] -->
<string name="wallet_empty_state_label">Get set up to make faster, more secure purchases with your phone</string>
<!-- Label of the button at the bottom prompting user enter wallet app. [CHAR LIMIT=NONE] -->
<string name="wallet_app_button_label">Show all</string>
<!-- Label of the button underneath the card carousel prompting user unlock device. [CHAR LIMIT=NONE] -->

View File

@@ -56,7 +56,6 @@ import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.systemui.wallet.ui.WalletActivity;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -71,7 +70,6 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
private final KeyguardStateController mKeyguardStateController;
private final PackageManager mPackageManager;
private final SecureSettings mSecureSettings;
private final Executor mExecutor;
private final QuickAccessWalletController mController;
private final FeatureFlags mFeatureFlags;
@@ -91,7 +89,6 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
KeyguardStateController keyguardStateController,
PackageManager packageManager,
SecureSettings secureSettings,
@Main Executor executor,
QuickAccessWalletController quickAccessWalletController,
FeatureFlags featureFlags) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
@@ -100,7 +97,6 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
mKeyguardStateController = keyguardStateController;
mPackageManager = packageManager;
mSecureSettings = secureSettings;
mExecutor = executor;
mFeatureFlags = featureFlags;
}
@@ -118,6 +114,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
if (listening) {
mController.setupWalletChangeObservers(mCardRetriever, DEFAULT_PAYMENT_APP_CHANGE);
if (!mController.getWalletClient().isWalletServiceAvailable()) {
Log.i(TAG, "QAW service is unavailable, recreating the wallet client.");
mController.reCreateWalletClient();
}
mController.queryWalletCards(mCardRetriever);

View File

@@ -25,7 +25,6 @@ import android.provider.Settings;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -143,10 +142,6 @@ public class QuickAccessWalletController {
*/
public void queryWalletCards(
QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
if (!mWalletEnabled) {
Log.w(TAG, "QuickAccessWallet is unavailable, unable to query cards.");
return;
}
int cardWidth =
mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width);
int cardHeight =

View File

@@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.WalletServiceEvent;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -51,7 +52,8 @@ import javax.inject.Inject;
/**
* Displays Wallet carousel screen inside an activity.
*/
public class WalletActivity extends LifecycleActivity {
public class WalletActivity extends LifecycleActivity implements
QuickAccessWalletClient.WalletServiceEventListener {
private static final String TAG = "WalletActivity";
private final KeyguardStateController mKeyguardStateController;
@@ -63,6 +65,8 @@ public class WalletActivity extends LifecycleActivity {
private final UserTracker mUserTracker;
private final StatusBarKeyguardViewManager mKeyguardViewManager;
private WalletScreenController mWalletScreenController;
private QuickAccessWalletClient mWalletClient;
private boolean mHasRegisteredListener;
@Inject
public WalletActivity(
@@ -102,11 +106,11 @@ public class WalletActivity extends LifecycleActivity {
getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
WalletView walletView = requireViewById(R.id.wallet_view);
QuickAccessWalletClient walletClient = QuickAccessWalletClient.create(this);
mWalletClient = QuickAccessWalletClient.create(this);
mWalletScreenController = new WalletScreenController(
this,
walletView,
walletClient,
mWalletClient,
mActivityStarter,
mExecutor,
mHandler,
@@ -116,7 +120,7 @@ public class WalletActivity extends LifecycleActivity {
walletView.getAppButton().setOnClickListener(
v -> {
if (walletClient.createWalletIntent() == null) {
if (mWalletClient.createWalletIntent() == null) {
Log.w(TAG, "Unable to create wallet app intent.");
return;
}
@@ -127,12 +131,12 @@ public class WalletActivity extends LifecycleActivity {
if (mKeyguardStateController.isUnlocked()) {
mActivityStarter.startActivity(
walletClient.createWalletIntent(), true);
mWalletClient.createWalletIntent(), true);
finish();
} else {
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
mActivityStarter.startActivity(
walletClient.createWalletIntent(), true);
mWalletClient.createWalletIntent(), true);
finish();
return false;
}, false, true);
@@ -154,6 +158,11 @@ public class WalletActivity extends LifecycleActivity {
@Override
protected void onStart() {
super.onStart();
if (!mHasRegisteredListener) {
// Listener is registered even when device is locked. Should only be registered once.
mWalletClient.addWalletServiceEventListener(this);
mHasRegisteredListener = true;
}
mKeyguardStateController.addCallback(mWalletScreenController);
}
@@ -178,6 +187,24 @@ public class WalletActivity extends LifecycleActivity {
return super.onCreateOptionsMenu(menu);
}
/**
* Implements {@link QuickAccessWalletClient.WalletServiceEventListener}. Called when the wallet
* application propagates an event, such as an NFC tap, to the quick access wallet view.
*/
@Override
public void onWalletServiceEvent(WalletServiceEvent event) {
switch (event.getEventType()) {
case WalletServiceEvent.TYPE_NFC_PAYMENT_STARTED:
finish();
break;
case WalletServiceEvent.TYPE_WALLET_CARDS_UPDATED:
mWalletScreenController.queryWalletCards();
break;
default:
Log.w(TAG, "onWalletServiceEvent: Unknown event type");
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
@@ -198,6 +225,8 @@ public class WalletActivity extends LifecycleActivity {
protected void onDestroy() {
mKeyguardStateController.removeCallback(mWalletScreenController);
mWalletScreenController.onDismissed();
mWalletClient.removeWalletServiceEventListener(this);
mHasRegisteredListener = false;
super.onDestroy();
}

View File

@@ -30,7 +30,6 @@ import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.SelectWalletCardRequest;
import android.service.quickaccesswallet.WalletCard;
import android.service.quickaccesswallet.WalletServiceEvent;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -55,7 +54,6 @@ import java.util.concurrent.TimeUnit;
public class WalletScreenController implements
WalletCardCarousel.OnSelectionListener,
QuickAccessWalletClient.OnWalletCardsRetrievedCallback,
QuickAccessWalletClient.WalletServiceEventListener,
KeyguardStateController.Callback {
private static final String TAG = "WalletScreenCtrl";
@@ -77,7 +75,6 @@ public class WalletScreenController implements
@VisibleForTesting String mSelectedCardId;
@VisibleForTesting boolean mIsDismissed;
private boolean mHasRegisteredListener;
public WalletScreenController(
Context context,
@@ -117,6 +114,7 @@ public class WalletScreenController implements
if (mIsDismissed) {
return;
}
Log.i(TAG, "Successfully retrieved wallet cards.");
List<WalletCard> walletCards = response.getWalletCards();
List<WalletCardViewInfo> data = new ArrayList<>(walletCards.size());
for (WalletCard card : walletCards) {
@@ -158,26 +156,6 @@ public class WalletScreenController implements
});
}
/**
* Implements {@link QuickAccessWalletClient.WalletServiceEventListener}. Called when the wallet
* application propagates an event, such as an NFC tap, to the quick access wallet view.
*/
@Override
public void onWalletServiceEvent(WalletServiceEvent event) {
if (mIsDismissed) {
return;
}
switch (event.getEventType()) {
case WalletServiceEvent.TYPE_NFC_PAYMENT_STARTED:
break;
case WalletServiceEvent.TYPE_WALLET_CARDS_UPDATED:
queryWalletCards();
break;
default:
Log.w(TAG, "onWalletServiceEvent: Unknown event type");
}
}
@Override
public void onKeyguardFadingAwayChanged() {
queryWalletCards();
@@ -236,11 +214,6 @@ public class WalletScreenController implements
if (cardWidthPx == 0 || cardHeightPx == 0) {
return;
}
if (!mHasRegisteredListener) {
// Listener is registered even when device is locked. Should only be registered once.
mWalletClient.addWalletServiceEventListener(this);
mHasRegisteredListener = true;
}
mWalletView.show();
mWalletView.hideErrorMessage();
@@ -261,7 +234,6 @@ public class WalletScreenController implements
mSelectedCardId = null;
mHandler.removeCallbacks(mSelectionRunnable);
mWalletClient.notifyWalletDismissed();
mWalletClient.removeWalletServiceEventListener(this);
mWalletView.animateDismissal();
// clear refs to the Wallet Activity
mContext = null;
@@ -282,7 +254,6 @@ public class WalletScreenController implements
mWalletView.hide();
mPrefs.edit().putInt(PREFS_WALLET_VIEW_HEIGHT, 0).apply();
} else {
logo.setTint(mContext.getColor(R.color.GM2_grey_900));
mWalletView.showEmptyStateView(
logo,
logoContentDesc,

View File

@@ -161,10 +161,12 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard
OnClickListener clickListener) {
mEmptyStateView.setVisibility(VISIBLE);
mErrorView.setVisibility(GONE);
mCardCarouselContainer.setVisibility(GONE);
mCardCarousel.setVisibility(GONE);
mIcon.setImageDrawable(logo);
mIcon.setContentDescription(logoContentDescription);
mCardLabel.setText(R.string.wallet_empty_state_label);
ImageView logoView = mEmptyStateView.requireViewById(R.id.empty_state_icon);
logoView.setImageDrawable(logo);
logoView.setContentDescription(logoContentDescription);
logoView.setImageDrawable(mContext.getDrawable(R.drawable.ic_qs_plus));
mEmptyStateView.<TextView>requireViewById(R.id.empty_state_title).setText(label);
mEmptyStateView.setOnClickListener(clickListener);
}

View File

@@ -46,7 +46,6 @@ import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Handler;
import android.service.quickaccesswallet.GetWalletCardsError;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.QuickAccessWalletService;
@@ -75,8 +74,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -126,8 +123,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
@Captor
ArgumentCaptor<Intent> mIntentCaptor;
@Captor
ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
@Captor
ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
private Context mSpiedContext;
@@ -163,7 +158,6 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mKeyguardStateController,
mPackageManager,
mSecureSettings,
MoreExecutors.directExecutor(),
mController,
mFeatureFlags);
}

View File

@@ -21,9 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -126,18 +124,8 @@ public class QuickAccessWalletControllerTest extends SysuiTestCase {
assertNotSame(mQuickAccessWalletClient, mController.getWalletClient());
}
@Test
public void queryWalletCards_walletNotEnabled_notQuery() {
when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
mController.queryWalletCards(mCardsRetriever);
verify(mQuickAccessWalletClient, never()).getWalletCards(any(), any(), any());
}
@Test
public void queryWalletCards_walletEnabled_queryCards() {
mController.updateWalletPreference();
mController.queryWalletCards(mCardsRetriever);
verify(mQuickAccessWalletClient)

View File

@@ -20,13 +20,9 @@ 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.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;
@@ -43,7 +39,6 @@ import android.service.quickaccesswallet.GetWalletCardsResponse;
import android.service.quickaccesswallet.QuickAccessWalletClient;
import android.service.quickaccesswallet.QuickAccessWalletService;
import android.service.quickaccesswallet.WalletCard;
import android.service.quickaccesswallet.WalletServiceEvent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -250,7 +245,7 @@ public class WalletScreenControllerTest extends SysuiTestCase {
callback.onWalletCardsRetrieved(response);
mTestableLooper.processAllMessages();
assertEquals(GONE, mWalletView.getCardCarouselContainer().getVisibility());
assertEquals(GONE, mWalletView.getCardCarousel().getVisibility());
assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
assertEquals(GONE, mWalletView.getErrorView().getVisibility());
}
@@ -267,7 +262,7 @@ public class WalletScreenControllerTest extends SysuiTestCase {
mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
mTestableLooper.processAllMessages();
assertEquals(GONE, mWalletView.getCardCarouselContainer().getVisibility());
assertEquals(GONE, mWalletView.getCardCarousel().getVisibility());
assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
assertEquals(GONE, mWalletView.getErrorView().getVisibility());
}
@@ -291,40 +286,6 @@ public class WalletScreenControllerTest extends SysuiTestCase {
assertEquals(errorMessage, mWalletView.getErrorView().getText().toString());
}
@Test
public void onWalletServiceEvent_nfcPaymentStart_doNothing() {
WalletServiceEvent event =
new WalletServiceEvent(WalletServiceEvent.TYPE_NFC_PAYMENT_STARTED);
mController.onWalletServiceEvent(event);
mTestableLooper.processAllMessages();
assertNull(mController.mSelectedCardId);
assertFalse(mController.mIsDismissed);
verifyZeroInteractions(mWalletClient);
}
@Test
public void onWalletServiceEvent_walletCardsUpdate_queryCards() {
mController.queryWalletCards();
verify(mWalletClient).addWalletServiceEventListener(mListenerCaptor.capture());
WalletServiceEvent event =
new WalletServiceEvent(WalletServiceEvent.TYPE_WALLET_CARDS_UPDATED);
QuickAccessWalletClient.WalletServiceEventListener listener = mListenerCaptor.getValue();
listener.onWalletServiceEvent(event);
mTestableLooper.processAllMessages();
verify(mWalletClient, times(2))
.getWalletCards(any(), mRequestCaptor.capture(), mCallbackCaptor.capture());
GetWalletCardsRequest request = mRequestCaptor.getValue();
assertEquals(MAX_CARDS, request.getMaxCards());
}
@Test
public void onKeyguardFadingAwayChanged_queryCards() {
mController.onKeyguardFadingAwayChanged();