Merge "Add card image to the QuickAccessWalletTile." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
aa3a84634d
@@ -143,6 +143,7 @@ public interface QSTile {
|
||||
public SlashState slash;
|
||||
public boolean handlesLongClick = true;
|
||||
public boolean showRippleEffect = true;
|
||||
public Drawable sideViewDrawable;
|
||||
|
||||
public boolean copyTo(State other) {
|
||||
if (other == null) throw new IllegalArgumentException();
|
||||
@@ -163,7 +164,8 @@ public interface QSTile {
|
||||
|| !Objects.equals(other.dualTarget, dualTarget)
|
||||
|| !Objects.equals(other.slash, slash)
|
||||
|| !Objects.equals(other.handlesLongClick, handlesLongClick)
|
||||
|| !Objects.equals(other.showRippleEffect, showRippleEffect);
|
||||
|| !Objects.equals(other.showRippleEffect, showRippleEffect)
|
||||
|| !Objects.equals(other.sideViewDrawable, sideViewDrawable);
|
||||
other.icon = icon;
|
||||
other.iconSupplier = iconSupplier;
|
||||
other.label = label;
|
||||
@@ -179,6 +181,7 @@ public interface QSTile {
|
||||
other.slash = slash != null ? slash.copy() : null;
|
||||
other.handlesLongClick = handlesLongClick;
|
||||
other.showRippleEffect = showRippleEffect;
|
||||
other.sideViewDrawable = sideViewDrawable;
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -204,6 +207,7 @@ public interface QSTile {
|
||||
sb.append(",isTransient=").append(isTransient);
|
||||
sb.append(",state=").append(state);
|
||||
sb.append(",slash=\"").append(slash).append("\"");
|
||||
sb.append(",sideViewDrawable").append(sideViewDrawable);
|
||||
return sb.append(']');
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
-->
|
||||
<com.android.systemui.qs.tileimpl.ButtonRelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="12dp">
|
||||
|
||||
@@ -1437,4 +1437,6 @@
|
||||
<dimen name="min_wallet_empty_height">208dp</dimen>
|
||||
<dimen name="wallet_card_border_width">1dp</dimen>
|
||||
<dimen name="wallet_empty_state_corner_radius">24dp</dimen>
|
||||
<dimen name="wallet_tile_card_view_height">32dp</dimen>
|
||||
<dimen name="wallet_tile_card_view_width">50dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -24,6 +24,8 @@ import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.RippleDrawable
|
||||
import android.service.quicksettings.Tile.STATE_ACTIVE
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RelativeLayout
|
||||
import com.android.systemui.R
|
||||
@@ -41,6 +43,7 @@ open class QSTileViewHorizontal(
|
||||
private var paintColor = Color.WHITE
|
||||
private var paintAnimator: ValueAnimator? = null
|
||||
private var labelAnimator: ValueAnimator? = null
|
||||
private var mSideView: ImageView = ImageView(mContext)
|
||||
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
|
||||
|
||||
init {
|
||||
@@ -56,6 +59,14 @@ open class QSTileViewHorizontal(
|
||||
val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
|
||||
addView(mIcon, 0, LayoutParams(iconSize, iconSize))
|
||||
|
||||
mSideView.visibility = View.GONE
|
||||
addView(
|
||||
mSideView,
|
||||
-1,
|
||||
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
})
|
||||
|
||||
mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
|
||||
changeLabelColor(getLabelColor(mState)) // Matches the default state of the tile
|
||||
}
|
||||
@@ -128,6 +139,7 @@ open class QSTileViewHorizontal(
|
||||
}
|
||||
paintColor = newColor
|
||||
}
|
||||
loadSideViewDrawableIfNecessary(state)
|
||||
}
|
||||
|
||||
private fun animateBackground(newBackgroundColor: Int) {
|
||||
@@ -180,5 +192,21 @@ open class QSTileViewHorizontal(
|
||||
labelAnimator?.cancel()?.also { labelAnimator = null }
|
||||
}
|
||||
|
||||
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
|
||||
if (state.sideViewDrawable != null) {
|
||||
(mSideView.layoutParams as MarginLayoutParams).apply {
|
||||
marginStart =
|
||||
context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
|
||||
}
|
||||
mSideView.setImageDrawable(state.sideViewDrawable)
|
||||
mSideView.visibility = View.VISIBLE
|
||||
mSideView.adjustViewBounds = true
|
||||
mSideView.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||
} else {
|
||||
mSideView.setImageDrawable(null)
|
||||
mSideView.visibility = GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleExpand(dualTarget: Boolean) {}
|
||||
}
|
||||
@@ -20,11 +20,20 @@ import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.service.quickaccesswallet.GetWalletCardsError;
|
||||
import android.service.quickaccesswallet.GetWalletCardsRequest;
|
||||
import android.service.quickaccesswallet.GetWalletCardsResponse;
|
||||
import android.service.quickaccesswallet.QuickAccessWalletClient;
|
||||
import android.service.quickaccesswallet.WalletCard;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
@@ -40,20 +49,29 @@ import com.android.systemui.statusbar.FeatureFlags;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Quick settings tile: Quick access wallet **/
|
||||
public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
|
||||
private static final String TAG = "QuickAccessWalletTile";
|
||||
private static final String FEATURE_CHROME_OS = "org.chromium.arc";
|
||||
|
||||
private final CharSequence mLabel = mContext.getString(R.string.wallet_title);
|
||||
private final WalletCardRetriever mCardRetriever = new WalletCardRetriever();
|
||||
// TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes.
|
||||
private final QuickAccessWalletClient mQuickAccessWalletClient;
|
||||
private final KeyguardStateController mKeyguardStateController;
|
||||
private final PackageManager mPackageManager;
|
||||
private final SecureSettings mSecureSettings;
|
||||
private final Executor mExecutor;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
@VisibleForTesting Drawable mCardViewDrawable;
|
||||
|
||||
@Inject
|
||||
public QuickAccessWalletTile(
|
||||
QSHost host,
|
||||
@@ -68,6 +86,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
KeyguardStateController keyguardStateController,
|
||||
PackageManager packageManager,
|
||||
SecureSettings secureSettings,
|
||||
@Background Executor executor,
|
||||
FeatureFlags featureFlags) {
|
||||
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
|
||||
statusBarStateController, activityStarter, qsLogger);
|
||||
@@ -75,6 +94,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
mKeyguardStateController = keyguardStateController;
|
||||
mPackageManager = packageManager;
|
||||
mSecureSettings = secureSettings;
|
||||
mExecutor = executor;
|
||||
mFeatureFlags = featureFlags;
|
||||
}
|
||||
|
||||
@@ -86,6 +106,14 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleSetListening(boolean listening) {
|
||||
super.handleSetListening(listening);
|
||||
if (listening) {
|
||||
queryWalletCards();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClick() {
|
||||
mActivityStarter.postStartActivityDismissingKeyguard(
|
||||
@@ -108,6 +136,7 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
} else {
|
||||
state.state = Tile.STATE_UNAVAILABLE;
|
||||
}
|
||||
state.sideViewDrawable = mCardViewDrawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,4 +162,40 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
|
||||
CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
|
||||
return qawLabel == null ? mLabel : qawLabel;
|
||||
}
|
||||
|
||||
private void queryWalletCards() {
|
||||
int cardWidth =
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width);
|
||||
int cardHeight =
|
||||
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);
|
||||
mQuickAccessWalletClient.getWalletCards(mExecutor, request, mCardRetriever);
|
||||
}
|
||||
|
||||
private class WalletCardRetriever implements
|
||||
QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
|
||||
|
||||
@Override
|
||||
public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
|
||||
Log.i(TAG, "Successfully retrieved wallet cards.");
|
||||
List<WalletCard> cards = response.getWalletCards();
|
||||
if (cards.isEmpty()) {
|
||||
Log.d(TAG, "No wallet cards exist.");
|
||||
mCardViewDrawable = null;
|
||||
refreshState();
|
||||
return;
|
||||
}
|
||||
mCardViewDrawable = cards.get(0).getCardImage().loadDrawable(mContext);
|
||||
refreshState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) {
|
||||
Log.w(TAG, "Error retrieve wallet cards");
|
||||
mCardViewDrawable = null;
|
||||
refreshState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,21 +19,36 @@ package com.android.systemui.qs.tiles;
|
||||
import static android.content.pm.PackageManager.FEATURE_NFC_HOST_CARD_EMULATION;
|
||||
import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
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;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
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;
|
||||
import android.service.quickaccesswallet.WalletCard;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
@@ -56,17 +71,30 @@ import com.android.systemui.statusbar.FeatureFlags;
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController;
|
||||
import com.android.systemui.util.settings.SecureSettings;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
|
||||
private static final String CARD_ID = "card_id";
|
||||
private static final Icon CARD_IMAGE =
|
||||
Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
|
||||
|
||||
private final Intent mWalletIntent = new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
|
||||
.setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
|
||||
|
||||
@Mock
|
||||
private QSTileHost mHost;
|
||||
@Mock
|
||||
@@ -88,6 +116,10 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
private SecureSettings mSecureSettings;
|
||||
@Mock
|
||||
private FeatureFlags mFeatureFlags;
|
||||
@Captor
|
||||
ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
|
||||
@Captor
|
||||
ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
|
||||
|
||||
private TestableLooper mTestableLooper;
|
||||
private QuickAccessWalletTile mTile;
|
||||
@@ -115,6 +147,7 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
mKeyguardStateController,
|
||||
mPackageManager,
|
||||
mSecureSettings,
|
||||
MoreExecutors.directExecutor(),
|
||||
mFeatureFlags);
|
||||
}
|
||||
|
||||
@@ -204,4 +237,112 @@ public class QuickAccessWalletTileTest extends SysuiTestCase {
|
||||
|
||||
assertEquals(Tile.STATE_UNAVAILABLE, state.state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleSetListening_queryCards() {
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
verify(mQuickAccessWalletClient)
|
||||
.getWalletCards(any(), mRequestCaptor.capture(), mCallbackCaptor.capture());
|
||||
|
||||
GetWalletCardsRequest request = mRequestCaptor.getValue();
|
||||
assertEquals(
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width),
|
||||
request.getCardWidthPx());
|
||||
assertEquals(
|
||||
mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height),
|
||||
request.getCardHeightPx());
|
||||
assertEquals(2, request.getMaxCards());
|
||||
assertThat(mCallbackCaptor.getValue()).isInstanceOf(
|
||||
QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleSetListening_queryCards_hasCards_updateSideViewDrawable() {
|
||||
GetWalletCardsResponse response =
|
||||
new GetWalletCardsResponse(
|
||||
Collections.singletonList(createWalletCard(mContext)), 0);
|
||||
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
|
||||
|
||||
mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNotNull(mTile.getState().sideViewDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testState_queryCards_hasCards_then_noCards() {
|
||||
GetWalletCardsResponse responseWithCards =
|
||||
new GetWalletCardsResponse(
|
||||
Collections.singletonList(createWalletCard(mContext)), 0);
|
||||
GetWalletCardsResponse responseWithoutCards =
|
||||
new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
|
||||
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
|
||||
|
||||
// query wallet cards, has cards
|
||||
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNotNull(mTile.getState().sideViewDrawable);
|
||||
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
verify(mQuickAccessWalletClient, times(2))
|
||||
.getWalletCards(any(), any(), mCallbackCaptor.capture());
|
||||
|
||||
// query wallet cards, has no cards
|
||||
mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mTile.getState().sideViewDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleSetListening_queryCards_noCards_notUpdateSideViewDrawable() {
|
||||
QSTile.State state = new QSTile.State();
|
||||
GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
|
||||
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
|
||||
|
||||
mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mTile.getState().sideViewDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleSetListening_queryCards_error_notUpdateSideViewDrawable() {
|
||||
String errorMessage = "getWalletCardsError";
|
||||
GetWalletCardsError error = new GetWalletCardsError(CARD_IMAGE, errorMessage);
|
||||
|
||||
mTile.handleSetListening(true);
|
||||
|
||||
verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
|
||||
|
||||
mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertNull(mTile.getState().sideViewDrawable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleSetListening_notListening_notQueryCards() {
|
||||
mTile.handleSetListening(false);
|
||||
|
||||
verifyZeroInteractions(mQuickAccessWalletClient);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user