Make wallet screen scrollable in landscape mode.

Also added test cases for cards with bad informations to prevent crash;
change the wallet tile label to use the one fetched from QAW API, with a
fallback static string in SysUI.
see demo:
https://drive.google.com/file/d/1Pu39KJTbQNJ2ABV__fVfdnNu9V_XlOsn/view?usp=sharing&resourcekey=0-WJgX6aUZTy9j3QWHfz3mjg

Test: atest
Test: manual
Bug: 187970998
Change-Id: I5c073989a9db959eba324b693c04e46a0b96d6b2
This commit is contained in:
Silin Huang
2021-05-13 02:07:53 -07:00
parent b941e181a3
commit 3ca59dc0e9
5 changed files with 158 additions and 51 deletions

View File

@@ -22,60 +22,67 @@
android:layout_height="match_parent"
android:clipChildren="false">
<Toolbar
android:id="@+id/action_bar"
style="?android:attr/actionBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:navigationContentDescription="@null" />
android:id="@+id/action_bar"
style="?android:attr/actionBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:navigationContentDescription="@null" />
<LinearLayout
android:id="@+id/card_carousel_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="48dp"
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/wallet_screen_header_view_size"
android:layout_height="@dimen/wallet_screen_header_view_size"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="10dp"
android:scaleType="center"
android:contentDescription="@null"/>
<TextView
android:id="@+id/label"
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Wallet.TextAppearance"
android:textAlignment="center"/>
<com.android.systemui.wallet.ui.WalletCardCarousel
android:id="@+id/card_carousel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:transitionName="dotIndicator"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_marginVertical="24dp"/>
<Button
android:id="@+id/wallet_action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="16dp"
android:paddingVertical="@dimen/wallet_button_vertical_padding"
android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
android:background="@drawable/wallet_action_button_bg"
android:textColor="?androidprv:attr/textColorPrimaryInverse"
android:textAlignment="center"
android:visibility="gone"/>
android:layout_height="0dp"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/wallet_screen_header_view_size"
android:layout_height="@dimen/wallet_screen_header_view_size"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="10dp"
android:scaleType="center"
android:contentDescription="@null"/>
<TextView
android:id="@+id/label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Wallet.TextAppearance"
android:textAlignment="center"/>
<com.android.systemui.wallet.ui.WalletCardCarousel
android:id="@+id/card_carousel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:transitionName="dotIndicator"
android:clipChildren="false"
android:clipToPadding="false"
android:layout_marginVertical="24dp"/>
<Button
android:id="@+id/wallet_action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="16dp"
android:paddingVertical="@dimen/wallet_button_vertical_padding"
android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
android:background="@drawable/wallet_action_button_bg"
android:textColor="?androidprv:attr/textColorPrimaryInverse"
android:textAlignment="center"
android:visibility="gone"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
android:layout_weight="0.1"/>
<Button
android:id="@+id/wallet_app_button"
android:layout_width="wrap_content"
@@ -88,7 +95,6 @@
android:textColor="?androidprv:attr/colorAccentPrimary"
android:textAlignment="center"
android:layout_marginVertical="24dp"/>
</LinearLayout>
<include layout="@layout/wallet_empty_state"/>

View File

@@ -152,7 +152,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
@Override
protected void handleUpdateState(State state, Object arg) {
state.label = mLabel;
CharSequence label = mQuickAccessWalletClient.getServiceLabel();
state.label = label == null ? mLabel : label;
state.contentDescription = state.label;
state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
@@ -197,7 +198,8 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
@Override
public CharSequence getTileLabel() {
return mLabel;
CharSequence label = mQuickAccessWalletClient.getServiceLabel();
return label == null ? mLabel : label;
}
private void queryWalletCards() {
@@ -227,7 +229,9 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
}
int selectedIndex = response.getSelectedIndex();
if (selectedIndex >= cards.size()) {
Log.d(TAG, "Selected card index out of bounds.");
Log.w(TAG, "Error retrieving cards: Invalid selected card index.");
mSelectedCard = null;
mCardViewDrawable = null;
return;
}
mSelectedCard = cards.get(selectedIndex);

View File

@@ -131,8 +131,14 @@ public class WalletScreenController implements
if (data.isEmpty()) {
showEmptyStateView();
} else {
mWalletView.showCardCarousel(
data, response.getSelectedIndex(), !mKeyguardStateController.isUnlocked());
int selectedIndex = response.getSelectedIndex();
if (selectedIndex >= data.size()) {
Log.w(TAG, "Invalid selected card index, showing empty state.");
showEmptyStateView();
} else {
mWalletView.showCardCarousel(
data, selectedIndex, !mKeyguardStateController.isUnlocked());
}
}
removeMinHeightAndRecordHeightOnLayout();
});

View File

@@ -92,6 +92,7 @@ import java.util.Collections;
public class QuickAccessWalletTileTest extends SysuiTestCase {
private static final String CARD_ID = "card_id";
private static final String LABEL = "QAW";
private static final Icon CARD_IMAGE =
Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
@@ -141,6 +142,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
when(mHost.getContext()).thenReturn(mSpiedContext);
when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true);
when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL);
when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
@@ -248,13 +250,19 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
mTile.handleUpdateState(state, null);
assertEquals(mContext.getString(R.string.wallet_title), state.label.toString());
assertEquals(LABEL, state.label.toString());
assertTrue(state.label.toString().contentEquals(state.contentDescription));
assertEquals(icon, state.icon);
}
@Test
public void testGetTileLabel() {
public void testGetTileLabel_serviceLabelExists() {
assertEquals(LABEL, mTile.getTileLabel().toString());
}
@Test
public void testGetTileLabel_serviceLabelDoesNotExist() {
when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(null);
assertEquals(mContext.getString(R.string.wallet_title), mTile.getTileLabel().toString());
}

View File

@@ -181,6 +181,80 @@ public class WalletScreenControllerTest extends SysuiTestCase {
assertEquals(GONE, mWalletView.getErrorView().getVisibility());
}
@Test
public void queryCards_hasCards_showCarousel_badCard_parseLabel_notCrash() {
GetWalletCardsResponse response =
new GetWalletCardsResponse(
Collections.singletonList(createCrazyWalletCard(mContext, true)), 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("This\nis\ncrazy!!", mWalletView.getCardLabel().getText().toString());
assertEquals(GONE, mWalletView.getActionButton().getVisibility());
assertEquals(GONE, mWalletView.getErrorView().getVisibility());
}
@Test
public void queryCards_hasCards_showCarousel_badCard_noLabel_notCrash() {
GetWalletCardsResponse response =
new GetWalletCardsResponse(
Collections.singletonList(createCrazyWalletCard(mContext, false)), 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("", mWalletView.getCardLabel().getText().toString());
assertEquals(GONE, mWalletView.getActionButton().getVisibility());
assertEquals(GONE, mWalletView.getErrorView().getVisibility());
}
@Test
public void queryCards_hasCards_showCarousel_invalidSelectedIndex_notCrash() {
GetWalletCardsResponse response =
new GetWalletCardsResponse(
Collections.singletonList(createCrazyWalletCard(mContext, true)), 8);
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(GONE, mWalletView.getCardCarouselContainer().getVisibility());
assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
assertEquals(GONE, mWalletView.getErrorView().getVisibility());
}
@Test
public void queryCards_noCards_showEmptyState() {
GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
@@ -329,6 +403,15 @@ public class WalletScreenControllerTest extends SysuiTestCase {
.build();
}
private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) {
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
return new WalletCard.Builder("BadCard", createIcon(), "•••• 1234", pendingIntent)
.setCardIcon(null)
.setCardLabel(hasLabel ? "This\nis\ncrazy!!" : null)
.build();
}
private static Icon createIcon() {
return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888));
}