| Index: chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
|
| index 72b617829117135860efed83b064436208c06335..9b21004e088f582db75ae52302a0df6231c656be 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/EditorView.java
|
| @@ -26,6 +26,7 @@ import android.widget.LinearLayout;
|
| import android.widget.TextView;
|
|
|
| import org.chromium.base.ApiCompatibilityUtils;
|
| +import org.chromium.base.VisibleForTesting;
|
| import org.chromium.chrome.R;
|
| import org.chromium.chrome.browser.EmbedContentViewActivity;
|
| import org.chromium.chrome.browser.payments.ui.PaymentRequestUI.PaymentRequestObserverForTest;
|
| @@ -58,10 +59,14 @@ public class EditorView extends AlwaysDismissedDialog
|
| private final Handler mHandler;
|
| private final AsyncTask<Void, Void, PhoneNumberFormattingTextWatcher> mPhoneFormatterTask;
|
| private final TextView.OnEditorActionListener mEditorActionListener;
|
| + private final int mHalfRowMargin;
|
| + private final List<EditorTextField> mEditorTextFields;
|
|
|
| private ViewGroup mLayout;
|
| private EditorModel mEditorModel;
|
| private Button mDoneButton;
|
| + private ViewGroup mDataView;
|
| + private View mFooter;
|
| @Nullable private AutoCompleteTextView mPhoneInput;
|
|
|
| /**
|
| @@ -98,6 +103,10 @@ public class EditorView extends AlwaysDismissedDialog
|
| return false;
|
| }
|
| };
|
| +
|
| + mHalfRowMargin = activity.getResources().getDimensionPixelSize(
|
| + R.dimen.payments_section_large_spacing);
|
| + mEditorTextFields = new ArrayList<>();
|
| }
|
|
|
| /** Launches the Autofill help page on top of the current Context. */
|
| @@ -157,21 +166,19 @@ public class EditorView extends AlwaysDismissedDialog
|
| */
|
| private boolean validateForm() {
|
| final List<EditorTextField> invalidViews = getViewsWithInvalidInformation();
|
| - if (invalidViews.isEmpty()) return true;
|
|
|
| // Focus the first field that's invalid.
|
| - if (!invalidViews.contains(getCurrentFocus())) focusInputField(invalidViews.get(0));
|
| + if (!invalidViews.isEmpty() && !invalidViews.contains(getCurrentFocus())) {
|
| + focusInputField(invalidViews.get(0));
|
| + }
|
|
|
| // Iterate over all the fields to update what errors are displayed, which is necessary to
|
| // to clear existing errors on any newly valid fields.
|
| - ViewGroup dataView = (ViewGroup) mLayout.findViewById(R.id.contents);
|
| - for (int i = 0; i < dataView.getChildCount(); i++) {
|
| - if (!(dataView.getChildAt(i) instanceof EditorTextField)) continue;
|
| - EditorTextField fieldView = (EditorTextField) dataView.getChildAt(i);
|
| + for (int i = 0; i < mEditorTextFields.size(); i++) {
|
| + EditorTextField fieldView = mEditorTextFields.get(i);
|
| fieldView.updateDisplayedError(invalidViews.contains(fieldView));
|
| }
|
| -
|
| - return false;
|
| + return invalidViews.isEmpty();
|
| }
|
|
|
| @Override
|
| @@ -210,47 +217,90 @@ public class EditorView extends AlwaysDismissedDialog
|
| /**
|
| * Create the visual representation of the EditorModel.
|
| *
|
| - * Fields are added to the layout at position |getChildCount() - 1| to account for the
|
| - * additional TextView that says "* indicates required field" at the bottom of the layout.
|
| - *
|
| - * TODO(rouslan): Put views side by side if !fieldModel.isFullLine();
|
| + * This would be more optimal as a RelativeLayout, but because it's dynamically generated, it's
|
| + * much more human-parsable with inefficient LinearLayouts for half-width controls sharing rows.
|
| */
|
| private void prepareEditor() {
|
| - final ViewGroup dataView = (ViewGroup) mLayout.findViewById(R.id.contents);
|
| + removeTextChangedListenerFromPhoneInputField();
|
| +
|
| + // Ensure the layout is empty.
|
| + mDataView = (ViewGroup) mLayout.findViewById(R.id.contents);
|
| + mDataView.removeAllViews();
|
| + mEditorTextFields.clear();
|
| +
|
| + // Add Views for each of the {@link EditorFields}.
|
| for (int i = 0; i < mEditorModel.getFields().size(); i++) {
|
| - final EditorFieldModel fieldModel = mEditorModel.getFields().get(i);
|
| -
|
| - if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN) {
|
| - EditorDropdownField dropdownView = new EditorDropdownField(mContext, fieldModel,
|
| - new Runnable() {
|
| - @Override
|
| - public void run() {
|
| - removeTextChangedListenerFromPhoneInputField();
|
| - // Do not remove the "* indicates required field" label at the
|
| - // bottom.
|
| - dataView.removeViews(0, dataView.getChildCount() - 1);
|
| - prepareEditor();
|
| - if (mObserverForTest != null) {
|
| - mObserverForTest.onPaymentRequestReadyToEdit();
|
| - }
|
| - }
|
| - });
|
| -
|
| - dataView.addView(dropdownView.getLabel(), dataView.getChildCount() - 1);
|
| - dataView.addView(dropdownView.getDropdown(), dataView.getChildCount() - 1);
|
| + EditorFieldModel fieldModel = mEditorModel.getFields().get(i);
|
| + EditorFieldModel nextFieldModel = null;
|
| +
|
| + boolean isLastField = i == mEditorModel.getFields().size() - 1;
|
| + boolean useFullLine = fieldModel.isFullLine();
|
| + if (!isLastField && !useFullLine) {
|
| + // If the next field isn't full, stretch it out.
|
| + nextFieldModel = mEditorModel.getFields().get(i + 1);
|
| + if (nextFieldModel.isFullLine()) useFullLine = true;
|
| + }
|
| +
|
| + if (useFullLine) {
|
| + addFieldViewToEditor(mDataView, fieldModel);
|
| } else {
|
| - EditorTextField inputLayout = new EditorTextField(mLayout.getContext(), fieldModel,
|
| - mEditorActionListener, getPhoneFormatter(), mObserverForTest);
|
| + // Create a LinearLayout to put it and the next view side by side.
|
| + LinearLayout rowLayout = new LinearLayout(mContext);
|
| + mDataView.addView(rowLayout);
|
| +
|
| + View firstView = addFieldViewToEditor(rowLayout, fieldModel);
|
| + View lastView = addFieldViewToEditor(rowLayout, nextFieldModel);
|
| +
|
| + LinearLayout.LayoutParams firstParams =
|
| + (LinearLayout.LayoutParams) firstView.getLayoutParams();
|
| + LinearLayout.LayoutParams lastParams =
|
| + (LinearLayout.LayoutParams) lastView.getLayoutParams();
|
| +
|
| + firstParams.width = 0;
|
| + firstParams.weight = 1;
|
| + firstParams.setMarginEnd(mHalfRowMargin);
|
| + lastParams.width = 0;
|
| + lastParams.weight = 1;
|
| + i = i + 1;
|
| + }
|
| + }
|
|
|
| - final AutoCompleteTextView input = inputLayout.getEditText();
|
| - if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_PHONE) {
|
| - assert mPhoneInput == null;
|
| - mPhoneInput = input;
|
| - }
|
| + // Add the footer.
|
| + mDataView.addView(mFooter);
|
| + }
|
|
|
| - dataView.addView(inputLayout, dataView.getChildCount() - 1);
|
| + private View addFieldViewToEditor(ViewGroup parent, EditorFieldModel fieldModel) {
|
| + View childView = null;
|
| +
|
| + if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN) {
|
| + Runnable prepareEditorRunnable = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + // The fields may have changed.
|
| + prepareEditor();
|
| + if (mObserverForTest != null) mObserverForTest.onPaymentRequestReadyToEdit();
|
| + }
|
| + };
|
| + EditorDropdownField dropdownView =
|
| + new EditorDropdownField(mContext, fieldModel, prepareEditorRunnable);
|
| +
|
| + childView = dropdownView.getLayout();
|
| + } else {
|
| + EditorTextField inputLayout = new EditorTextField(mLayout.getContext(), fieldModel,
|
| + mEditorActionListener, getPhoneFormatter(), mObserverForTest);
|
| + mEditorTextFields.add(inputLayout);
|
| +
|
| + final AutoCompleteTextView input = inputLayout.getEditText();
|
| + if (fieldModel.getInputTypeHint() == EditorFieldModel.INPUT_TYPE_HINT_PHONE) {
|
| + assert mPhoneInput == null;
|
| + mPhoneInput = input;
|
| }
|
| +
|
| + childView = inputLayout;
|
| }
|
| +
|
| + parent.addView(childView);
|
| + return childView;
|
| }
|
|
|
| /**
|
| @@ -265,6 +315,10 @@ public class EditorView extends AlwaysDismissedDialog
|
| mLayout = (LinearLayout) LayoutInflater.from(mContext).inflate(
|
| R.layout.payment_request_editor, null);
|
| setContentView(mLayout);
|
| +
|
| + mFooter = LayoutInflater.from(mContext).inflate(
|
| + R.layout.payment_request_editor_footer, null, false);
|
| +
|
| prepareToolbar();
|
| prepareButtons();
|
| prepareEditor();
|
| @@ -289,6 +343,12 @@ public class EditorView extends AlwaysDismissedDialog
|
| if (mObserverForTest != null) mObserverForTest.onPaymentRequestEditorDismissed();
|
| }
|
|
|
| + /** @return All the EditorTextFields that exist in the EditorView. */
|
| + @VisibleForTesting
|
| + public List<EditorTextField> getEditorTextFields() {
|
| + return mEditorTextFields;
|
| + }
|
| +
|
| private void removeTextChangedListenerFromPhoneInputField() {
|
| if (mPhoneInput != null) mPhoneInput.removeTextChangedListener(getPhoneFormatter());
|
| mPhoneInput = null;
|
| @@ -305,14 +365,9 @@ public class EditorView extends AlwaysDismissedDialog
|
| }
|
|
|
| private List<EditorTextField> getViewsWithInvalidInformation() {
|
| - ViewGroup container = (ViewGroup) findViewById(R.id.contents);
|
| -
|
| List<EditorTextField> invalidViews = new ArrayList<>();
|
| - for (int i = 0; i < container.getChildCount(); i++) {
|
| - View layout = container.getChildAt(i);
|
| - if (!(layout instanceof EditorTextField)) continue;
|
| -
|
| - EditorTextField fieldView = (EditorTextField) layout;
|
| + for (int i = 0; i < mEditorTextFields.size(); i++) {
|
| + EditorTextField fieldView = mEditorTextFields.get(i);
|
| if (!fieldView.getFieldModel().isValid()) invalidViews.add(fieldView);
|
| }
|
| return invalidViews;
|
|
|