Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..51103f3d3085a5ee3652873f880d75df15a66e17 |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java |
| @@ -0,0 +1,302 @@ |
| +// 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.Handler; |
| +import android.telephony.PhoneNumberUtils; |
| +import android.text.TextUtils; |
| + |
| +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.payments.ui.EditorFieldModel; |
| +import org.chromium.chrome.browser.payments.ui.EditorFieldModel.EditorFieldValidator; |
| +import org.chromium.chrome.browser.payments.ui.EditorFieldModel.SpinnerChangedEventData; |
| +import org.chromium.chrome.browser.payments.ui.EditorModel; |
| +import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge; |
| +import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.AddressField; |
| +import org.chromium.chrome.browser.preferences.autofill.AutofillProfileBridge.AddressUiComponent; |
| + |
| +import java.util.ArrayList; |
| +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; |
| + |
| +/** |
| + * An address editor. Can be used for either shipping or billing address editing. |
| + */ |
| +public class AddressEditor extends EditorBase<AutofillAddress> { |
| + private final Handler mHandler; |
| + private final Map<Integer, EditorFieldModel> mAddressFields; |
| + private final List<CharSequence> mAllPhoneNumbers; |
|
gone
2016/06/28 17:30:28
Same comment about "All" as the other CL
please use gerrit instead
2016/06/29 00:28:45
Done.
|
| + @Nullable private AutofillProfileBridge mAutofillProfileBridge; |
| + @Nullable private EditorFieldModel mCountryField; |
| + @Nullable private EditorFieldModel mPhoneField; |
| + @Nullable private EditorFieldValidator mPhoneValidator; |
| + @Nullable private List<AddressUiComponent> mAddressUiComponents; |
| + |
| + /** |
| + * Builds an address editor. |
| + */ |
| + public AddressEditor() { |
| + mHandler = new Handler(); |
| + mAddressFields = new HashMap<>(); |
| + mAllPhoneNumbers = new ArrayList<>(); |
| + } |
| + |
| + /** |
| + * Returns whether the given profile can be sent to the merchant as-is without editing first. If |
| + * the country code is not set or invalid, but all fields for the default locale's country code |
| + * are present, then the profile is deemed "complete." AutoflllAddress.toPaymentAddress() will |
| + * use the default locale to fill in a blank country code before sending the address to the |
| + * renderer. |
| + * |
| + * @param profile The profile to check. |
| + * @return Whether the profile is complete. |
| + */ |
| + public boolean isProfileComplete(@Nullable AutofillProfile profile) { |
| + if (profile == null || TextUtils.isEmpty(profile.getFullName()) |
| + || !getPhoneValidator().isValid(profile.getPhoneNumber())) { |
| + return false; |
| + } |
| + |
| + List<Integer> requiredFields = AutofillProfileBridge.getRequiredAddressFields( |
| + AutofillAddress.getCountryCode(profile)); |
| + for (int i = 0; i < requiredFields.size(); i++) { |
| + if (TextUtils.isEmpty(getProfileField(profile, requiredFields.get(i)))) return false; |
| + } |
| + |
| + return true; |
| + } |
| + |
| + /** |
| + * Adds the given phone number to the autocomplete list, if it's valid. |
| + * |
| + * @param phoneNumber The phone number to possibly add. |
| + */ |
| + public void addPhoneNumberIfValid(@Nullable CharSequence phoneNumber) { |
| + if (getPhoneValidator().isValid(phoneNumber)) mAllPhoneNumbers.add(phoneNumber); |
| + } |
| + |
| + @Override |
| + public void edit(@Nullable AutofillAddress toEdit, final Callback<AutofillAddress> callback) { |
| + super.edit(toEdit, callback); |
| + |
| + if (mAutofillProfileBridge == null) mAutofillProfileBridge = new AutofillProfileBridge(); |
| + |
| + final AutofillAddress address = toEdit == null |
| + ? new AutofillAddress(new AutofillProfile(), false) : toEdit; |
| + final AutofillProfile profile = address.getProfile(); |
| + |
| + final EditorModel editor = new EditorModel( |
| + mContext.getString(toEdit == null ? R.string.payments_add_address_label |
| + : R.string.payments_edit_address_label)); |
| + |
| + if (mCountryField == null) { |
| + mCountryField = new EditorFieldModel( |
| + mContext.getString(R.string.autofill_profile_editor_country), |
| + AutofillProfileBridge.getSupportedCountries()); |
| + } |
| + mCountryField.setValue(AutofillAddress.getCountryCode(profile)); |
| + mCountryField.setSpinnerCallback(new Callback<SpinnerChangedEventData>() { |
|
gone
2016/06/28 17:30:28
You really, really need to add comments all over t
please use gerrit instead
2016/06/29 00:28:45
Done.
|
| + @Override |
| + public void onResult(SpinnerChangedEventData eventData) { |
| + editor.removeAllFields(); |
| + editor.addField(mCountryField); |
| + addAddressTextFieldsToEditor( |
| + editor, eventData.key, Locale.getDefault().getLanguage()); |
| + editor.addField(mPhoneField); |
| + mHandler.post(eventData.callback); |
| + } |
| + }); |
| + editor.addField(mCountryField); |
| + |
| + if (mAddressFields.isEmpty()) { |
|
gone
2016/06/28 17:30:28
Adding a bunch of default fields?
please use gerrit instead
2016/06/29 00:28:45
Added a comment:
// There's a finite number of fi
|
| + mAddressFields.put(AddressField.LOCALITY, new EditorFieldModel(0)); |
| + mAddressFields.put(AddressField.DEPENDENT_LOCALITY, new EditorFieldModel(0)); |
| + mAddressFields.put(AddressField.ORGANIZATION, new EditorFieldModel(0)); |
| + mAddressFields.put(AddressField.ADMIN_AREA, |
| + new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_REGION)); |
| + mAddressFields.put(AddressField.SORTING_CODE, |
| + new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_ALPHA_NUMERIC)); |
| + mAddressFields.put(AddressField.POSTAL_CODE, |
| + new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_ALPHA_NUMERIC)); |
| + mAddressFields.put(AddressField.STREET_ADDRESS, |
| + new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_STREET_LINES)); |
| + mAddressFields.put(AddressField.RECIPIENT, |
| + new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_PERSON_NAME)); |
| + } |
| + for (Map.Entry<Integer, EditorFieldModel> entry : mAddressFields.entrySet()) { |
| + entry.getValue().setValue(getProfileField(profile, entry.getKey())); |
| + } |
| + addAddressTextFieldsToEditor(editor, profile.getCountryCode(), profile.getLanguageCode()); |
| + |
| + if (mPhoneField == null) { |
| + mPhoneField = new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_PHONE, |
| + mContext.getString(R.string.autofill_profile_editor_phone_number), |
| + mAllPhoneNumbers, getPhoneValidator(), |
| + mContext.getString(R.string.payments_address_field_required_validation_message), |
| + mContext.getString(R.string.payments_phone_invalid_validation_message), null); |
| + } |
| + mPhoneField.setValue(profile.getPhoneNumber()); |
| + editor.addField(mPhoneField); |
| + |
| + editor.setCancelCallback(new Runnable() { |
| + @Override |
| + public void run() { |
| + callback.onResult(null); |
| + } |
| + }); |
| + |
| + editor.setDoneCallback(new Runnable() { |
| + @Override |
| + public void run() { |
| + profile.setCountryCode(mCountryField.getValue().toString()); |
|
gone
2016/06/28 17:30:28
Pull all this out into a function called "commitCh
please use gerrit instead
2016/06/29 00:28:45
Done.
|
| + profile.setPhoneNumber(mPhoneField.getValue().toString()); |
| + profile.setLanguageCode(mAutofillProfileBridge.getCurrentBestLanguageCode()); |
| + |
| + Set<Integer> visibleFields = new HashSet<>(); |
| + for (int i = 0; i < mAddressUiComponents.size(); i++) { |
| + AddressUiComponent component = mAddressUiComponents.get(i); |
| + visibleFields.add(component.id); |
| + if (component.id != AddressField.COUNTRY) { |
| + setProfileField(profile, component.id, |
| + mAddressFields.get(component.id).getValue()); |
| + } |
| + } |
| + |
| + // Clear the fields that are hidden from the user interface, so |
| + // AutofillAddress.toPaymentAddress() will send them to the renderer as empty |
| + // strings. |
| + for (Map.Entry<Integer, EditorFieldModel> entry : mAddressFields.entrySet()) { |
| + if (!visibleFields.contains(entry.getKey())) { |
| + setProfileField(profile, entry.getKey(), ""); |
| + } |
| + } |
| + |
| + PersonalDataManager pmd = PersonalDataManager.getInstance(); |
| + pmd.setProfile(profile); |
| + profile.setLabel(pmd.getGetAddressLabelForPaymentRequest(profile)); |
| + address.completeAddress(profile); |
| + callback.onResult(address); |
| + } |
| + }); |
| + |
| + mEditorView.show(editor); |
| + } |
| + |
| + private static String getProfileField(AutofillProfile profile, int field) { |
| + assert profile != null; |
| + switch (field) { |
| + case AddressField.COUNTRY: |
| + return profile.getCountryCode(); |
| + case AddressField.ADMIN_AREA: |
| + return profile.getRegion(); |
| + case AddressField.LOCALITY: |
| + return profile.getLocality(); |
| + case AddressField.DEPENDENT_LOCALITY: |
| + return profile.getDependentLocality(); |
| + case AddressField.SORTING_CODE: |
| + return profile.getSortingCode(); |
| + case AddressField.POSTAL_CODE: |
| + return profile.getPostalCode(); |
| + case AddressField.STREET_ADDRESS: |
| + return profile.getStreetAddress(); |
| + case AddressField.ORGANIZATION: |
| + return profile.getCompanyName(); |
| + case AddressField.RECIPIENT: |
| + return profile.getFullName(); |
| + } |
| + |
| + assert false; |
| + return null; |
| + } |
| + |
| + private static void setProfileField( |
| + AutofillProfile profile, int field, @Nullable CharSequence value) { |
| + assert profile != null; |
| + switch (field) { |
| + case AddressField.COUNTRY: |
| + profile.setCountryCode(ensureNotNull(value)); |
| + return; |
| + case AddressField.ADMIN_AREA: |
| + profile.setRegion(ensureNotNull(value)); |
| + return; |
| + case AddressField.LOCALITY: |
| + profile.setLocality(ensureNotNull(value)); |
| + return; |
| + case AddressField.DEPENDENT_LOCALITY: |
| + profile.setDependentLocality(ensureNotNull(value)); |
| + return; |
| + case AddressField.SORTING_CODE: |
| + profile.setSortingCode(ensureNotNull(value)); |
| + return; |
| + case AddressField.POSTAL_CODE: |
| + profile.setPostalCode(ensureNotNull(value)); |
| + return; |
| + case AddressField.STREET_ADDRESS: |
| + profile.setStreetAddress(ensureNotNull(value)); |
| + return; |
| + case AddressField.ORGANIZATION: |
| + profile.setCompanyName(ensureNotNull(value)); |
| + return; |
| + case AddressField.RECIPIENT: |
| + profile.setFullName(ensureNotNull(value)); |
| + return; |
| + } |
| + |
| + assert false; |
| + } |
| + |
| + private static String ensureNotNull(@Nullable CharSequence value) { |
| + return value == null ? "" : value.toString(); |
| + } |
| + |
| + private void addAddressTextFieldsToEditor( |
| + EditorModel container, String countryCode, String languageCode) { |
| + mAddressUiComponents = mAutofillProfileBridge.getAddressUiComponents(countryCode, |
| + languageCode); |
| + |
| + for (int i = 0; i < mAddressUiComponents.size(); i++) { |
| + AddressUiComponent component = mAddressUiComponents.get(i); |
| + if (component.id == AddressField.COUNTRY) continue; |
| + |
| + EditorFieldModel field = mAddressFields.get(component.id); |
| + field.setLabel(component.label); |
| + field.setIsFullLine(component.isFullLine); |
| + |
| + // Libaddressinput formats do not always require the full name (RECIPIENT), but |
| + // PaymentRequest does. |
| + if (component.isRequired || component.id == AddressField.RECIPIENT) { |
| + field.setRequiredErrorMessage(mContext.getString( |
| + R.string.payments_address_field_required_validation_message)); |
| + } else { |
| + field.setRequiredErrorMessage(null); |
| + } |
| + |
| + container.addField(field); |
| + } |
| + } |
| + |
| + private EditorFieldValidator getPhoneValidator() { |
| + if (mPhoneValidator == null) { |
| + mPhoneValidator = new EditorFieldValidator() { |
| + @Override |
| + public boolean isValid(@Nullable CharSequence value) { |
| + return value != null |
| + && PhoneNumberUtils.isGlobalPhoneNumber( |
| + PhoneNumberUtils.stripSeparators(value.toString())); |
| + } |
| + }; |
| + } |
| + return mPhoneValidator; |
| + } |
| +} |