Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java |
| index e4984546129ee9ba4209bfd06db6886d13701ee6..47aefa8ff9cc0a6ef9693fcc0d679dd6421b07ef 100644 |
| --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestUI.java |
| @@ -16,18 +16,34 @@ import android.content.Context; |
| import android.content.DialogInterface; |
| import android.graphics.Bitmap; |
| import android.graphics.Color; |
| +import android.graphics.ColorFilter; |
| +import android.graphics.PorterDuff; |
| +import android.graphics.PorterDuffColorFilter; |
| import android.graphics.drawable.ColorDrawable; |
| +import android.os.AsyncTask; |
| +import android.os.Build; |
| +import android.os.Handler; |
| import android.support.annotation.IntDef; |
| import android.support.v4.view.animation.FastOutLinearInInterpolator; |
| import android.support.v4.view.animation.LinearOutSlowInInterpolator; |
| -import android.text.TextUtils.TruncateAt; |
| +import android.telephony.PhoneNumberFormattingTextWatcher; |
| +import android.text.Editable; |
| +import android.text.InputType; |
| +import android.text.TextUtils; |
| +import android.text.TextWatcher; |
| import android.view.Gravity; |
| +import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnLayoutChangeListener; |
| import android.view.ViewGroup; |
| import android.view.ViewGroup.LayoutParams; |
| import android.view.Window; |
| +import android.view.accessibility.AccessibilityEvent; |
| +import android.view.inputmethod.EditorInfo; |
| +import android.view.inputmethod.InputMethodManager; |
| +import android.widget.ArrayAdapter; |
| +import android.widget.AutoCompleteTextView; |
| import android.widget.Button; |
| import android.widget.FrameLayout; |
| import android.widget.ImageView; |
| @@ -160,6 +176,22 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| void onPaymentRequestReadyToPay(PaymentRequestUI ui); |
| /** |
| + * Called when edit dialog is showing. |
|
gone
2016/06/21 06:04:39
the Edit dialog? Add the in more places?
please use gerrit instead
2016/06/22 02:12:30
Done.
|
| + */ |
| + void onPaymentRequestReadyToEdit(PaymentRequestUI ui); |
| + |
| + /** |
| + * Called when editor validation completes with error. This can happen, for example, when |
| + * user enters an invalid email address. |
| + */ |
| + void onPaymentRequestEditorValidationError(PaymentRequestUI ui); |
| + |
| + /** |
| + * Called when editor is dismissed. |
| + */ |
| + void onPaymentRequestEditorDismissed(PaymentRequestUI ui); |
| + |
| + /** |
| * Called when the result UI is showing. |
| */ |
| void onPaymentRequestResultReady(PaymentRequestUI ui); |
| @@ -170,6 +202,9 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| void onPaymentRequestDismiss(); |
| } |
| + /** The indicator for input fields that are required. */ |
| + private static final String REQUIRED_FIELD_INDICATOR = "*"; |
| + |
| /** Length of the animation to either show the UI or expand it to full height. */ |
| private static final int DIALOG_ENTER_ANIMATION_MS = 225; |
| @@ -184,6 +219,8 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| private final boolean mRequestContactDetails; |
| private final Dialog mDialog; |
| + private final Dialog mEditDialog; |
| + private final Handler mHandler = new Handler(); |
| private final ViewGroup mFullContainer; |
| private final ViewGroup mRequestView; |
| private final PaymentResultUIManager mResultView; |
| @@ -194,6 +231,8 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| private Button mEditButton; |
| private Button mPayButton; |
| private View mCloseButton; |
| + private AutoCompleteTextView mPhoneInput; |
| + private PhoneNumberFormattingTextWatcher mPhoneFormatter; |
| private LineItemBreakdownSection mOrderSummarySection; |
| private ExtraTextSection mShippingSummarySection; |
| @@ -262,6 +301,8 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| bottomSheetParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; |
| mFullContainer.addView(mRequestView, bottomSheetParams); |
| + mEditDialog = new AlwaysDismissedDialog(activity, R.style.DialogWhenLarge); |
| + |
| // Set up the dialog. |
| mDialog = new AlwaysDismissedDialog(activity, R.style.DialogWhenLarge); |
| mDialog.setOnDismissListener(this); |
| @@ -297,7 +338,7 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| R.string.payments_select_shipping_address_prompt) |
| : selectedShippingAddress, selectedShippingName); |
| mShippingSummarySection.setSummaryProperties( |
| - TruncateAt.MIDDLE, true, null, true); |
| + TextUtils.TruncateAt.MIDDLE, true, null, true); |
| // Indicate the shipping option below the address. |
| mShippingSummarySection.setExtraText(selectedShippingOptionLabel == null |
| @@ -547,6 +588,187 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| } |
| /** |
| + * Displays the editor user interface for the given model. |
|
gone
2016/06/21 06:04:39
Everything new you've added that is editor related
please use gerrit instead
2016/06/22 02:12:31
Done.
|
| + * |
| + * @param editorModel The description of the editor user interface to display. |
| + */ |
| + public void showEditor(final EditorModel editorModel) { |
| + final LinearLayout editor = new LinearLayout(mContext); |
| + editor.setOrientation(LinearLayout.VERTICAL); |
| + TextView title = new TextView(mContext); |
| + title.setText(editorModel.getTitle()); |
| + editor.addView(title); |
| + |
| + final Button done = new Button(mContext); |
|
gone
2016/06/21 06:04:39
Rearrange this code so that the button initializin
please use gerrit instead
2016/06/22 02:12:30
Done.
|
| + for (int i = 0; i < editorModel.getFields().size(); i++) { |
| + final AutoCompleteTextView input = new AutoCompleteTextView(mContext); |
| + final EditorFieldModel fieldModel = editorModel.getFields().get(i); |
| + input.setTag(fieldModel); |
| + input.setHint(fieldModel.getLabel() + REQUIRED_FIELD_INDICATOR); |
| + input.setText(fieldModel.getValue()); |
| + if (fieldModel.getSuggestions() != null && !fieldModel.getSuggestions().isEmpty()) { |
| + input.setAdapter(new ArrayAdapter<CharSequence>(mContext, |
| + android.R.layout.simple_spinner_dropdown_item, |
| + fieldModel.getSuggestions())); |
| + input.setThreshold(0); |
| + } |
|
gone
2016/06/21 06:04:39
newlines between the blocks
please use gerrit instead
2016/06/22 02:12:30
Done.
|
| + if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_PHONE) { |
| + input.setId(R.id.payments_edit_phone_input); |
| + input.setInputType(InputType.TYPE_CLASS_PHONE); |
| + if (mPhoneFormatter == null) { |
| + mPhoneInput = input; |
| + new PhoneFormatterTask().execute(); |
| + } else { |
| + input.addTextChangedListener(mPhoneFormatter); |
| + } |
| + } else if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_EMAIL) { |
| + input.setId(R.id.payments_edit_email_input); |
| + input.setInputType( |
| + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); |
| + } |
| + input.addTextChangedListener(new TextWatcher() { |
| + @Override |
| + public void afterTextChanged(Editable s) { |
| + fieldModel.setValue(s.toString()); |
| + input.getBackground().mutate().setColorFilter(null); |
| + input.setError(null); |
| + } |
| + @Override |
| + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} |
| + @Override |
| + public void onTextChanged(CharSequence s, int start, int before, int count) {} |
| + }); |
| + input.setOnEditorActionListener(new TextView.OnEditorActionListener() { |
|
gone
2016/06/21 06:04:39
This listener looks to be the same for all element
please use gerrit instead
2016/06/22 02:12:30
Done.
|
| + @Override |
| + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { |
| + if (actionId == EditorInfo.IME_ACTION_DONE) { |
| + done.performClick(); |
| + return true; |
| + } else if (actionId == EditorInfo.IME_ACTION_NEXT) { |
| + View next = input.focusSearch(View.FOCUS_FORWARD); |
| + if (next != null && next instanceof AutoCompleteTextView) { |
| + next.requestFocus(); |
| + return true; |
| + } |
| + } |
| + return false; |
| + } |
| + }); |
| + editor.addView(input); |
| + } |
| + |
| + TextView explanation = new TextView(mContext); |
| + explanation.setText(mContext.getString(R.string.payments_required_field_message)); |
| + editor.addView(explanation); |
| + |
| + Button cancel = new Button(mContext); |
| + cancel.setId(R.id.payments_edit_cancel_button); |
| + cancel.setText(mContext.getString(R.string.cancel)); |
| + cancel.setOnClickListener(new View.OnClickListener() { |
| + @Override |
| + public void onClick(View view) { |
| + editorModel.cancel(); |
| + mEditDialog.dismiss(); |
| + } |
| + }); |
| + editor.addView(cancel); |
| + |
| + done.setId(R.id.payments_edit_done_button); |
| + done.setText(mContext.getString(R.string.done)); |
| + done.setOnClickListener(new View.OnClickListener() { |
| + @Override |
| + public void onClick(View view) { |
| + final List<AutoCompleteTextView> invalidViews = getInvalidViews(editor); |
| + if (invalidViews.isEmpty()) { |
| + editorModel.done(); |
| + mEditDialog.dismiss(); |
| + } else { |
| + if (!invalidViews.contains(mEditDialog.getCurrentFocus())) { |
| + focusInputFieldAndShowSoftKeyboard(invalidViews.get(0)); |
| + } |
| + ColorFilter errorMessageColorFilter = new PorterDuffColorFilter( |
| + ApiCompatibilityUtils.getColor( |
| + mContext.getResources(), R.color.input_underline_error_color), |
| + PorterDuff.Mode.SRC_IN); |
| + for (int i = 0; i < invalidViews.size(); i++) { |
| + AutoCompleteTextView invalid = invalidViews.get(i); |
| + invalid.setError(((EditorFieldModel) invalid.getTag()).getErrorMessage()); |
| + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
| + invalid.getBackground().mutate().setColorFilter( |
| + errorMessageColorFilter); |
| + } |
| + } |
| + if (sObserverForTest != null) { |
| + sObserverForTest.onPaymentRequestEditorValidationError( |
| + PaymentRequestUI.this); |
| + } |
| + } |
| + } |
| + }); |
| + editor.addView(done); |
| + |
| + mEditDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { |
| + @Override |
| + public void onDismiss(DialogInterface dialog) { |
| + mPhoneInput = null; |
| + editorModel.cancel(); |
| + if (sObserverForTest != null) { |
| + sObserverForTest.onPaymentRequestEditorDismissed(PaymentRequestUI.this); |
| + } |
| + } |
| + }); |
| + mEditDialog.setContentView(editor); |
| + mEditDialog.show(); |
| + |
| + final List<AutoCompleteTextView> invalidViews = getInvalidViews(editor); |
| + if (!invalidViews.isEmpty()) { |
| + mHandler.post(new Runnable() { |
| + @Override |
| + public void run() { |
| + focusInputFieldAndShowSoftKeyboard(invalidViews.get(0)); |
|
gone
2016/06/21 06:04:39
You really shouldn't be forcing a keyboard to show
please use gerrit instead
2016/06/22 02:12:30
This is what we do in CVV prompt.
https://cs.chro
gone
2016/06/22 05:24:13
Acknowledged.
|
| + } |
| + }); |
| + } |
| + } |
| + |
| + /** |
| + * Avoids disk reads when building a phone formatter. |
| + */ |
| + private class PhoneFormatterTask |
|
gone
2016/06/21 06:04:39
Geez, do we actually get StrictMode violations her
please use gerrit instead
2016/06/22 02:12:30
Yes.
|
| + extends AsyncTask<Void, Void, PhoneNumberFormattingTextWatcher> { |
| + @Override |
| + protected PhoneNumberFormattingTextWatcher doInBackground(Void... unused) { |
| + return new PhoneNumberFormattingTextWatcher(); |
| + } |
| + |
| + @Override |
| + protected void onPostExecute(PhoneNumberFormattingTextWatcher result) { |
| + mPhoneFormatter = result; |
| + if (mPhoneInput != null) mPhoneInput.addTextChangedListener(mPhoneFormatter); |
| + } |
| + } |
| + |
| + private static List<AutoCompleteTextView> getInvalidViews(ViewGroup container) { |
|
gone
2016/06/21 06:04:39
getViewsWithInvalidInformation
"invalid" and "inv
please use gerrit instead
2016/06/22 02:12:30
Done.
|
| + List<AutoCompleteTextView> invalidViews = new ArrayList<>(); |
| + for (int i = 0; i < container.getChildCount(); i++) { |
| + View view = container.getChildAt(i); |
| + if (!(view instanceof AutoCompleteTextView)) continue; |
| + AutoCompleteTextView textView = (AutoCompleteTextView) view; |
| + if (!((EditorFieldModel) textView.getTag()).isValid()) invalidViews.add(textView); |
| + } |
| + return invalidViews; |
| + } |
| + |
| + private void focusInputFieldAndShowSoftKeyboard(View view) { |
| + view.requestFocus(); |
| + InputMethodManager imm = |
| + (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); |
| + imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); |
| + view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); |
| + if (sObserverForTest != null) sObserverForTest.onPaymentRequestReadyToEdit(this); |
| + } |
| + |
| + /** |
| * Called when user clicks anything in the dialog. |
| */ |
| @Override |
| @@ -967,10 +1189,20 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View |
| } |
| @VisibleForTesting |
| + public Dialog getEditDialogForTest() { |
| + return mEditDialog; |
| + } |
| + |
| + @VisibleForTesting |
| public ViewGroup getShippingAddressSectionForTest() { |
| return mShippingAddressSection; |
| } |
| + @VisibleForTesting |
| + public ViewGroup getContactDetailsSectionForTest() { |
| + return mContactDetailsSection; |
| + } |
| + |
| private void notifyReadyForInput() { |
| if (sObserverForTest != null && isAcceptingUserInput()) { |
| sObserverForTest.onPaymentRequestReadyForInput(this); |