Merge "Fix ViewStructure.getHint() for autofill on child of TextInputLayout." into oc-support-26.0-dev
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index bd02bc3..52efdde 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -64,6 +64,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewStructure;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.EditText;
@@ -121,6 +122,7 @@
 
     private final FrameLayout mInputFrame;
     EditText mEditText;
+    private CharSequence mOriginalHint;
 
     private boolean mHintEnabled;
     private CharSequence mHint;
@@ -313,6 +315,24 @@
         return mTypeface;
     }
 
+    @Override
+    public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
+        if (mOriginalHint == null || mEditText == null) {
+            super.dispatchProvideAutofillStructure(structure, flags);
+            return;
+        }
+
+        // Temporarily sets child's hint to its original value so it is properly set in the
+        // child's ViewStructure.
+        final CharSequence hint = mEditText.getHint();
+        mEditText.setHint(mOriginalHint);
+        try {
+            super.dispatchProvideAutofillStructure(structure, flags);
+        } finally {
+            mEditText.setHint(hint);
+        }
+    }
+
     private void setEditText(EditText editText) {
         // If we already have an EditText, throw an exception
         if (mEditText != null) {
@@ -364,7 +384,9 @@
 
         // If we do not have a valid hint, try and retrieve it from the EditText, if enabled
         if (mHintEnabled && TextUtils.isEmpty(mHint)) {
-            setHint(mEditText.getHint());
+            // Save the hint so it can be restored on dispatchProvideAutofillStructure();
+            mOriginalHint = mEditText.getHint();
+            setHint(mOriginalHint);
             // Clear the EditText's hint as we will display it ourselves
             mEditText.setHint(null);
         }
diff --git a/design/tests/src/android/support/design/testutils/ViewStructureImpl.java b/design/tests/src/android/support/design/testutils/ViewStructureImpl.java
new file mode 100644
index 0000000..c7369e9
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/ViewStructureImpl.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.design.testutils;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.view.ViewStructure;
+import android.view.ViewStructure.HtmlInfo.Builder;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Simple implementation of {@link ViewStructure} that's easier to use than a Mockito mock.
+ *
+ * <p>Currently supports only {@code hint}, {@code className}, and child-related methods.
+ */
+public class ViewStructureImpl extends ViewStructure {
+
+    private CharSequence mHint;
+    private String mClassName;
+    private ViewStructureImpl[] mChildren;
+
+    @Override
+    public void setHint(CharSequence hint) {
+        mHint = hint;
+    }
+
+    // Supported methods
+    @Override
+    public CharSequence getHint() {
+        return mHint;
+    }
+
+    @Override
+    public void setChildCount(int num) {
+        mChildren = new ViewStructureImpl[num];
+    }
+
+    @Override
+    public void setClassName(String className) {
+        mClassName = className;
+    }
+
+    public String getClassName() {
+        return mClassName;
+    }
+
+    @Override
+    public int addChildCount(int num) {
+        if (mChildren == null) {
+            setChildCount(num);
+            return 0;
+        }
+        final int start = mChildren.length;
+        ViewStructureImpl[] newArray = new ViewStructureImpl[start + num];
+        System.arraycopy(mChildren, 0, newArray, 0, start);
+        mChildren = newArray;
+        return start;
+    }
+
+    @Override
+    public int getChildCount() {
+        if (mChildren == null) {
+            return 0;
+        }
+        return mChildren.length;
+    }
+
+    public ViewStructureImpl getChildAt(int index) {
+        return mChildren[index];
+    }
+
+    @Override
+    public ViewStructure newChild(int index) {
+        final ViewStructureImpl child = new ViewStructureImpl();
+        mChildren[index] = child;
+        return child;
+    }
+
+    @Override
+    public ViewStructure asyncNewChild(int index) {
+        return newChild(index);
+    }
+
+
+    // Unsupported methods
+    @Override
+    public void setId(int id, String packageName, String typeName, String entryName) {
+    }
+
+    @Override
+    public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
+    }
+
+    @Override
+    public void setTransformation(Matrix matrix) {
+    }
+
+    @Override
+    public void setElevation(float elevation) {
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+    }
+
+    public void setAssistBlocked(boolean state) {
+    }
+
+    @Override
+    public void setEnabled(boolean state) {
+    }
+
+    @Override
+    public void setClickable(boolean state) {
+    }
+
+    @Override
+    public void setLongClickable(boolean state) {
+    }
+
+    @Override
+    public void setContextClickable(boolean state) {
+    }
+
+    @Override
+    public void setFocusable(boolean state) {
+    }
+
+    @Override
+    public void setFocused(boolean state) {
+    }
+
+    @Override
+    public void setAccessibilityFocused(boolean state) {
+    }
+
+    @Override
+    public void setCheckable(boolean state) {
+    }
+
+    @Override
+    public void setChecked(boolean state) {
+    }
+
+    @Override
+    public void setSelected(boolean state) {
+    }
+
+    @Override
+    public void setActivated(boolean state) {
+    }
+
+    @Override
+    public void setOpaque(boolean opaque) {
+    }
+
+    @Override
+    public void setContentDescription(CharSequence contentDescription) {
+    }
+
+    @Override
+    public void setText(CharSequence text) {
+    }
+
+    @Override
+    public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+    }
+
+    @Override
+    public void setTextStyle(float size, int fgColor, int bgColor, int style) {
+    }
+
+    @Override
+    public void setTextLines(int[] charOffsets, int[] baselines) {
+    }
+
+    @Override
+    public CharSequence getText() {
+        return null;
+    }
+
+    @Override
+    public int getTextSelectionStart() {
+        return 0;
+    }
+
+    @Override
+    public int getTextSelectionEnd() {
+        return 0;
+    }
+
+    @Override
+    public Bundle getExtras() {
+        return null;
+    }
+
+    @Override
+    public boolean hasExtras() {
+        return false;
+    }
+
+    @Override
+    public AutofillId getAutofillId() {
+        return null;
+    }
+
+    @Override
+    public void setAutofillId(AutofillId id) {
+    }
+
+    public void setAutofillId(ViewStructure parent, int virtualId) {
+    }
+
+    @Override
+    public void setAutofillId(AutofillId parentId, int virtualId) {
+    }
+
+    @Override
+    public void setAutofillType(int type) {
+    }
+
+    @Override
+    public void setAutofillHints(String[] hint) {
+    }
+
+    @Override
+    public void setAutofillValue(AutofillValue value) {
+    }
+
+    @Override
+    public void setAutofillOptions(CharSequence[] options) {
+    }
+
+    @Override
+    public void setInputType(int inputType) {
+    }
+
+    @Override
+    public void setDataIsSensitive(boolean sensitive) {
+    }
+
+    @Override
+    public void asyncCommit() {
+    }
+
+    public Rect getTempRect() {
+        return null;
+    }
+
+    @Override
+    public void setWebDomain(String domain) {
+    }
+
+    @Override
+    public void setLocaleList(LocaleList localeList) {
+    }
+
+    @Override
+    public Builder newHtmlInfoBuilder(String tagName) {
+        return null;
+    }
+
+    @Override
+    public void setHtmlInfo(HtmlInfo htmlInfo) {
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 929fcd7..226463c 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -49,6 +49,7 @@
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
@@ -58,13 +59,16 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Parcelable;
 import android.support.design.test.R;
 import android.support.design.testutils.TestUtils;
+import android.support.design.testutils.ViewStructureImpl;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.ViewAssertion;
 import android.support.test.filters.MediumTest;
+import android.support.test.filters.SdkSuppress;
 import android.support.v4.widget.TextViewCompat;
 import android.util.AttributeSet;
 import android.util.SparseArray;
@@ -304,6 +308,30 @@
         assertEquals(INPUT_TEXT, info.hintText);
     }
 
+    @UiThreadTest
+    @Test
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    public void testDispatchProvideAutofillStructure() {
+        final Activity activity = mActivityTestRule.getActivity();
+
+        final TextInputLayout layout = activity.findViewById(R.id.textinput);
+
+        final ViewStructureImpl structure = new ViewStructureImpl();
+        layout.dispatchProvideAutofillStructure(structure, 0);
+
+        assertEquals(2, structure.getChildCount()); // EditText and TextView
+
+        // Asserts the structure.
+        final ViewStructureImpl childStructure = structure.getChildAt(0);
+        assertEquals(EditText.class.getName(), childStructure.getClassName());
+        assertEquals("Hint to the user", childStructure.getHint());
+
+        // Make sure the widget's hint was restored.
+        assertEquals("Hint to the user", layout.getHint());
+        final EditText editText = activity.findViewById(R.id.textinput_edittext);
+        assertNull(editText.getHint());
+    }
+
     /**
      * Regression test for b/31663756.
      */