From e8d0abe6e061808c276dcad3b002d81a2b67150f Mon Sep 17 00:00:00 2001 From: John Johnson Date: Wed, 1 Mar 2023 15:40:17 -0500 Subject: Add list of locations to WalletCard Bug: b/247587924 Test: atest CtsQuickAccessWalletTestCases Change-Id: I03aab2872468905322b5a2754fee9ad8d7c2647c --- core/api/current.txt | 3 + core/java/android/content/pm/PackageManager.java | 17 ++++-- .../GetWalletCardsCallbackImpl.java | 27 ++++++-- .../QuickAccessWalletService.java | 4 +- .../service/quickaccesswallet/WalletCard.java | 71 +++++++++++++++++----- 5 files changed, 95 insertions(+), 27 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 2db3eae5fab6..2a16e12b23cb 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12725,6 +12725,7 @@ package android.content.pm { field public static final String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute"; field public static final String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level"; field public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version"; + field public static final String FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS = "android.software.wallet_location_based_suggestions"; field public static final String FEATURE_WATCH = "android.hardware.type.watch"; field public static final String FEATURE_WEBVIEW = "android.software.webview"; field public static final String FEATURE_WIFI = "android.hardware.wifi"; @@ -41086,6 +41087,7 @@ package android.service.quickaccesswallet { method @NonNull public String getCardId(); method @NonNull public android.graphics.drawable.Icon getCardImage(); method @Nullable public CharSequence getCardLabel(); + method @NonNull public java.util.List getCardLocations(); method @NonNull public int getCardType(); method @NonNull public CharSequence getContentDescription(); method @Nullable public android.graphics.drawable.Icon getNonPaymentCardSecondaryImage(); @@ -41103,6 +41105,7 @@ package android.service.quickaccesswallet { method @NonNull public android.service.quickaccesswallet.WalletCard build(); method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardIcon(@Nullable android.graphics.drawable.Icon); method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardLabel(@Nullable CharSequence); + method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setCardLocations(@NonNull java.util.List); method @NonNull public android.service.quickaccesswallet.WalletCard.Builder setNonPaymentCardSecondaryImage(@Nullable android.graphics.drawable.Icon); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d927c5e37a80..7da23b28f3e4 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4353,19 +4353,26 @@ public abstract class PackageManager { public static final String FEATURE_CREDENTIALS = "android.software.credentials"; /** - * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: - * The device supports locking (for example, by a financing provider in case of a missed - * payment). + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * supports locking (for example, by a financing provider in case of a missed payment). */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_DEVICE_LOCK = "android.software.device_lock"; + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * supports showing location-based suggestions for wallet cards provided by the default payment + * app. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS = + "android.software.wallet_location_based_suggestions"; + /** @hide */ public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true; /** - * Extra field name for the URI to a verification file. Passed to a package - * verifier. + * Extra field name for the URI to a verification file. Passed to a package verifier. * * @hide */ diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java index ae67068d9f32..8409503d9b4c 100644 --- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java +++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java @@ -17,6 +17,8 @@ package android.service.quickaccesswallet; import android.annotation.NonNull; +import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.os.Handler; @@ -36,13 +38,18 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { private final IQuickAccessWalletServiceCallbacks mCallback; private final GetWalletCardsRequest mRequest; private final Handler mHandler; + private final Context mContext; private boolean mCalled; - GetWalletCardsCallbackImpl(GetWalletCardsRequest request, - IQuickAccessWalletServiceCallbacks callback, Handler handler) { + GetWalletCardsCallbackImpl( + GetWalletCardsRequest request, + IQuickAccessWalletServiceCallbacks callback, + Handler handler, + Context context) { mRequest = request; mCallback = callback; mHandler = handler; + mContext = context; } /** @@ -50,11 +57,17 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { * was successfully handled by the service. * * @param response The response contains the list of {@link WalletCard walletCards} to be shown - * to the user as well as the index of the card that should initially be - * presented as the selected card. + * to the user as well as the index of the card that should initially be presented as the + * selected card. */ public void onSuccess(@NonNull GetWalletCardsResponse response) { if (isValidResponse(response)) { + // Strip location info from response if the feature is not enabled. + if (!mContext.getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS)) { + removeLocationsFromResponse(response); + } + mHandler.post(() -> onSuccessInternal(response)); } else { Log.w(TAG, "Invalid GetWalletCards response"); @@ -152,4 +165,10 @@ final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback { } return true; } + + private void removeLocationsFromResponse(@NonNull GetWalletCardsResponse response) { + for (WalletCard card : response.getWalletCards()) { + card.removeCardLocations(); + } + } } diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java index d004f34bc721..36fa21c19d27 100644 --- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java +++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java @@ -262,8 +262,8 @@ public abstract class QuickAccessWalletService extends Service { private void onWalletCardsRequestedInternal( GetWalletCardsRequest request, IQuickAccessWalletServiceCallbacks callback) { - onWalletCardsRequested(request, - new GetWalletCardsCallbackImpl(request, callback, mHandler)); + onWalletCardsRequested( + request, new GetWalletCardsCallbackImpl(request, callback, mHandler, this)); } private void onTargetActivityIntentRequestedInternal( diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java index e52adcc6345d..4a4fd041fb19 100644 --- a/core/java/android/service/quickaccesswallet/WalletCard.java +++ b/core/java/android/service/quickaccesswallet/WalletCard.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; import android.graphics.drawable.Icon; +import android.location.Location; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -29,7 +30,8 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; - +import java.util.ArrayList; +import java.util.List; /** * A {@link WalletCard} can represent anything that a user might carry in their wallet -- a credit @@ -67,6 +69,7 @@ public final class WalletCard implements Parcelable { private final Icon mCardIcon; private final CharSequence mCardLabel; private final Icon mNonPaymentCardSecondaryImage; + private List mCardLocations; private WalletCard(Builder builder) { this.mCardId = builder.mCardId; @@ -77,6 +80,7 @@ public final class WalletCard implements Parcelable { this.mCardIcon = builder.mCardIcon; this.mCardLabel = builder.mCardLabel; this.mNonPaymentCardSecondaryImage = builder.mNonPaymentCardSecondaryImage; + this.mCardLocations = builder.mCardLocations; } /** @@ -106,7 +110,7 @@ public final class WalletCard implements Parcelable { writeIconIfNonNull(mCardIcon, dest, flags); TextUtils.writeToParcel(mCardLabel, dest, flags); writeIconIfNonNull(mNonPaymentCardSecondaryImage, dest, flags); - + dest.writeTypedList(mCardLocations, flags); } /** Utility function called by writeToParcel @@ -128,15 +132,20 @@ public final class WalletCard implements Parcelable { PendingIntent pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source); Icon cardIcon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source); CharSequence cardLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); - Icon nonPaymentCardSecondaryImage = source.readByte() == 0 ? null : - Icon.CREATOR.createFromParcel(source); - Builder builder = new Builder(cardId, cardType, cardImage, contentDesc, pendingIntent) - .setCardIcon(cardIcon) - .setCardLabel(cardLabel); - - return cardType == CARD_TYPE_NON_PAYMENT - ? builder.setNonPaymentCardSecondaryImage(nonPaymentCardSecondaryImage).build() : - builder.build(); + Icon nonPaymentCardSecondaryImage = + source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source); + Builder builder = + new Builder(cardId, cardType, cardImage, contentDesc, pendingIntent) + .setCardIcon(cardIcon) + .setCardLabel(cardLabel); + if (cardType == CARD_TYPE_NON_PAYMENT) { + builder.setNonPaymentCardSecondaryImage(nonPaymentCardSecondaryImage); + } + List cardLocations = new ArrayList<>(); + source.readTypedList(cardLocations, Location.CREATOR); + builder.setCardLocations(cardLocations); + + return builder.build(); } @NonNull @@ -226,13 +235,29 @@ public final class WalletCard implements Parcelable { /** * Visual representation of the card when it is tapped. May include additional information - * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. + * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. */ @Nullable public Icon getNonPaymentCardSecondaryImage() { return mNonPaymentCardSecondaryImage; } + /** List of locations that this card might be useful at. */ + @NonNull + public List getCardLocations() { + return mCardLocations; + } + + /** + * Removes locations from card. Should be called if {@link + * PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS} is disabled. + * + * @hide + */ + public void removeCardLocations() { + mCardLocations = new ArrayList<>(); + } + /** * Builder for {@link WalletCard} objects. You must provide cardId, cardImage, * contentDescription, and pendingIntent. If the card is opaque and should be shown with @@ -247,6 +272,7 @@ public final class WalletCard implements Parcelable { private Icon mCardIcon; private CharSequence mCardLabel; private Icon mNonPaymentCardSecondaryImage; + private List mCardLocations = new ArrayList<>(); /** * @param cardId The card id must be non-null and unique within the list of @@ -333,17 +359,30 @@ public final class WalletCard implements Parcelable { /** * Visual representation of the card when it is tapped. May include additional information - * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. + * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. */ @NonNull - public Builder - setNonPaymentCardSecondaryImage(@Nullable Icon nonPaymentCardSecondaryImage) { - Preconditions.checkState(mCardType == CARD_TYPE_NON_PAYMENT, + public Builder setNonPaymentCardSecondaryImage( + @Nullable Icon nonPaymentCardSecondaryImage) { + Preconditions.checkState( + mCardType == CARD_TYPE_NON_PAYMENT, "This field can only be set on non-payment cards"); mNonPaymentCardSecondaryImage = nonPaymentCardSecondaryImage; return this; } + /** + * Set of locations this card might be useful at. If {@link + * PackageManager.FEATURE_WALLET_LOCATION_BASED_SUGGESTIONS} is enabled, the card might be + * shown to the user when a user is near one of these locations. + */ + @NonNull + public Builder setCardLocations(@NonNull List cardLocations) { + Preconditions.checkCollectionElementsNotNull(cardLocations, "cardLocations"); + mCardLocations = cardLocations; + return this; + } + /** * Builds a new {@link WalletCard} instance. * -- cgit v1.2.3-59-g8ed1b