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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java

Issue 2092083003: Refactor contact editor controller into its own class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Match payments UI size even on large screens. 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.payments; 5 package org.chromium.chrome.browser.payments;
6 6
7 import android.app.Activity; 7 import android.app.Activity;
8 import android.graphics.Bitmap; 8 import android.graphics.Bitmap;
9 import android.os.Handler; 9 import android.os.Handler;
10 import android.telephony.PhoneNumberUtils;
11 import android.text.TextUtils; 10 import android.text.TextUtils;
12 import android.util.Patterns;
13 11
14 import org.chromium.base.Callback; 12 import org.chromium.base.Callback;
15 import org.chromium.base.Log; 13 import org.chromium.base.Log;
16 import org.chromium.base.VisibleForTesting; 14 import org.chromium.base.VisibleForTesting;
17 import org.chromium.chrome.R;
18 import org.chromium.chrome.browser.autofill.PersonalDataManager; 15 import org.chromium.chrome.browser.autofill.PersonalDataManager;
19 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; 16 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
20 import org.chromium.chrome.browser.favicon.FaviconHelper; 17 import org.chromium.chrome.browser.favicon.FaviconHelper;
21 import org.chromium.chrome.browser.payments.ui.EditorFieldModel;
22 import org.chromium.chrome.browser.payments.ui.EditorFieldModel.EditorFieldValid ator;
23 import org.chromium.chrome.browser.payments.ui.EditorModel;
24 import org.chromium.chrome.browser.payments.ui.LineItem; 18 import org.chromium.chrome.browser.payments.ui.LineItem;
25 import org.chromium.chrome.browser.payments.ui.PaymentInformation; 19 import org.chromium.chrome.browser.payments.ui.PaymentInformation;
26 import org.chromium.chrome.browser.payments.ui.PaymentOption; 20 import org.chromium.chrome.browser.payments.ui.PaymentOption;
27 import org.chromium.chrome.browser.payments.ui.PaymentRequestUI; 21 import org.chromium.chrome.browser.payments.ui.PaymentRequestUI;
28 import org.chromium.chrome.browser.payments.ui.SectionInformation; 22 import org.chromium.chrome.browser.payments.ui.SectionInformation;
29 import org.chromium.chrome.browser.payments.ui.ShoppingCart; 23 import org.chromium.chrome.browser.payments.ui.ShoppingCart;
30 import org.chromium.chrome.browser.preferences.PreferencesLauncher; 24 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
31 import org.chromium.chrome.browser.preferences.autofill.AutofillCreditCardEditor ; 25 import org.chromium.chrome.browser.preferences.autofill.AutofillCreditCardEditor ;
32 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileEditor; 26 import org.chromium.chrome.browser.preferences.autofill.AutofillProfileEditor;
33 import org.chromium.chrome.browser.profiles.Profile; 27 import org.chromium.chrome.browser.profiles.Profile;
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
128 private SectionInformation mShippingAddressesSection; 122 private SectionInformation mShippingAddressesSection;
129 private SectionInformation mContactSection; 123 private SectionInformation mContactSection;
130 private List<PaymentApp> mPendingApps; 124 private List<PaymentApp> mPendingApps;
131 private List<PaymentInstrument> mPendingInstruments; 125 private List<PaymentInstrument> mPendingInstruments;
132 private SectionInformation mPaymentMethodsSection; 126 private SectionInformation mPaymentMethodsSection;
133 private PaymentRequestUI mUI; 127 private PaymentRequestUI mUI;
134 private Callback<PaymentInformation> mPaymentInformationCallback; 128 private Callback<PaymentInformation> mPaymentInformationCallback;
135 private Pattern mRegionCodePattern; 129 private Pattern mRegionCodePattern;
136 private boolean mMerchantNeedsShippingAddress; 130 private boolean mMerchantNeedsShippingAddress;
137 private boolean mPaymentAppRunning; 131 private boolean mPaymentAppRunning;
138 private boolean mRequestPayerPhone; 132 private ContactEditor mContactEditor;
139 private boolean mRequestPayerEmail;
140 private List<CharSequence> mAllPhoneNumbers;
141 private List<CharSequence> mAllEmailAddresses;
142 private EditorFieldValidator mPhoneValidator;
143 private EditorFieldValidator mEmailValidator;
144 133
145 /** 134 /**
146 * Builds the PaymentRequest service implementation. 135 * Builds the PaymentRequest service implementation.
147 * 136 *
148 * @param webContents The web contents that have invoked the PaymentRequest API. 137 * @param webContents The web contents that have invoked the PaymentRequest API.
149 */ 138 */
150 public PaymentRequestImpl(WebContents webContents) { 139 public PaymentRequestImpl(WebContents webContents) {
151 if (webContents == null) return; 140 if (webContents == null) return;
152 141
153 ContentViewCore contentViewCore = ContentViewCore.fromWebContents(webCon tents); 142 ContentViewCore contentViewCore = ContentViewCore.fromWebContents(webCon tents);
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 } 209 }
221 210
222 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return; 211 if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return;
223 212
224 // If the merchant requests shipping and does not provide a selected shi pping option, then 213 // If the merchant requests shipping and does not provide a selected shi pping option, then
225 // the merchant needs the shipping address to calculate the shipping pri ce and availability. 214 // the merchant needs the shipping address to calculate the shipping pri ce and availability.
226 boolean requestShipping = options != null && options.requestShipping; 215 boolean requestShipping = options != null && options.requestShipping;
227 mMerchantNeedsShippingAddress = 216 mMerchantNeedsShippingAddress =
228 requestShipping && mUiShippingOptions.getSelectedItem() == null; 217 requestShipping && mUiShippingOptions.getSelectedItem() == null;
229 218
230 mRequestPayerPhone = options != null && options.requestPayerPhone; 219 boolean requestPayerPhone = options != null && options.requestPayerPhone ;
231 mRequestPayerEmail = options != null && options.requestPayerEmail; 220 boolean requestPayerEmail = options != null && options.requestPayerEmail ;
221 if (requestPayerPhone || requestPayerEmail) {
222 mContactEditor = new ContactEditor(requestPayerPhone, requestPayerEm ail);
223 }
232 224
233 if (requestShipping || mRequestPayerPhone || mRequestPayerEmail) { 225 if (requestShipping || requestPayerPhone || requestPayerEmail) {
234 List<AutofillProfile> profiles = 226 List<AutofillProfile> profiles =
235 PersonalDataManager.getInstance().getProfilesToSuggest(); 227 PersonalDataManager.getInstance().getProfilesToSuggest();
236 List<AutofillContact> contacts = new ArrayList<>(); 228 List<AutofillContact> contacts = new ArrayList<>();
237 List<AutofillAddress> addresses = new ArrayList<>(); 229 List<AutofillAddress> addresses = new ArrayList<>();
238 mAllPhoneNumbers = new ArrayList<>();
239 mAllEmailAddresses = new ArrayList<>();
240 int firstCompleteContactIndex = SectionInformation.NO_SELECTION; 230 int firstCompleteContactIndex = SectionInformation.NO_SELECTION;
241 for (int i = 0; i < profiles.size(); i++) { 231 for (int i = 0; i < profiles.size(); i++) {
242 AutofillProfile profile = profiles.get(i); 232 AutofillProfile profile = profiles.get(i);
243 233
244 String phone = mRequestPayerPhone && !TextUtils.isEmpty(profile. getPhoneNumber()) 234 String phone = requestPayerPhone && !TextUtils.isEmpty(profile.g etPhoneNumber())
245 ? profile.getPhoneNumber() : null; 235 ? profile.getPhoneNumber() : null;
246 String email = mRequestPayerEmail && !TextUtils.isEmpty(profile. getEmailAddress()) 236 String email = requestPayerEmail && !TextUtils.isEmpty(profile.g etEmailAddress())
247 ? profile.getEmailAddress() : null; 237 ? profile.getEmailAddress() : null;
248 if (phone != null || email != null) { 238 if (phone != null || email != null) {
249 boolean isComplete = isContactInformationComplete(phone, ema il); 239 boolean isComplete = mContactEditor.isContactInformationComp lete(phone, email);
250 contacts.add(new AutofillContact(profile, phone, email, isCo mplete)); 240 contacts.add(new AutofillContact(profile, phone, email, isCo mplete));
251 if (isComplete && firstCompleteContactIndex < 0) firstComple teContactIndex = i; 241 if (isComplete && firstCompleteContactIndex < 0) firstComple teContactIndex = i;
252 if (getPhoneValidator().isValid(phone)) mAllPhoneNumbers.add (phone); 242 mContactEditor.addPhoneNumberIfValid(phone);
253 if (getEmailValidator().isValid(email)) mAllEmailAddresses.a dd(email); 243 mContactEditor.addEmailAddressIfValid(email);
254 } 244 }
255 245
256 if (canUseAddress(profile, requestShipping)) { 246 if (canUseAddress(profile, requestShipping)) {
257 addresses.add(new AutofillAddress(profile)); 247 addresses.add(new AutofillAddress(profile));
258 } 248 }
259 } 249 }
260 250
261 if (requestShipping) { 251 if (requestShipping) {
262 int selectedIndex = SectionInformation.NO_SELECTION; 252 int selectedIndex = SectionInformation.NO_SELECTION;
263 if (!addresses.isEmpty() && mUiShippingOptions.getSelectedItem() != null) { 253 if (!addresses.isEmpty() && mUiShippingOptions.getSelectedItem() != null) {
264 selectedIndex = 0; 254 selectedIndex = 0;
265 } 255 }
266 mShippingAddressesSection = new SectionInformation( 256 mShippingAddressesSection = new SectionInformation(
267 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, addresses); 257 PaymentRequestUI.TYPE_SHIPPING_ADDRESSES, selectedIndex, addresses);
268 } 258 }
269 259
270 // The contact section automatically selects the first complete entr y. 260 // The contact section automatically selects the first complete entr y.
271 if (mRequestPayerPhone || mRequestPayerEmail) { 261 if (requestPayerPhone || requestPayerEmail) {
272 mContactSection = new SectionInformation( 262 mContactSection = new SectionInformation(
273 PaymentRequestUI.TYPE_CONTACT_DETAILS, firstCompleteCont actIndex, contacts); 263 PaymentRequestUI.TYPE_CONTACT_DETAILS, firstCompleteCont actIndex, contacts);
274 } 264 }
275 } 265 }
276 266
277 mPendingApps = new ArrayList<>(mApps); 267 mPendingApps = new ArrayList<>(mApps);
278 mPendingInstruments = new ArrayList<>(); 268 mPendingInstruments = new ArrayList<>();
279 boolean isGettingInstruments = false; 269 boolean isGettingInstruments = false;
280 270
281 for (int i = 0; i < mApps.size(); i++) { 271 for (int i = 0; i < mApps.size(); i++) {
282 PaymentApp app = mApps.get(i); 272 PaymentApp app = mApps.get(i);
283 Set<String> appMethods = app.getSupportedMethodNames(); 273 Set<String> appMethods = app.getSupportedMethodNames();
284 appMethods.retainAll(mMethodData.keySet()); 274 appMethods.retainAll(mMethodData.keySet());
285 if (appMethods.isEmpty()) { 275 if (appMethods.isEmpty()) {
286 mPendingApps.remove(app); 276 mPendingApps.remove(app);
287 } else { 277 } else {
288 isGettingInstruments = true; 278 isGettingInstruments = true;
289 app.getInstruments(mRawTotal, mRawLineItems, this); 279 app.getInstruments(mRawTotal, mRawLineItems, this);
290 } 280 }
291 } 281 }
292 282
293 if (!isGettingInstruments) { 283 if (!isGettingInstruments) {
294 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS); 284 mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYP E_PAYMENT_METHODS);
295 } 285 }
296 286
297 mUI = new PaymentRequestUI(mContext, this, requestShipping, 287 mUI = new PaymentRequestUI(mContext, this, requestShipping,
298 mRequestPayerPhone || mRequestPayerEmail, mMerchantName, mOrigin ); 288 requestPayerPhone || requestPayerEmail, mMerchantName, mOrigin);
289
299 if (mFavicon != null) mUI.setTitleBitmap(mFavicon); 290 if (mFavicon != null) mUI.setTitleBitmap(mFavicon);
300 mFavicon = null; 291 mFavicon = null;
292
293 if (mContactEditor != null) mContactEditor.setEditorView(mUI.getEditorVi ew());
301 } 294 }
302 295
303 private static HashMap<String, JSONObject> getValidatedMethodData( 296 private static HashMap<String, JSONObject> getValidatedMethodData(
304 PaymentMethodData[] methodData) { 297 PaymentMethodData[] methodData) {
305 // Payment methodData are required. 298 // Payment methodData are required.
306 if (methodData == null || methodData.length == 0) return null; 299 if (methodData == null || methodData.length == 0) return null;
307 HashMap<String, JSONObject> result = new HashMap<>(); 300 HashMap<String, JSONObject> result = new HashMap<>();
308 for (int i = 0; i < methodData.length; i++) { 301 for (int i = 0; i < methodData.length; i++) {
309 JSONObject data = null; 302 JSONObject data = null;
310 if (!TextUtils.isEmpty(methodData[i].stringifiedData)) { 303 if (!TextUtils.isEmpty(methodData[i].stringifiedData)) {
(...skipping 17 matching lines...) Expand all
328 321
329 for (int j = 0; j < methods.length; j++) { 322 for (int j = 0; j < methods.length; j++) {
330 // Payment methods should be non-empty. 323 // Payment methods should be non-empty.
331 if (TextUtils.isEmpty(methods[j])) return null; 324 if (TextUtils.isEmpty(methods[j])) return null;
332 result.put(methods[j], data); 325 result.put(methods[j], data);
333 } 326 }
334 } 327 }
335 return result; 328 return result;
336 } 329 }
337 330
338 private boolean isContactInformationComplete(String phone, String email) {
339 return (!mRequestPayerPhone || getPhoneValidator().isValid(phone))
340 && (!mRequestPayerEmail || getEmailValidator().isValid(email));
341 }
342
343 private boolean canUseAddress(AutofillProfile profile, boolean requestShippi ng) { 331 private boolean canUseAddress(AutofillProfile profile, boolean requestShippi ng) {
344 return requestShipping && profile.getCountryCode() != null 332 return requestShipping && profile.getCountryCode() != null
345 && mRegionCodePattern.matcher(profile.getCountryCode()).matches( ) 333 && mRegionCodePattern.matcher(profile.getCountryCode()).matches( )
346 && profile.getStreetAddress() != null && profile.getRegion() != null 334 && profile.getStreetAddress() != null && profile.getRegion() != null
347 && profile.getLocality() != null && profile.getDependentLocality () != null 335 && profile.getLocality() != null && profile.getDependentLocality () != null
348 && profile.getPostalCode() != null && profile.getSortingCode() ! = null 336 && profile.getPostalCode() != null && profile.getSortingCode() ! = null
349 && profile.getCompanyName() != null && profile.getFullName() != null 337 && profile.getCompanyName() != null && profile.getFullName() != null
350 && profile.getPhoneNumber() != null; 338 && profile.getPhoneNumber() != null;
351 } 339 }
352 340
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 } else { 602 } else {
615 editContact(contact); 603 editContact(contact);
616 } 604 }
617 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) { 605 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) {
618 assert option instanceof PaymentInstrument; 606 assert option instanceof PaymentInstrument;
619 mPaymentMethodsSection.setSelectedItem(option); 607 mPaymentMethodsSection.setSelectedItem(option);
620 } 608 }
621 return false; 609 return false;
622 } 610 }
623 611
624 private void editContact(final AutofillContact contact) {
625 final EditorFieldModel phoneField = mRequestPayerPhone
626 ? new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_PHONE,
627 mContext.getString(R.string.autofill_profile_editor_ph one_number),
628 mAllPhoneNumbers, getPhoneValidator(),
629 mContext.getString(R.string.payments_phone_required_va lidation_message),
630 mContext.getString(R.string.payments_phone_invalid_val idation_message),
631 contact == null ? null : contact.getPayerPhone())
632 : null;
633
634 final EditorFieldModel emailField = mRequestPayerEmail
635 ? new EditorFieldModel(EditorFieldModel.INPUT_TYPE_HINT_EMAIL,
636 mContext.getString(R.string.autofill_profile_editor_em ail_address),
637 mAllEmailAddresses, getEmailValidator(),
638 mContext.getString(R.string.payments_email_required_va lidation_message),
639 mContext.getString(R.string.payments_email_invalid_val idation_message),
640 contact == null ? null : contact.getPayerEmail())
641 : null;
642
643 EditorModel editor =
644 new EditorModel(mContext.getString(R.string.payments_add_contact _details_label));
645 if (phoneField != null) editor.addField(phoneField);
646 if (emailField != null) editor.addField(emailField);
647
648 editor.setCancelCallback(new Runnable() {
649 @Override
650 public void run() {
651 mContactSection.setSelectedItemIndex(SectionInformation.NO_SELEC TION);
652 mUI.updateSection(PaymentRequestUI.TYPE_CONTACT_DETAILS, mContac tSection);
653 }
654 });
655
656 editor.setDoneCallback(new Runnable() {
657 @Override
658 public void run() {
659 AutofillProfile profile =
660 contact != null ? contact.getProfile() : new AutofillPro file();
661 String phone = null;
662 String email = null;
663 if (phoneField != null) {
664 phone = phoneField.getValue().toString();
665 profile.setPhoneNumber(phone);
666 }
667 if (emailField != null) {
668 email = emailField.getValue().toString();
669 profile.setEmailAddress(email);
670 }
671 PersonalDataManager.getInstance().setProfile(profile);
672
673 if (contact == null) {
674 mContactSection.addAndSelectItem(
675 new AutofillContact(profile, phone, email, true));
676 } else {
677 contact.completeContact(phone, email);
678 }
679 mUI.updateSection(PaymentRequestUI.TYPE_CONTACT_DETAILS, mContac tSection);
680 }
681 });
682 mUI.showEditor(editor);
683 }
684
685 private EditorFieldValidator getPhoneValidator() {
686 if (mPhoneValidator == null) {
687 mPhoneValidator = new EditorFieldValidator() {
688 @Override
689 public boolean isValid(CharSequence value) {
690 return value != null
691 && PhoneNumberUtils.isGlobalPhoneNumber(
692 PhoneNumberUtils.stripSeparators(value.to String()));
693 }
694 };
695 }
696 return mPhoneValidator;
697 }
698
699 private EditorFieldValidator getEmailValidator() {
700 if (mEmailValidator == null) {
701 mEmailValidator = new EditorFieldValidator() {
702 @Override
703 public boolean isValid(CharSequence value) {
704 return value != null && Patterns.EMAIL_ADDRESS.matcher(value ).matches();
705 }
706 };
707 }
708 return mEmailValidator;
709 }
710
711 @Override 612 @Override
712 public void onSectionAddOption(@PaymentRequestUI.DataType int optionType) { 613 public void onSectionAddOption(@PaymentRequestUI.DataType int optionType) {
713 if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) { 614 if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) {
714 PreferencesLauncher.launchSettingsPage(mContext, AutofillProfileEdit or.class.getName()); 615 PreferencesLauncher.launchSettingsPage(mContext, AutofillProfileEdit or.class.getName());
715 } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) { 616 } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) {
716 editContact(null); 617 editContact(null);
717 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) { 618 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) {
718 PreferencesLauncher.launchSettingsPage( 619 PreferencesLauncher.launchSettingsPage(
719 mContext, AutofillCreditCardEditor.class.getName()); 620 mContext, AutofillCreditCardEditor.class.getName());
720 } 621 }
721 } 622 }
722 623
624 private void editContact(final AutofillContact toEdit) {
625 mContactEditor.editContact(toEdit, new Callback<AutofillContact>() {
626 @Override
627 public void onResult(AutofillContact completeContact) {
628 if (completeContact == null) {
629 mContactSection.setSelectedItemIndex(SectionInformation.NO_S ELECTION);
630 } else if (toEdit == null) {
631 mContactSection.addAndSelectItem(completeContact);
632 }
633
634 mUI.updateSection(PaymentRequestUI.TYPE_CONTACT_DETAILS, mContac tSection);
635 }
636 });
637 }
638
723 @Override 639 @Override
724 public void onPayClicked(PaymentOption selectedShippingAddress, 640 public void onPayClicked(PaymentOption selectedShippingAddress,
725 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) { 641 PaymentOption selectedShippingOption, PaymentOption selectedPaymentM ethod) {
726 assert selectedPaymentMethod instanceof PaymentInstrument; 642 assert selectedPaymentMethod instanceof PaymentInstrument;
727 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ; 643 PaymentInstrument instrument = (PaymentInstrument) selectedPaymentMethod ;
728 mPaymentAppRunning = true; 644 mPaymentAppRunning = true;
729 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems, 645 instrument.getDetails(mMerchantName, mOrigin, mRawTotal, mRawLineItems,
730 mMethodData.get(instrument.getMethodName()), this); 646 mMethodData.get(instrument.getMethodName()), this);
731 } 647 }
732 648
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
891 private void closeClient() { 807 private void closeClient() {
892 if (mClient != null) mClient.close(); 808 if (mClient != null) mClient.close();
893 mClient = null; 809 mClient = null;
894 } 810 }
895 811
896 @VisibleForTesting 812 @VisibleForTesting
897 public static void setObserverForTest(PaymentRequestServiceObserverForTest o bserverForTest) { 813 public static void setObserverForTest(PaymentRequestServiceObserverForTest o bserverForTest) {
898 sObserverForTest = observerForTest; 814 sObserverForTest = observerForTest;
899 } 815 }
900 } 816 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698