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; |
+ } |
+} |