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