1 /* 2 * Copyright 2020 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 android.service.quickaccesswallet; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.PendingIntent; 23 import android.graphics.drawable.Icon; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.text.TextUtils; 27 28 import com.android.internal.util.Preconditions; 29 30 import java.lang.annotation.Retention; 31 import java.lang.annotation.RetentionPolicy; 32 33 34 /** 35 * A {@link WalletCard} can represent anything that a user might carry in their wallet -- a credit 36 * card, library card, transit pass, etc. Cards are identified by a String identifier and contain a 37 * card type, card image, card image content description, and a {@link PendingIntent} to be used if 38 * the user clicks on the card. Cards may be displayed with an icon and label, though these are 39 * optional. Non-payment cards will also have a second image that will be displayed when the card is 40 * tapped. 41 */ 42 43 public final class WalletCard implements Parcelable { 44 45 /** 46 * Unknown cards refer to cards whose types are unspecified. 47 * @hide 48 */ 49 public static final int CARD_TYPE_UNKNOWN = 0; 50 51 /** 52 * Payment cards refer to credit cards, debit cards or any other cards in the wallet used to 53 * make cash-equivalent payments. 54 * @hide 55 */ 56 public static final int CARD_TYPE_PAYMENT = 1; 57 58 /** 59 * Non-payment cards refer to any cards that are not used for cash-equivalent payment, including 60 * event tickets, flights, offers, loyalty cards, gift cards and transit tickets. 61 * @hide 62 */ 63 public static final int CARD_TYPE_NON_PAYMENT = 2; 64 65 private final String mCardId; 66 private final int mCardType; 67 private final Icon mCardImage; 68 private final CharSequence mContentDescription; 69 private final PendingIntent mPendingIntent; 70 private final Icon mCardIcon; 71 private final CharSequence mCardLabel; 72 private final Icon mNonPaymentCardSecondaryImage; 73 WalletCard(Builder builder)74 private WalletCard(Builder builder) { 75 this.mCardId = builder.mCardId; 76 this.mCardType = builder.mCardType; 77 this.mCardImage = builder.mCardImage; 78 this.mContentDescription = builder.mContentDescription; 79 this.mPendingIntent = builder.mPendingIntent; 80 this.mCardIcon = builder.mCardIcon; 81 this.mCardLabel = builder.mCardLabel; 82 this.mNonPaymentCardSecondaryImage = builder.mNonPaymentCardSecondaryImage; 83 } 84 85 /** 86 * @hide 87 */ 88 @Retention(RetentionPolicy.SOURCE) 89 @IntDef(prefix = {"CARD_TYPE_"}, value = { 90 CARD_TYPE_UNKNOWN, 91 CARD_TYPE_PAYMENT, 92 CARD_TYPE_NON_PAYMENT 93 }) 94 public @interface CardType { 95 } 96 97 @Override describeContents()98 public int describeContents() { 99 return 0; 100 } 101 102 @Override writeToParcel(@onNull Parcel dest, int flags)103 public void writeToParcel(@NonNull Parcel dest, int flags) { 104 dest.writeString(mCardId); 105 dest.writeInt(mCardType); 106 mCardImage.writeToParcel(dest, flags); 107 TextUtils.writeToParcel(mContentDescription, dest, flags); 108 PendingIntent.writePendingIntentOrNullToParcel(mPendingIntent, dest); 109 writeIconIfNonNull(mCardIcon, dest, flags); 110 TextUtils.writeToParcel(mCardLabel, dest, flags); 111 writeIconIfNonNull(mNonPaymentCardSecondaryImage, dest, flags); 112 113 } 114 115 /** Utility function called by writeToParcel 116 */ writeIconIfNonNull(Icon icon, Parcel dest, int flags)117 private void writeIconIfNonNull(Icon icon, Parcel dest, int flags) { 118 if (icon == null) { 119 dest.writeByte((byte) 0); 120 } else { 121 dest.writeByte((byte) 1); 122 icon.writeToParcel(dest, flags); 123 } 124 } 125 readFromParcel(Parcel source)126 private static WalletCard readFromParcel(Parcel source) { 127 String cardId = source.readString(); 128 int cardType = source.readInt(); 129 Icon cardImage = Icon.CREATOR.createFromParcel(source); 130 CharSequence contentDesc = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 131 PendingIntent pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(source); 132 Icon cardIcon = source.readByte() == 0 ? null : Icon.CREATOR.createFromParcel(source); 133 CharSequence cardLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); 134 Icon nonPaymentCardSecondaryImage = source.readByte() == 0 ? null : 135 Icon.CREATOR.createFromParcel(source); 136 Builder builder = new Builder(cardId, cardType, cardImage, contentDesc, pendingIntent) 137 .setCardIcon(cardIcon) 138 .setCardLabel(cardLabel); 139 140 return cardType == CARD_TYPE_NON_PAYMENT 141 ? builder.setNonPaymentCardSecondaryImage(nonPaymentCardSecondaryImage).build() : 142 builder.build(); 143 } 144 145 @NonNull 146 public static final Creator<WalletCard> CREATOR = 147 new Creator<WalletCard>() { 148 @Override 149 public WalletCard createFromParcel(Parcel source) { 150 return readFromParcel(source); 151 } 152 153 @Override 154 public WalletCard[] newArray(int size) { 155 return new WalletCard[size]; 156 } 157 }; 158 159 /** 160 * The card id must be unique within the list of cards returned. 161 */ 162 @NonNull getCardId()163 public String getCardId() { 164 return mCardId; 165 } 166 167 /** 168 * Returns the card type. 169 * @hide 170 */ 171 @NonNull 172 @CardType getCardType()173 public int getCardType() { 174 return mCardType; 175 } 176 177 /** 178 * The visual representation of the card. If the card image Icon is a bitmap, it should have a 179 * width of {@link GetWalletCardsRequest#getCardWidthPx()} and a height of {@link 180 * GetWalletCardsRequest#getCardHeightPx()}. 181 */ 182 @NonNull getCardImage()183 public Icon getCardImage() { 184 return mCardImage; 185 } 186 187 /** 188 * The content description of the card image. 189 */ 190 @NonNull getContentDescription()191 public CharSequence getContentDescription() { 192 return mContentDescription; 193 } 194 195 /** 196 * If the user performs a click on the card, this PendingIntent will be sent. If the device is 197 * locked, the wallet will first request device unlock before sending the pending intent. 198 */ 199 @NonNull getPendingIntent()200 public PendingIntent getPendingIntent() { 201 return mPendingIntent; 202 } 203 204 /** 205 * An icon may be shown alongside the card image to convey information about how the card can be 206 * used, or if some other action must be taken before using the card. For example, an NFC logo 207 * could indicate that the card is NFC-enabled and will be provided to an NFC terminal if the 208 * phone is held in close proximity to the NFC reader. 209 * 210 * <p>If the supplied Icon is backed by a bitmap, it should have width and height 211 * {@link GetWalletCardsRequest#getIconSizePx()}. 212 */ 213 @Nullable getCardIcon()214 public Icon getCardIcon() { 215 return mCardIcon; 216 } 217 218 /** 219 * A card label may be shown alongside the card image to convey information about how the card 220 * can be used, or if some other action must be taken before using the card. For example, an 221 * NFC-enabled card could be labeled "Hold near reader" to inform the user of how to use NFC 222 * cards when interacting with an NFC reader. 223 * 224 * <p>If the provided label is too long to fit on one line, it may be truncated and ellipsized. 225 */ 226 @Nullable getCardLabel()227 public CharSequence getCardLabel() { 228 return mCardLabel; 229 } 230 231 /** 232 * Visual representation of the card when it is tapped. May include additional information 233 * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. 234 * @hide 235 */ 236 @Nullable getNonPaymentCardSecondaryImage()237 public Icon getNonPaymentCardSecondaryImage() { 238 return mNonPaymentCardSecondaryImage; 239 } 240 241 /** 242 * Builder for {@link WalletCard} objects. You must provide cardId, cardImage, 243 * contentDescription, and pendingIntent. If the card is opaque and should be shown with 244 * elevation, set hasShadow to true. cardIcon and cardLabel are optional. 245 */ 246 public static final class Builder { 247 private String mCardId; 248 private int mCardType; 249 private Icon mCardImage; 250 private CharSequence mContentDescription; 251 private PendingIntent mPendingIntent; 252 private Icon mCardIcon; 253 private CharSequence mCardLabel; 254 private Icon mNonPaymentCardSecondaryImage; 255 256 /** 257 * @param cardId The card id must be non-null and unique within the list of 258 * cards returned. <b>Note: 259 * </b> this card ID should <b>not</b> contain PII (Personally 260 * Identifiable Information, such as username or email address). 261 * @param cardType Integer representing the card type. The card type must be 262 * non-null. 263 * @param cardImage The visual representation of the card. If the card image Icon 264 * is a bitmap, it should have a width of {@link 265 * GetWalletCardsRequest#getCardWidthPx()} and a height of {@link 266 * GetWalletCardsRequest#getCardHeightPx()}. If the card image 267 * does not have these dimensions, it may appear distorted when it 268 * is scaled to fit these dimensions on screen. Bitmaps must be 269 * of type {@link android.graphics.Bitmap.Config#HARDWARE} for 270 * performance reasons. 271 * @param contentDescription The content description of the card image. This field is 272 * required and may not be null or empty. 273 * <b>Note: </b> this message should <b>not</b> contain PII 274 * (Personally Identifiable Information, such as username or email 275 * address). 276 * @param pendingIntent If the user performs a click on the card, this PendingIntent 277 * will be sent. If the device is locked, the wallet will first 278 * request device unlock before sending the pending intent. It is 279 * recommended that the pending intent be immutable (use {@link 280 * PendingIntent#FLAG_IMMUTABLE}). 281 * @hide 282 */ Builder(@onNull String cardId, @NonNull @CardType int cardType, @NonNull Icon cardImage, @NonNull CharSequence contentDescription, @NonNull PendingIntent pendingIntent )283 public Builder(@NonNull String cardId, 284 @NonNull @CardType int cardType, 285 @NonNull Icon cardImage, 286 @NonNull CharSequence contentDescription, 287 @NonNull PendingIntent pendingIntent 288 ) { 289 mCardId = cardId; 290 mCardType = cardType; 291 mCardImage = cardImage; 292 mContentDescription = contentDescription; 293 mPendingIntent = pendingIntent; 294 } 295 296 /** 297 * Called when a card type is not provided, in which case it defaults to CARD_TYPE_UNKNOWN. 298 */ Builder(@onNull String cardId, @NonNull Icon cardImage, @NonNull CharSequence contentDescription, @NonNull PendingIntent pendingIntent)299 public Builder(@NonNull String cardId, 300 @NonNull Icon cardImage, 301 @NonNull CharSequence contentDescription, 302 @NonNull PendingIntent pendingIntent) { 303 this(cardId, WalletCard.CARD_TYPE_UNKNOWN, cardImage, contentDescription, 304 pendingIntent); 305 } 306 307 /** 308 * An icon may be shown alongside the card image to convey information about how the card 309 * can be used, or if some other action must be taken before using the card. For example, an 310 * NFC logo could indicate that the card is NFC-enabled and will be provided to an NFC 311 * terminal if the phone is held in close proximity to the NFC reader. This field is 312 * optional. 313 * 314 * <p>If the supplied Icon is backed by a bitmap, it should have width and height 315 * {@link GetWalletCardsRequest#getIconSizePx()}. 316 */ 317 @NonNull setCardIcon(@ullable Icon cardIcon)318 public Builder setCardIcon(@Nullable Icon cardIcon) { 319 mCardIcon = cardIcon; 320 return this; 321 } 322 323 /** 324 * A card label may be shown alongside the card image to convey information about how the 325 * card can be used, or if some other action must be taken before using the card. For 326 * example, an NFC-enabled card could be labeled "Hold near reader" to inform the user of 327 * how to use NFC cards when interacting with an NFC reader. This field is optional. 328 * <b>Note: </b> this card label should <b>not</b> contain PII (Personally Identifiable 329 * Information, such as username or email address). If the provided label is too long to fit 330 * on one line, it may be truncated and ellipsized. 331 */ 332 @NonNull setCardLabel(@ullable CharSequence cardLabel)333 public Builder setCardLabel(@Nullable CharSequence cardLabel) { 334 mCardLabel = cardLabel; 335 return this; 336 } 337 338 /** 339 * Visual representation of the card when it is tapped. May include additional information 340 * unique to the card, such as a barcode or number. Only valid for CARD_TYPE_NON_PAYMENT. 341 * @hide 342 */ 343 @NonNull 344 public Builder setNonPaymentCardSecondaryImage(@ullable Icon nonPaymentCardSecondaryImage)345 setNonPaymentCardSecondaryImage(@Nullable Icon nonPaymentCardSecondaryImage) { 346 Preconditions.checkState(mCardType == CARD_TYPE_NON_PAYMENT, 347 "This field can only be set on non-payment cards"); 348 mNonPaymentCardSecondaryImage = nonPaymentCardSecondaryImage; 349 return this; 350 } 351 352 /** 353 * Builds a new {@link WalletCard} instance. 354 * 355 * @return A built response. 356 */ 357 @NonNull build()358 public WalletCard build() { 359 return new WalletCard(this); 360 } 361 } 362 } 363