Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4620)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java

Issue 2116583002: Credit card editor for PaymentRequest UI. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update comments in personal_data_manager_android.h Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b881eb50eb681eadadcdd377ab4664916e70c17
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/CardEditor.java
@@ -0,0 +1,555 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import org.chromium.base.Callback;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.autofill.PersonalDataManager;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
+import org.chromium.chrome.browser.payments.PaymentRequestImpl.PaymentRequestServiceObserverForTest;
+import org.chromium.chrome.browser.payments.ui.EditorFieldModel;
+import org.chromium.chrome.browser.payments.ui.EditorFieldModel.EditorFieldValidator;
+import org.chromium.chrome.browser.payments.ui.EditorModel;
+import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.DropdownKeyValue;
+import org.chromium.content_public.browser.WebContents;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * A credit card editor. Can be used for editing both local and server credit cards. Everything in
+ * local cards can be edited. For server cards, only the billing address is editable.
+ */
+public class CardEditor extends EditorBase<AutofillPaymentInstrument> {
+ /** The web contents where the web payments API is invoked. */
+ private final WebContents mWebContents;
+
+ /** Used for verifying billing address completeness and also editing billing addresses. */
+ private final AddressEditor mAddressEditor;
+
+ private final PaymentRequestServiceObserverForTest mObserverForTest;
+
+ /** All card types recognized by this editor. This is a subset of card types in
gone 2016/07/13 21:12:59 Full Javadocs don't have sentences on the same lin
please use gerrit instead 2016/07/14 17:21:43 Done.
+ * https://w3c.github.io/webpayments-methods-card/#method-id */
+ private final Set<String> mCardTypes;
+
+ /** A mapping from the recognized card types to their icons. */
+ private final Map<String, Integer> mCardTypeIcons;
+
+ /** A mapping from the recognized card types to the string resource identifiers of their
+ * human-readable descriptions. This is for the screen reader. */
+ private final Map<String, Integer> mCardTypeDescriptions;
+
+ /** The card types accepted by the merchant website. This is a subset of recognized cards. Used
+ * in the validator. */
+ private final Set<String> mAcceptedCardTypes;
+
+ /** The resource identifiers for the icons of the accepted card types. Used in the editor as a
+ * hint to the user about the valid card types. This is important to keep in a list, because the
+ * display order matters to the merchants. */
+ private final List<Integer> mAcceptedCardTypeResourceIds;
+
+ /** The string resource identifiers for the human-readable descriptions of the accepted card
+ * types. Used for the screen reader on the card type icons. Should match the icons in
+ * mAcceptedCardTypeResourceIds. */
+ private final List<Integer> mAcceptedCardTypeDescriptions;
+
+ private final Handler mHandler;
+
+ @Nullable private Calendar mCalendar;
+
+ @Nullable private EditorFieldValidator mCardNumberValidator;
+ @Nullable private EditorFieldModel mIconHint;
+ @Nullable private EditorFieldModel mNumberField;
+ @Nullable private EditorFieldModel mNameField;
+ @Nullable private EditorFieldModel mMonthField;
+ @Nullable private EditorFieldModel mYearField;
+ @Nullable private EditorFieldModel mBillingAddressField;
+ @Nullable private EditorFieldModel mSaveCardCheckbox;
+
+ /**
+ * Builds a credit card editor.
+ *
+ * @param webContents The web contents where the web payments API is invoked. Should not be
gone 2016/07/13 21:13:00 Probably don't need to say "Should not be null" gi
please use gerrit instead 2016/07/14 17:21:44 Done.
+ * null.
+ * @param addressEditor Used for verifying billing address completeness and also editing
+ * billing addresses. Should not be null.
+ * @param observerForTest Optional observer for test.
+ */
+ public CardEditor(WebContents webContents, AddressEditor addressEditor,
+ @Nullable PaymentRequestServiceObserverForTest observerForTest) {
+ assert webContents != null;
+ assert addressEditor != null;
+
+ mWebContents = webContents;
+ mAddressEditor = addressEditor;
+ mObserverForTest = observerForTest;
+
+ mCardTypes = new HashSet<>();
gone 2016/07/13 21:13:00 Makes more sense to have a HashMap containing Card
please use gerrit instead 2016/07/14 17:21:44 Done.
+ mCardTypes.add("amex");
+ mCardTypes.add("diners");
+ mCardTypes.add("discover");
+ mCardTypes.add("jcb");
+ mCardTypes.add("mastercard");
+ mCardTypes.add("unionpay");
+ mCardTypes.add("visa");
+
+ mCardTypeIcons = new HashMap<>();
+ mCardTypeIcons.put("amex", R.drawable.pr_amex);
+ mCardTypeIcons.put("diners", R.drawable.pr_dinersclub);
+ mCardTypeIcons.put("discover", R.drawable.pr_discover);
+ mCardTypeIcons.put("jcb", R.drawable.pr_jcb);
+ mCardTypeIcons.put("mastercard", R.drawable.pr_mc);
+ mCardTypeIcons.put("unionpay", R.drawable.pr_unionpay);
+ mCardTypeIcons.put("visa", R.drawable.pr_visa);
+ assert mCardTypes.size() == mCardTypeIcons.size();
+
+ mCardTypeDescriptions = new HashMap<>();
+ mCardTypeDescriptions.put("amex", R.string.autofill_cc_amex);
+ mCardTypeDescriptions.put("diners", R.string.autofill_cc_diners);
+ mCardTypeDescriptions.put("discover", R.string.autofill_cc_discover);
+ mCardTypeDescriptions.put("jcb", R.string.autofill_cc_jcb);
+ mCardTypeDescriptions.put("mastercard", R.string.autofill_cc_mastercard);
+ mCardTypeDescriptions.put("unionpay", R.string.autofill_cc_union_pay);
+ mCardTypeDescriptions.put("visa", R.string.autofill_cc_visa);
+ assert mCardTypes.size() == mCardTypeDescriptions.size();
+
+ mAcceptedCardTypes = new HashSet<>();
+ mAcceptedCardTypeResourceIds = new ArrayList<>();
+ mAcceptedCardTypeDescriptions = new ArrayList<>();
+ mHandler = new Handler();
+
+ // Avoid strict mode violation when reading timezone information from disk.
+ new AsyncTask<Void, Void, Calendar>() {
+ @Override
+ protected Calendar doInBackground(Void... unused) {
+ return Calendar.getInstance();
+ }
+
+ @Override
+ protected void onPostExecute(Calendar result) {
+ mCalendar = result;
+ }
+ }.execute();
+ }
+
+ /**
+ * Returns whether the given credit card is complete, i.e., can be sent to the merchant as-is
+ * without editing first.
+ *
+ * For both local and server cards, verifies that the billing address is complete. For local
+ * cards also verifies that the card number is valid and the name on card is not empty.
+ *
+ * Does not check the expiration date. If the card is expired, the user has the opportunity
+ * update the expiration date when providing their CVC in the card unmask dialog.
+ *
+ * Does not check that the card type is accepted by the merchant. This is done elsewhere to
+ * filter out such cards from view entirely. Cards that are not accepted by the merchant should
+ * not be edited.
+ *
+ * @param card The card to check.
+ * @return Whether the card is complete.
+ */
+ public boolean isCardComplete(CreditCard card) {
+ if (card == null || TextUtils.isEmpty(card.getBillingAddressId())
+ || !mAddressEditor.isProfileComplete(PersonalDataManager.getInstance().getProfile(
+ card.getBillingAddressId()))) {
+ return false;
+ }
+
+ if (!card.getIsLocal()) return true;
+
+ return !TextUtils.isEmpty(card.getName())
+ || getCardNumberValidator().isValid(card.getNumber());
+ }
+
+ private EditorFieldValidator getCardNumberValidator() {
gone 2016/07/13 21:12:59 Make mCardNumberValidator final and create this in
please use gerrit instead 2016/07/14 17:21:43 Done.
+ if (mCardNumberValidator == null) {
+ mCardNumberValidator = new EditorFieldValidator() {
+ @Override
+ public boolean isValid(@Nullable CharSequence value) {
+ return value != null && mAcceptedCardTypes.contains(
+ PersonalDataManager.getInstance().getBasicCardPaymentTypeIfValid(
+ value.toString()));
+ }
+ };
+ }
+
+ return mCardNumberValidator;
+ }
+
+ /**
+ * Adds accepted payment methods to the editor, if they are recognized credit card types.
+ *
+ * @param acceptedMethods The accepted method payments.
+ */
+ public void addAcceptedPaymentMethodsIfRecognized(String[] acceptedMethods) {
+ assert acceptedMethods != null;
+ for (int i = 0; i < acceptedMethods.length; i++) {
+ String method = acceptedMethods[i];
+ if (mCardTypes.contains(method) && !mAcceptedCardTypes.contains(method)) {
+ mAcceptedCardTypes.add(method);
+ if (mCardTypeIcons.containsKey(method)) {
+ mAcceptedCardTypeResourceIds.add(mCardTypeIcons.get(method));
+ mAcceptedCardTypeDescriptions.add(mCardTypeDescriptions.get(method));
+ }
+ }
+ }
+ }
+
+ /**
+ * Builds and shows an editor model with the following fields for local cards.
+ *
+ * [ accepted card types hint images ]
+ * [ card number ]
+ * [ name on card ]
+ * [ expiration month ][ expiration year ]
+ * [ billing address dropdown ]
+ * [ save this card checkbox ] <-- Shown only for new cards.
+ *
+ * Server cards have the following fields instead.
+ *
+ * [ card's obfuscated number ]
+ * [ billing address dropdown ]
+ */
+ @Override
+ public void edit(@Nullable final AutofillPaymentInstrument toEdit,
+ final Callback<AutofillPaymentInstrument> callback) {
+ super.edit(toEdit, callback);
+
gone 2016/07/13 21:13:00 Given how often you do toEdit == null, you should
please use gerrit instead 2016/07/14 17:21:44 Done.
+ // Ensure that |instrument| and |card| are always not null. If |toEdit| is null, we're
+ // creating a new credit card.
+ final AutofillPaymentInstrument instrument = toEdit == null
gone 2016/07/13 21:13:00 really hard to parse; put (toEdit == null ? ....);
please use gerrit instead 2016/07/14 17:21:43 Should be better after using isNewCard.
+ ? new AutofillPaymentInstrument(mWebContents, new CreditCard(), null) : toEdit;
+ final CreditCard card = instrument.getCard();
+
+ // The title of the editor depends on whether we're adding a new card (toEdit is null) or
+ // editing an existing card (toEdit is not null).
+ final EditorModel editor = new EditorModel(mContext.getString(toEdit == null
+ ? R.string.autofill_create_credit_card : R.string.autofill_edit_credit_card));
gone 2016/07/13 21:13:00 indentation
please use gerrit instead 2016/07/14 17:21:43 Done.
+
+ if (card.getIsLocal()) {
+ // It's unlikely, but possible that the calendar retrieval task has not completed yet.
+ // The calendar is required only for local cards. Error out if it's not ready.
gone 2016/07/13 21:13:00 Can't you save the AsyncTask and then do a .get()
please use gerrit instead 2016/07/14 17:21:43 Done.
+ if (mCalendar == null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onResult(null);
+ }
+ });
+ return;
+ }
+
+ // Let user edit any part of the local card.
+ addLocalCardInputs(editor, card);
+ } else {
+ // Display some information about the server card.
+ editor.addField(EditorFieldModel.createLabel(card.getObfuscatedNumber(), card.getName(),
+ card.getFormattedExpirationDate(mContext), card.getIssuerIconDrawableId()));
+ }
+
+ // Always show the billing address dropdown.
+ addBillingAddressDropdown(editor, card);
+
+ // Allow saving new cards on disk.
+ if (toEdit == null) {
+ addSaveCardCheckbox(editor);
+ }
+
+ // If the user clicks [Cancel], send a null card back to the caller.
+ editor.setCancelCallback(new Runnable() {
+ @Override
+ public void run() {
+ callback.onResult(null);
+ }
+ });
+
+ // If the user clicks [Done], save changes on disk, mark the card "complete," and send it
+ // back to the caller.
+ editor.setDoneCallback(new Runnable() {
+ @Override
+ public void run() {
+ commitChanges(card, toEdit == null);
+ instrument.completeInstrument(card,
+ PersonalDataManager.getInstance().getProfile(card.getBillingAddressId()));
+ callback.onResult(instrument);
+ }
+ });
+
+ mEditorView.show(editor);
+ }
+
+ /**
+ * Adds the following fields to the editor.
+ *
+ * [ accepted card types hint images ]
+ * [ card number ]
+ * [ name on card ]
+ * [ expiration month ][ expiration year ]
+ */
+ private void addLocalCardInputs(EditorModel editor, CreditCard card) {
+ assert mCalendar != null;
+
+ // Local card editor shows a card icon hint.
+ if (mIconHint == null) {
+ mIconHint = EditorFieldModel.createIconList(
+ mContext.getString(R.string.payments_accepted_cards_label),
+ mAcceptedCardTypeResourceIds, mAcceptedCardTypeDescriptions);
+ }
+ editor.addField(mIconHint);
+
+ // Card number is validated.
+ if (mNumberField == null) {
+ mNumberField = EditorFieldModel.createTextInput(
+ EditorFieldModel.INPUT_TYPE_HINT_CREDIT_CARD,
+ mContext.getString(R.string.autofill_credit_card_editor_number),
+ null, getCardNumberValidator(),
+ mContext.getString(R.string.payments_card_number_required_validation_message),
+ mContext.getString(R.string.payments_card_number_invalid_validation_message),
+ null);
+ }
+ mNumberField.setValue(card.getNumber());
+ editor.addField(mNumberField);
+
+ // Name on card is required.
+ if (mNameField == null) {
+ mNameField = EditorFieldModel.createTextInput(
+ EditorFieldModel.INPUT_TYPE_HINT_PERSON_NAME,
+ mContext.getString(R.string.autofill_credit_card_editor_name), null, null,
+ mContext.getString(R.string.payments_name_on_card_required_validation_message),
+ null, null);
+ }
+ mNameField.setValue(card.getName());
+ editor.addField(mNameField);
+
+ // Expiration month dropdown.
+ if (mMonthField == null) {
+ mMonthField = EditorFieldModel.createDropdown(
+ mContext.getString(R.string.autofill_credit_card_editor_expiration_date),
+ buildMonthDropdownKeyValues(mCalendar));
+ mMonthField.setIsFullLine(false);
+ }
+ mMonthField.setDropdownCallback(new Callback<Pair<String, Runnable>>() {
+ @Override
+ public void onResult(final Pair<String, Runnable> eventData) {
+ mMonthField.setValue(eventData.first);
+ }
+ });
+ mMonthField.setValue(card.getMonth());
+ editor.addField(mMonthField);
+
+ // Expiration year dropdown is side-by-side with the expiration year dropdown. The dropdown
+ // should include the card's expiration year, so it's not cached.
+ mYearField = EditorFieldModel.createDropdown(
+ null, buildYearDropdownKeyValues(mCalendar, card.getYear()));
+ mYearField.setIsFullLine(false);
+ mYearField.setDropdownCallback(new Callback<Pair<String, Runnable>>() {
+ @Override
+ public void onResult(final Pair<String, Runnable> eventData) {
+ mYearField.setValue(eventData.first);
+ }
+ });
+ mYearField.setValue(card.getYear());
+ editor.addField(mYearField);
+ }
+
+ /** Builds the key-value pairs for the month dropdown. */
+ private static List<DropdownKeyValue> buildMonthDropdownKeyValues(Calendar calendar) {
+ assert calendar != null;
+
+ List<DropdownKeyValue> result = new ArrayList<>();
+
+ Locale locale = Locale.getDefault();
+ SimpleDateFormat keyFormatter = new SimpleDateFormat("MM", locale);
+ SimpleDateFormat valueFormatter = new SimpleDateFormat("MMMM (MM)", locale);
+
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ for (int month = 0; month < 12; month++) {
+ calendar.set(Calendar.MONTH, month);
+ Date date = calendar.getTime();
+ result.add(
+ new DropdownKeyValue(keyFormatter.format(date), valueFormatter.format(date)));
+ }
+
+ return result;
+ }
+
+ /** Builds the key-value pairs for the year dropdown. */
+ private static List<DropdownKeyValue> buildYearDropdownKeyValues(
+ Calendar calendar, String alwaysIncludeYear) {
gone 2016/07/13 21:13:00 Use: alwaysIncludedYear "alwaysIncludeYear" sound
please use gerrit instead 2016/07/14 17:21:44 Done.
+ assert calendar != null;
+
+ List<DropdownKeyValue> result = new ArrayList<>();
+
+ int initialYear = calendar.get(Calendar.YEAR);
+ boolean foundAlwaysIncludeYear = false;
+ for (int year = initialYear; year < initialYear + 10; year++) {
+ String yearString = Integer.toString(year);
+ if (yearString.equals(alwaysIncludeYear)) foundAlwaysIncludeYear = true;
+ result.add(new DropdownKeyValue(yearString, yearString));
+ }
+
+ if (!foundAlwaysIncludeYear && !TextUtils.isEmpty(alwaysIncludeYear)) {
+ result.add(0, new DropdownKeyValue(alwaysIncludeYear, alwaysIncludeYear));
+ }
+
+ return result;
+ }
+
+ /**
+ * Adds the billing address dropdown to the editor with the following items.
+ *
+ * | "select" |
+ * | complete address 1 |
+ * | complete address 2 |
+ * ...
+ * | complete address n |
+ * | "add address" |
+ */
+ private void addBillingAddressDropdown(EditorModel editor, final CreditCard card) {
+ final List<DropdownKeyValue> billingAddresses = new ArrayList<>();
+
+ // The empty key indicates no selection has been made.
+ billingAddresses.add(new DropdownKeyValue("",
gone 2016/07/13 21:13:00 pull "" into a constant instead of just commenting
please use gerrit instead 2016/07/14 17:21:43 Done.
+ mContext.getString(R.string.autofill_billing_address_select_prompt)));
+
+ // Re-read profiles every time, in case any of them have changed. This does not cause a disk
+ // read, because personal_data_manager.h holds a cache.
+ final PersonalDataManager pdm = PersonalDataManager.getInstance();
+ List<AutofillProfile> profiles = pdm.getProfilesForSettings();
+
+ // 1) Include only local profiles, because GUIDs of server profiles change on every browser
+ // restart. Server profiles are not supported as billing addresses.
+ // 2) Include only complete profiles, so that user launches the editor only when explicitly
+ // selecting [+ ADD ADDRESS] in the dropdown.
+ for (int i = 0; i < profiles.size(); i++) {
+ AutofillProfile profile = profiles.get(i);
+ if (profile.getIsLocal() && mAddressEditor.isProfileComplete(profile)) {
+ // Key is profile GUID. Value is profile label.
+ billingAddresses.add(new DropdownKeyValue(profile.getGUID(), profile.getLabel()));
+ }
+ }
+
+ // The "add" key indicates the user wishes to add a new profile.
+ billingAddresses.add(new DropdownKeyValue("add",
gone 2016/07/13 21:13:00 pull "add" into a constant
please use gerrit instead 2016/07/14 17:21:44 Done.
+ mContext.getString(R.string.autofill_create_profile)));
+
+ // Don't cache the billing address dropdown, because the user may have added or removed
+ // profiles.
+ mBillingAddressField = EditorFieldModel.createDropdown(
+ mContext.getString(R.string.autofill_credit_card_editor_billing_address),
+ billingAddresses);
+
+ // The billing address is required.
+ mBillingAddressField.setRequiredErrorMessage(
+ mContext.getString(R.string.payments_billing_address_required_validation_message));
+
+ mBillingAddressField.setDropdownCallback(new Callback<Pair<String, Runnable>>() {
+ @Override
+ public void onResult(final Pair<String, Runnable> eventData) {
+ // Ignore all billing address dropdown selection except [+ ADD ADDRESS].
+ if (!"add".equals(eventData.first)) {
+ if (mObserverForTest != null) {
+ mObserverForTest.onPaymentRequestServiceProcessBillingAddressChange();
+ }
+ return;
+ }
+
+ mAddressEditor.edit(null, new Callback<AutofillAddress>() {
+ @Override
+ public void onResult(AutofillAddress billingAddress) {
+ if (billingAddress == null) {
+ // User has cancelled the address editor.
+ mBillingAddressField.setValue(null);
+ } else {
+ // User has added a new complete address. Add it to the top of the
+ // dropdown, under the "Select" prompt.
+ billingAddresses.add(1, new DropdownKeyValue(
+ billingAddress.getIdentifier(), billingAddress.getSublabel()));
+ mBillingAddressField.setDropdownKeyValues(billingAddresses);
+ mBillingAddressField.setValue(billingAddress.getIdentifier());
+ }
+
+ // Let the card editor UI re-read the model and re-create UI elements.
+ mHandler.post(eventData.second);
+ }
+ });
+ }
+ });
+
+ mBillingAddressField.setValue(card.getBillingAddressId());
+ editor.addField(mBillingAddressField);
+ }
+
+ /** Adds the "save this card" checkbox to the editor. */
+ private void addSaveCardCheckbox(EditorModel editor) {
+ if (mSaveCardCheckbox == null) {
+ mSaveCardCheckbox = EditorFieldModel.createCheckbox(
+ mContext.getString(R.string.payments_save_card_to_device_checkbox));
+ }
+
+ // Saving locally is disabled by default, similar to autofill always prompting the user to
+ // save card on device, instead of silently saving it.
+ mSaveCardCheckbox.setIsChecked(false);
+
+ editor.addField(mSaveCardCheckbox);
+ }
+
+ /**
+ * Saves the edited credit card.
+ *
+ * If this is a server card, then only its billing address identifier is updated.
+ *
+ * If this is a new local card, then it's saved on this device only if the user has checked the
+ * "save this card" checkbox.
+ */
+ private void commitChanges(CreditCard card, boolean isNewCard) {
+ card.setBillingAddressId(mBillingAddressField.getValue().toString());
+ PersonalDataManager pdm = PersonalDataManager.getInstance();
+ if (!card.getIsLocal()) {
+ pdm.updateServerCardBillingAddress(card.getGUID(), card.getBillingAddressId());
+ return;
+ }
+
+ card.setNumber(mNumberField.getValue().toString().replace(" ", "").replace("-", ""));
+ card.setName(mNameField.getValue().toString());
+ card.setMonth(mMonthField.getValue().toString());
+ card.setYear(mYearField.getValue().toString());
+
+ // Calculate the basic card payment type, obfuscated number, and the icon for this card.
+ // All of these depend on the card number. The type is sent to the merchant website. The
+ // obfuscated number and the icon are displayed in the user interface.
+ CreditCard displayableCard = pdm.getCreditCardForNumber(card.getNumber());
+ card.setBasicCardPaymentType(displayableCard.getBasicCardPaymentType());
+ card.setObfuscatedNumber(displayableCard.getObfuscatedNumber());
+ card.setIssuerIconDrawableId(displayableCard.getIssuerIconDrawableId());
+
+ if (!isNewCard) {
+ pdm.setCreditCard(card);
+ return;
+ }
+
+ if (mSaveCardCheckbox != null && mSaveCardCheckbox.isChecked()) {
+ card.setGUID(pdm.setCreditCard(card));
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698