| // Copyright 2019 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.autofill_assistant.user_data; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.text.TextUtils; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.ImageView; |
| import android.widget.TextView; |
| |
| import androidx.annotation.DrawableRes; |
| import androidx.annotation.Nullable; |
| |
| import org.chromium.chrome.autofill_assistant.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.AutofillAddress; |
| import org.chromium.chrome.browser.payments.AutofillPaymentInstrument; |
| import org.chromium.chrome.browser.payments.CardEditor; |
| |
| import java.util.List; |
| |
| /** |
| * The payment method section of the Autofill Assistant payment request. |
| */ |
| public class AssistantPaymentMethodSection |
| extends AssistantCollectUserDataSection<AutofillPaymentInstrument> { |
| private CardEditor mEditor; |
| private boolean mIgnorePaymentMethodsChangeNotifications; |
| private boolean mRequiresBillingPostalCode; |
| private String mBillingPostalCodeMissingText; |
| private String mCreditCardExpiredText; |
| |
| AssistantPaymentMethodSection(Context context, ViewGroup parent) { |
| super(context, parent, R.layout.autofill_assistant_payment_method_summary, |
| R.layout.autofill_assistant_payment_method_full, |
| context.getResources().getDimensionPixelSize( |
| R.dimen.autofill_assistant_payment_request_payment_method_title_padding), |
| context.getString(R.string.payments_add_card), |
| context.getString(R.string.payments_add_card)); |
| setTitle(context.getString(R.string.payments_method_of_payment_label)); |
| } |
| |
| public void setEditor(CardEditor editor) { |
| mEditor = editor; |
| if (mEditor == null) { |
| return; |
| } |
| |
| PersonalDataManager personalDataManager = PersonalDataManager.getInstance(); |
| for (AutofillPaymentInstrument method : getItems()) { |
| String guid = method.getCard().getBillingAddressId(); |
| PersonalDataManager.AutofillProfile profile = personalDataManager.getProfile(guid); |
| if (profile != null) { |
| addAutocompleteInformationToEditor(profile); |
| } |
| } |
| } |
| |
| @Override |
| protected void createOrEditItem(@Nullable AutofillPaymentInstrument oldItem) { |
| if (mEditor == null) { |
| return; |
| } |
| mEditor.edit(oldItem, newItem -> { |
| assert (newItem != null && newItem.isComplete()); |
| mIgnorePaymentMethodsChangeNotifications = true; |
| addOrUpdateItem(newItem, true); |
| mIgnorePaymentMethodsChangeNotifications = false; |
| }, cancel -> {}); |
| } |
| |
| @Override |
| protected void updateFullView(View fullView, AutofillPaymentInstrument method) { |
| if (method == null) { |
| return; |
| } |
| updateSummaryView(fullView, method); |
| TextView cardNameView = fullView.findViewById(R.id.credit_card_name); |
| cardNameView.setText(method.getCard().getName()); |
| hideIfEmpty(cardNameView); |
| } |
| |
| @Override |
| protected void updateSummaryView(View summaryView, AutofillPaymentInstrument method) { |
| if (method == null) { |
| return; |
| } |
| |
| ImageView cardIssuerImageView = summaryView.findViewById(R.id.credit_card_issuer_icon); |
| try { |
| cardIssuerImageView.setImageDrawable( |
| summaryView.getContext().getResources().getDrawable( |
| method.getCard().getIssuerIconDrawableId())); |
| } catch (Resources.NotFoundException e) { |
| cardIssuerImageView.setImageDrawable(null); |
| } |
| |
| /** |
| * By default, the obfuscated number contains the issuer (e.g., 'Visa'). This is needlessly |
| * verbose, so we strip it away. See |PersonalDataManagerTest::testAddAndEditCreditCards| |
| * for explanation of "\u0020...\u2060". |
| */ |
| String obfuscatedNumber = method.getCard().getObfuscatedNumber(); |
| int beginningOfObfuscatedNumber = |
| Math.max(obfuscatedNumber.indexOf("\u0020\u202A\u2022\u2060"), 0); |
| obfuscatedNumber = obfuscatedNumber.substring(beginningOfObfuscatedNumber); |
| TextView cardNumberView = summaryView.findViewById(R.id.credit_card_number); |
| cardNumberView.setText(obfuscatedNumber); |
| hideIfEmpty(cardNumberView); |
| |
| TextView cardExpirationView = summaryView.findViewById(R.id.credit_card_expiration); |
| cardExpirationView.setText( |
| method.getCard().getFormattedExpirationDate(summaryView.getContext())); |
| hideIfEmpty(cardExpirationView); |
| |
| TextView errorMessageView = summaryView.findViewById(R.id.incomplete_error); |
| setErrorMessage(errorMessageView, method); |
| hideIfEmpty(errorMessageView); |
| } |
| |
| @Override |
| protected boolean canEditOption(AutofillPaymentInstrument method) { |
| return true; |
| } |
| |
| @Override |
| protected @DrawableRes int getEditButtonDrawable(AutofillPaymentInstrument method) { |
| return R.drawable.ic_edit_24dp; |
| } |
| |
| @Override |
| protected String getEditButtonContentDescription(AutofillPaymentInstrument method) { |
| return mContext.getString(R.string.autofill_edit_credit_card); |
| } |
| |
| @Override |
| protected boolean areEqual(@Nullable AutofillPaymentInstrument optionA, |
| @Nullable AutofillPaymentInstrument optionB) { |
| if (optionA == null || optionB == null) { |
| return optionA == optionB; |
| } |
| if (TextUtils.equals(optionA.getIdentifier(), optionB.getIdentifier())) { |
| return true; |
| } |
| return areEqualCards(optionA.getCard(), optionB.getCard()) |
| && areEqualBillingProfiles( |
| optionA.getBillingProfile(), optionB.getBillingProfile()); |
| } |
| private boolean areEqualCards(CreditCard cardA, CreditCard cardB) { |
| // TODO(crbug.com/806868): Implement better check for the case where PDM is disabled, we |
| // won't have IDs. |
| return TextUtils.equals(cardA.getGUID(), cardB.getGUID()); |
| } |
| private boolean areEqualBillingProfiles( |
| @Nullable AutofillProfile profileA, @Nullable AutofillProfile profileB) { |
| if (profileA == null || profileB == null) { |
| return profileA == profileB; |
| } |
| // TODO(crbug.com/806868): Implement better check for the case where PDM is disabled, we |
| // won't have IDs. |
| return TextUtils.equals(profileA.getGUID(), profileB.getGUID()); |
| } |
| |
| void onProfilesChanged(List<PersonalDataManager.AutofillProfile> profiles) { |
| // TODO(crbug.com/806868): replace suggested billing addresses (remove if necessary). |
| for (PersonalDataManager.AutofillProfile profile : profiles) { |
| addAutocompleteInformationToEditor(profile); |
| } |
| } |
| |
| /** |
| * The set of available payment methods has changed externally. This will rebuild the UI with |
| * the new/changed set of payment methods, while keeping the selected item if possible. |
| */ |
| void onAvailablePaymentMethodsChanged(List<AutofillPaymentInstrument> paymentMethods) { |
| if (mIgnorePaymentMethodsChangeNotifications) { |
| return; |
| } |
| |
| int selectedMethodIndex = -1; |
| if (mSelectedOption != null) { |
| for (int i = 0; i < paymentMethods.size(); ++i) { |
| if (areEqual(mSelectedOption, paymentMethods.get(i))) { |
| selectedMethodIndex = i; |
| break; |
| } |
| } |
| } |
| |
| // Replace current set of items, keep selection if possible. |
| setItems(paymentMethods, selectedMethodIndex); |
| } |
| |
| void setRequiresBillingPostalCode(boolean requiresBillingPostalCode) { |
| mRequiresBillingPostalCode = requiresBillingPostalCode; |
| } |
| |
| void setBillingPostalCodeMissingText(String text) { |
| mBillingPostalCodeMissingText = text; |
| } |
| |
| void setCreditCardExpiredText(String text) { |
| mCreditCardExpiredText = text; |
| } |
| |
| private void addAutocompleteInformationToEditor(PersonalDataManager.AutofillProfile profile) { |
| // The check for non-null label is necessary to prevent crash in editor when opening. |
| if (mEditor == null || profile.getLabel() == null) { |
| return; |
| } |
| mEditor.updateBillingAddressIfComplete(new AutofillAddress(mContext, profile)); |
| } |
| |
| private void setErrorMessage(TextView errorMessageView, AutofillPaymentInstrument method) { |
| if (!method.isComplete()) { |
| errorMessageView.setText(R.string.autofill_assistant_payment_information_missing); |
| return; |
| } |
| |
| if (mRequiresBillingPostalCode |
| && (method.getBillingProfile() == null |
| || TextUtils.isEmpty(method.getBillingProfile().getPostalCode()))) { |
| errorMessageView.setText(mBillingPostalCodeMissingText); |
| return; |
| } |
| |
| if ((method.getMissingFields() |
| & AutofillPaymentInstrument.CompletionStatus.CREDIT_CARD_EXPIRED) |
| == 1) { |
| errorMessageView.setText(mCreditCardExpiredText); |
| return; |
| } |
| |
| errorMessageView.setText(""); |
| } |
| } |