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 |
| index df376273c960b848be28bb620aacca3b31d2897e..e123a34bed3d8552be1885d64e94a4fe3f50c3f0 100644 |
| --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AddressEditor.java |
| @@ -11,6 +11,7 @@ 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.GetSubKeysRequestDelegate; |
| import org.chromium.chrome.browser.autofill.PhoneNumberUtil; |
| import org.chromium.chrome.browser.payments.ui.EditorFieldModel; |
| import org.chromium.chrome.browser.payments.ui.EditorFieldModel.EditorFieldValidator; |
| @@ -31,16 +32,27 @@ import javax.annotation.Nullable; |
| /** |
| * An address editor. Can be used for either shipping or billing address editing. |
| */ |
| -public class AddressEditor extends EditorBase<AutofillAddress> { |
| +public class AddressEditor |
| + extends EditorBase<AutofillAddress> implements GetSubKeysRequestDelegate { |
| private final Handler mHandler = new Handler(); |
| private final Map<Integer, EditorFieldModel> mAddressFields = new HashMap<>(); |
| private final Set<CharSequence> mPhoneNumbers = new HashSet<>(); |
| - @Nullable private AutofillProfileBridge mAutofillProfileBridge; |
| - @Nullable private EditorFieldModel mCountryField; |
| - @Nullable private EditorFieldModel mPhoneField; |
| - @Nullable private EditorFieldValidator mPhoneValidator; |
| - @Nullable private List<AddressUiComponent> mAddressUiComponents; |
| - |
| + @Nullable |
| + private AutofillProfileBridge mAutofillProfileBridge; |
| + @Nullable |
| + private EditorFieldModel mCountryField; |
| + @Nullable |
| + private EditorFieldModel mPhoneField; |
| + @Nullable |
| + private EditorFieldValidator mPhoneValidator; |
| + @Nullable |
| + private List<AddressUiComponent> mAddressUiComponents; |
| + |
| + private boolean mAdminAreasLoaded; |
| + private String mRecentlySelectedCountry; |
| + private Runnable mCountryChangeCallback; |
| + private AutofillProfile mProfile; |
| + private EditorModel mEditor; |
| /** |
|
please use gerrit instead
2017/03/31 16:06:20
Need a newline between "private..." and "/**" line
Parastoo
2017/04/03 16:18:23
Done.
|
| * Adds the given phone number to the autocomplete set, if it's valid. |
| * |
| @@ -72,17 +84,20 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| // default locale on this device. |
| boolean isNewAddress = toEdit == null; |
| - // Ensure that |address| and |profile| are always not null. |
| + // Ensure that |address| and |mProfile| are always not null. |
| final AutofillAddress address = |
| isNewAddress ? new AutofillAddress(mContext, new AutofillProfile()) : toEdit; |
| - final AutofillProfile profile = address.getProfile(); |
| + mProfile = address.getProfile(); |
| // The title of the editor depends on whether we're adding a new address or editing an |
| // existing address. |
| - final EditorModel editor = |
| - new EditorModel(isNewAddress |
| - ? mContext.getString(R.string.autofill_create_profile) |
| - : toEdit.getEditTitle()); |
| + mEditor = |
| + new EditorModel(isNewAddress ? mContext.getString(R.string.autofill_create_profile) |
| + : toEdit.getEditTitle()); |
| + |
| + // When edit is called, a new form is started, so the country on the |
| + // drop-down list is not changed. => mRecentlySelectedCountry should be null. |
| + mRecentlySelectedCountry = null; |
| // The country dropdown is always present on the editor. |
| if (mCountryField == null) { |
| @@ -96,23 +111,23 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| // discarded, so their contents are preserved. |
| mCountryField.setDropdownCallback(new Callback<Pair<String, Runnable>>() { |
| @Override |
| + /* |
| + * If the selected country on the country dropdown list is changed, |
| + * the first element of eventData is the recently selected dropdown key, |
| + * the second element is the callback to invoke for when the dropdown |
| + * change has been processed. |
| + */ |
| public void onResult(Pair<String, Runnable> eventData) { |
| - editor.removeAllFields(); |
| - editor.addField(mCountryField); |
| - addAddressTextFieldsToEditor(editor, eventData.first, |
| - Locale.getDefault().getLanguage()); |
| - editor.addField(mPhoneField); |
| - |
| - // Notify EditorView that the fields in the model have changed. EditorView should |
| - // re-read the model and update the UI accordingly. |
| - mHandler.post(eventData.second); |
| + mEditor.removeAllFields(); |
| + mRecentlySelectedCountry = eventData.first; |
| + mCountryChangeCallback = eventData.second; |
| + loadAdminAreasForCountry(mRecentlySelectedCountry); |
| } |
| }); |
| // Country dropdown is cached, so the selected item needs to be updated for the new profile |
| // that's being edited. This will not fire the dropdown callback. |
| - mCountryField.setValue(AutofillAddress.getCountryCode(profile)); |
| - editor.addField(mCountryField); |
| + mCountryField.setValue(AutofillAddress.getCountryCode(mProfile)); |
| // There's a finite number of fields for address editing. Changing the country will re-order |
| // and relabel the fields. The meaning of each field remains the same. |
| @@ -122,10 +137,6 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| mAddressFields.put(AddressField.DEPENDENT_LOCALITY, EditorFieldModel.createTextInput()); |
| mAddressFields.put(AddressField.ORGANIZATION, EditorFieldModel.createTextInput()); |
| - // State should be formatted in all capitals. |
| - mAddressFields.put(AddressField.ADMIN_AREA, EditorFieldModel.createTextInput( |
| - EditorFieldModel.INPUT_TYPE_HINT_REGION)); |
| - |
| // Sorting code and postal code (a.k.a. ZIP code) should show both letters and digits on |
| // the keyboard, if possible. |
| mAddressFields.put(AddressField.SORTING_CODE, EditorFieldModel.createTextInput( |
| @@ -142,17 +153,6 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| EditorFieldModel.INPUT_TYPE_HINT_PERSON_NAME)); |
| } |
| - // Address fields are cached, so their values need to be updated for every new profile |
| - // that's being edited. |
| - for (Map.Entry<Integer, EditorFieldModel> entry : mAddressFields.entrySet()) { |
| - entry.getValue().setValue(AutofillAddress.getProfileField(profile, entry.getKey())); |
| - } |
| - |
| - // Both country code and language code dictate which fields should be added to the editor. |
| - // For example, "US" will not add dependent locality to the editor. A "JP" address will |
| - // start with a person's full name or a with a prefecture name, depending on whether the |
| - // language code is "ja-Latn" or "ja". |
| - addAddressTextFieldsToEditor(editor, profile.getCountryCode(), profile.getLanguageCode()); |
| // Phone number is present and required for all countries. |
| if (mPhoneField == null) { |
| @@ -165,30 +165,35 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| // Phone number field is cached, so its value needs to be updated for every new profile |
| // that's being edited. |
| - mPhoneField.setValue(profile.getPhoneNumber()); |
| - editor.addField(mPhoneField); |
| + mPhoneField.setValue(mProfile.getPhoneNumber()); |
| // If the user clicks [Cancel], send |toEdit| address back to the caller, which was the |
| // original state (could be null, a complete address, a partial address). |
| - editor.setCancelCallback(new Runnable() { |
| + mEditor.setCancelCallback(new Runnable() { |
| @Override |
| public void run() { |
| + /* This makes sure that onSubKeysReceived returns early if it's |
| + ever called when Cancel has already occurred.*/ |
| + mAdminAreasLoaded = true; |
| + PersonalDataManager.getInstance().cancelPendingGetSubKeys(); |
| callback.onResult(toEdit); |
| } |
| }); |
| // If the user clicks [Done], save changes on disk, mark the address "complete," and send it |
| // back to the caller. |
| - editor.setDoneCallback(new Runnable() { |
| + mEditor.setDoneCallback(new Runnable() { |
| @Override |
| public void run() { |
| - commitChanges(profile); |
| - address.completeAddress(profile); |
| + mAdminAreasLoaded = true; |
| + PersonalDataManager.getInstance().cancelPendingGetSubKeys(); |
| + commitChanges(mProfile); |
| + address.completeAddress(mProfile); |
| callback.onResult(address); |
| } |
| }); |
| - mEditorView.show(editor); |
| + loadAdminAreasForCountry(mProfile.getCountryCode()); |
| } |
| /** Saves the edited profile on disk. */ |
| @@ -220,7 +225,7 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| } |
| // Save the edited autofill profile locally. |
| - profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(profile)); |
| + profile.setGUID(PersonalDataManager.getInstance().setProfileToLocal(mProfile)); |
| profile.setIsLocal(true); |
| } |
| @@ -265,27 +270,109 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| return value == null ? "" : value.toString(); |
| } |
| + private void setAddressFieldValuesFromCache() { |
| + // Address fields are cached, so their values need to be updated for every new profile |
| + // that's being edited. |
| + for (Map.Entry<Integer, EditorFieldModel> entry : mAddressFields.entrySet()) { |
| + entry.getValue().setValue(AutofillAddress.getProfileField(mProfile, entry.getKey())); |
| + } |
| + } |
| + |
| + /* Callback of the sub keys request that is called when the sub keys are loaded. |
| + Here the sub keys are admin areas (sub keys of the region=country.) */ |
| + @Override |
| + public void onSubKeysReceived(String[] adminAreas) { |
| + if (mAdminAreasLoaded) return; |
| + mAdminAreasLoaded = true; |
| + |
| + // If can't get admin areas from server or there is no admin area on the |
| + // server, then use text field. Otherwise, use drop down list. |
| + if (adminAreas == null || adminAreas.length == 0) { |
| + mAddressFields.put(AddressField.ADMIN_AREA, EditorFieldModel.createTextInput()); |
| + } else { |
| + mAddressFields.put(AddressField.ADMIN_AREA, EditorFieldModel.createDropdown()); |
| + } |
| + |
| + /* Admin Areas need to be fetched in two cases. |
| + 1. Initial loading of the form. |
| + 2. When the selected country is changed in the form. |
| + mRecentlySelectedCountry is not null <=> the 2nd case */ |
| + if (mRecentlySelectedCountry != null) { |
| + // Both country code and language code dictate which fields should be added to the |
| + // editor. |
| + // For example, "US" will not add dependent locality to the editor. A "JP" address will |
| + // start with a person's full name or a with a prefecture name, depending on whether the |
| + // language code is "ja-Latn" or "ja". |
| + addAddressFieldsToEditor(mRecentlySelectedCountry, |
| + Locale.getDefault().getLanguage(), |
| + adminAreas); |
| + // Notify EditorView that the fields in the model have changed. EditorView should |
| + // re-read the model and update the UI accordingly. |
| + mHandler.post(mCountryChangeCallback); |
| + } else { |
| + // This should be called when all required fields are put in mAddressField. |
| + setAddressFieldValuesFromCache(); |
| + addAddressFieldsToEditor(mProfile.getCountryCode(), |
| + mProfile.getLanguageCode(), adminAreas); |
| + mEditorView.show(mEditor); |
| + } |
| + } |
| + |
| + /** Requests the list of admin areas. */ |
| + private void loadAdminAreasForCountry(String countryCode) { |
| + // Used to check if the callback is called (for time-out). |
| + mAdminAreasLoaded = false; |
| + |
| + // In each rule, admin area keys are saved under sub keys of country. |
| + PersonalDataManager.getInstance().loadRulesForSubKeys(countryCode); |
| + PersonalDataManager.getInstance().getRegionSubKeys(countryCode, this); |
| + onAdminAreasLoading(); |
| + } |
| + |
| + /* In case the the admin areas are not loaded yet, starts a timer to cancel the request. */ |
| + public void onAdminAreasLoading() { |
| + if (!mAdminAreasLoaded) { |
| + if (PersonalDataManager.getInstance().getRequestTimeoutMS() == 0) { |
| + onSubKeysReceived(null); |
| + PersonalDataManager.getInstance().cancelPendingGetSubKeys(); |
| + } else { |
| + new Handler().postDelayed(new Runnable() { |
| + @Override |
| + public void run() { |
| + if (!mAdminAreasLoaded) { |
| + onSubKeysReceived(null); |
| + PersonalDataManager.getInstance().cancelPendingGetSubKeys(); |
| + } |
| + } |
| + }, PersonalDataManager.getInstance().getRequestTimeoutMS()); |
| + } |
| + } |
| + } |
| + |
| /** |
| - * Adds text fields to the editor model based on the country and language code of the profile |
| - * that's being edited. |
| + * Adds fields to the editor model based on the country and language code of |
| + * the profile that's being edited. |
| */ |
| - private void addAddressTextFieldsToEditor( |
| - EditorModel container, String countryCode, String languageCode) { |
| - mAddressUiComponents = mAutofillProfileBridge.getAddressUiComponents(countryCode, |
| - languageCode); |
| - |
| + private void addAddressFieldsToEditor( |
| + String countryCode, String languageCode, String[] adminAreas) { |
| + mAddressUiComponents = |
| + mAutofillProfileBridge.getAddressUiComponents(countryCode, languageCode); |
| + // In terms of order, country must be the first field. |
| + mEditor.addField(mCountryField); |
| for (int i = 0; i < mAddressUiComponents.size(); i++) { |
| AddressUiComponent component = mAddressUiComponents.get(i); |
| - // The country field is a dropdown, so there's no need to add a text field for it. |
| - if (component.id == AddressField.COUNTRY) continue; |
| - |
| EditorFieldModel field = mAddressFields.get(component.id); |
| // Labels depend on country, e.g., state is called province in some countries. These are |
| // already localized. |
| field.setLabel(component.label); |
| field.setIsFullLine(component.isFullLine); |
| + if (component.id == AddressField.ADMIN_AREA && field.isDropdownField()) { |
| + field.setDropdownKeyValues( |
| + mAutofillProfileBridge.getAdminAreaDropdownList(adminAreas)); |
| + } |
| + |
| // Libaddressinput formats do not always require the full name (RECIPIENT), but |
| // PaymentRequest does. |
| if (component.isRequired || component.id == AddressField.RECIPIENT) { |
| @@ -294,9 +381,10 @@ public class AddressEditor extends EditorBase<AutofillAddress> { |
| } else { |
| field.setRequiredErrorMessage(null); |
| } |
| - |
| - container.addField(field); |
| + mEditor.addField(field); |
| } |
| + // Phone number must be the last field. |
| + mEditor.addField(mPhoneField); |
| } |
| private EditorFieldValidator getPhoneValidator() { |