Implement password toggle in TextInputLayout

This CL implements the new password spec from the
Material Design guidelines into TextInputLayout.
The icon and tint are customizable via attributes
and method calls.

Also added getCompoundDrawablesRelative() to
TextViewCompat.

BUG: 26614707

Change-Id: I3d36277fe92f1be0c9f23567fa55b42c5ba15be1
diff --git a/api/current.txt b/api/current.txt
index 704f51f..4ef959d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -724,11 +724,14 @@
     method public android.widget.EditText getEditText();
     method public java.lang.CharSequence getError();
     method public java.lang.CharSequence getHint();
+    method public java.lang.CharSequence getPasswordVisibilityToggleContentDescription();
+    method public android.graphics.drawable.Drawable getPasswordVisibilityToggleDrawable();
     method public android.graphics.Typeface getTypeface();
     method public boolean isCounterEnabled();
     method public boolean isErrorEnabled();
     method public boolean isHintAnimationEnabled();
     method public boolean isHintEnabled();
+    method public boolean isPasswordVisibilityToggleEnabled();
     method public android.os.Parcelable onSaveInstanceState();
     method public void setCounterEnabled(boolean);
     method public void setCounterMaxLength(int);
@@ -738,6 +741,13 @@
     method public void setHintAnimationEnabled(boolean);
     method public void setHintEnabled(boolean);
     method public void setHintTextAppearance(int);
+    method public void setPasswordVisibilityToggleContentDescription(int);
+    method public void setPasswordVisibilityToggleContentDescription(java.lang.CharSequence);
+    method public void setPasswordVisibilityToggleDrawable(int);
+    method public void setPasswordVisibilityToggleDrawable(android.graphics.drawable.Drawable);
+    method public void setPasswordVisibilityToggleEnabled(boolean);
+    method public void setPasswordVisibilityToggleTintList(android.content.res.ColorStateList);
+    method public void setPasswordVisibilityToggleTintMode(android.graphics.PorterDuff.Mode);
     method public void setTypeface(android.graphics.Typeface);
   }
 
@@ -7319,6 +7329,7 @@
   }
 
   public final class TextViewCompat {
+    method public static android.graphics.drawable.Drawable[] getCompoundDrawablesRelative(android.widget.TextView);
     method public static int getMaxLines(android.widget.TextView);
     method public static int getMinLines(android.widget.TextView);
     method public static void setCompoundDrawablesRelative(android.widget.TextView, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
@@ -10112,3 +10123,4 @@
   }
 
 }
+
diff --git a/compat/java/android/support/v4/widget/TextViewCompat.java b/compat/java/android/support/v4/widget/TextViewCompat.java
index 3239587..d61ddc2 100644
--- a/compat/java/android/support/v4/widget/TextViewCompat.java
+++ b/compat/java/android/support/v4/widget/TextViewCompat.java
@@ -46,6 +46,7 @@
         int getMaxLines(TextView textView);
         int getMinLines(TextView textView);
         void setTextAppearance(@NonNull TextView textView, @StyleRes int resId);
+        Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView);
     }
 
     static class BaseTextViewCompatImpl implements TextViewCompatImpl {
@@ -84,6 +85,11 @@
         public void setTextAppearance(TextView textView, @StyleRes int resId) {
             TextViewCompatGingerbread.setTextAppearance(textView, resId);
         }
+
+        @Override
+        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+            return TextViewCompatGingerbread.getCompoundDrawablesRelative(textView);
+        }
     }
 
     static class JbTextViewCompatImpl extends BaseTextViewCompatImpl {
@@ -121,6 +127,11 @@
             TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
                     start, top, end, bottom);
         }
+
+        @Override
+        public Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+            return TextViewCompatJbMr1.getCompoundDrawablesRelative(textView);
+        }
     }
 
     static class JbMr2TextViewCompatImpl extends JbMr1TextViewCompatImpl {
@@ -268,4 +279,11 @@
     public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
         IMPL.setTextAppearance(textView, resId);
     }
+
+    /**
+     * Returns drawables for the start, top, end, and bottom borders from the given text view.
+     */
+    public static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+        return textView.getCompoundDrawables();
+    }
 }
diff --git a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
index 65056f5..faa0939 100644
--- a/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
+++ b/compat/java/android/support/v4/widget/TextViewCompatGingerbread.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.widget;
 
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
 import android.util.Log;
 import android.widget.TextView;
 
@@ -95,4 +97,8 @@
     static void setTextAppearance(TextView textView, int resId) {
         textView.setTextAppearance(textView.getContext(), resId);
     }
+
+    static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+        return textView.getCompoundDrawables();
+    }
 }
diff --git a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
index 7c073c6..fc088ed 100644
--- a/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
+++ b/compat/jellybean-mr1/android/support/v4/widget/TextViewCompatJbMr1.java
@@ -46,4 +46,8 @@
                 bottom);
     }
 
+    static Drawable[] getCompoundDrawablesRelative(@NonNull TextView textView) {
+        return textView.getCompoundDrawablesRelative();
+    }
+
 }
diff --git a/design/res/color-v23/design_tint_password_toggle.xml b/design/res/color-v23/design_tint_password_toggle.xml
new file mode 100644
index 0000000..3a09d32
--- /dev/null
+++ b/design/res/color-v23/design_tint_password_toggle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:color="?android:attr/colorForeground" android:alpha="0.54"/>
+    <item android:color="?android:attr/colorForeground" android:alpha="0.38"/>
+</selector>
\ No newline at end of file
diff --git a/design/res/color/design_tint_password_toggle.xml b/design/res/color/design_tint_password_toggle.xml
new file mode 100644
index 0000000..15016e4
--- /dev/null
+++ b/design/res/color/design_tint_password_toggle.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:state_checked="true" android:color="?android:attr/colorForeground" app:alpha="0.54"/>
+    <item android:color="?android:attr/colorForeground" app:alpha="0.38"/>
+</selector>
\ No newline at end of file
diff --git a/design/res/drawable/design_ic_visibility.xml b/design/res/drawable/design_ic_visibility.xml
new file mode 100644
index 0000000..91d46ff
--- /dev/null
+++ b/design/res/drawable/design_ic_visibility.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@android:color/black"
+        android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
+</vector>
\ No newline at end of file
diff --git a/design/res/layout/design_text_input_password_icon.xml b/design/res/layout/design_text_input_password_icon.xml
new file mode 100644
index 0000000..0dbb9fa
--- /dev/null
+++ b/design/res/layout/design_text_input_password_icon.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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.
+-->
+
+<android.support.design.widget.CheckableImageButton
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/text_input_password_toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:layout_gravity="center_vertical|end|right"/>
\ No newline at end of file
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index fc233da..113f576 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -187,25 +187,50 @@
 
     <declare-styleable name="TextInputLayout">
         <attr name="hintTextAppearance" format="reference"/>
-        <!-- The hint to display in the floating label -->
+        <!-- The hint to display in the floating label. -->
         <attr name="android:hint"/>
-        <!-- Whether the layout's floating label functionality is enabled -->
+        <!-- Whether the layout's floating label functionality is enabled. -->
         <attr name="hintEnabled" format="boolean"/>
-        <!-- Whether the layout is laid out as if an error will be displayed -->
+        <!-- Whether the layout is laid out as if an error will be displayed. -->
         <attr name="errorEnabled" format="boolean"/>
-        <!-- TextAppearance of any error message displayed -->
+        <!-- TextAppearance of any error message displayed. -->
         <attr name="errorTextAppearance" format="reference"/>
-        <!-- Whether the layout is laid out as if the character counter will be displayed -->
+        <!-- Whether the layout is laid out as if the character counter will be displayed. -->
         <attr name="counterEnabled" format="boolean"/>
-        <!-- The max length to display in the character counter -->
+        <!-- The max length to display in the character counter. -->
         <attr name="counterMaxLength" format="integer" />
-        <!-- TextAppearance of the character counter -->
+        <!-- TextAppearance of the character counter. -->
         <attr name="counterTextAppearance" format="reference"/>
-        <!-- TextAppearance of the character counter when the text is longer than the max -->
+        <!-- TextAppearance of the character counter when the text is longer than the max. -->
         <attr name="counterOverflowTextAppearance" format="reference"/>
         <attr name="android:textColorHint"/>
         <!-- Whether to animate hint state changes. -->
         <attr name="hintAnimationEnabled" format="boolean"/>
+        <!-- Whether the view will display a toggle when the EditText has a password. -->
+        <attr name="passwordToggleEnabled" format="boolean"/>
+        <!-- Drawable to use as the password input visibility toggle icon. -->
+        <attr name="passwordToggleDrawable" format="reference"/>
+        <!-- Text to set as the content description for the password input visibility toggle. -->
+        <attr name="passwordToggleContentDescription" format="string"/>
+        <!-- Icon to use for the password input visibility toggle -->
+        <attr name="passwordToggleTint" format="color"/>
+        <!-- Blending mode used to apply the background tint. -->
+        <attr name="passwordToggleTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="SnackbarLayout">
diff --git a/design/res/values/styles.xml b/design/res/values/styles.xml
index b25646c..be5b4b0 100644
--- a/design/res/values/styles.xml
+++ b/design/res/values/styles.xml
@@ -64,6 +64,8 @@
         <item name="errorTextAppearance">@style/TextAppearance.Design.Error</item>
         <item name="counterTextAppearance">@style/TextAppearance.Design.Counter</item>
         <item name="counterOverflowTextAppearance">@style/TextAppearance.Design.Counter.Overflow</item>
+        <item name="passwordToggleDrawable">@drawable/design_ic_visibility</item>
+        <item name="passwordToggleTint">@color/design_tint_password_toggle</item>
     </style>
 
     <style name="TextAppearance.Design.Hint" parent="TextAppearance.AppCompat.Caption">
diff --git a/design/src/android/support/design/widget/CheckableImageButton.java b/design/src/android/support/design/widget/CheckableImageButton.java
new file mode 100644
index 0000000..2e7612b
--- /dev/null
+++ b/design/src/android/support/design/widget/CheckableImageButton.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 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.widget;
+
+import android.content.Context;
+import android.support.v4.view.AccessibilityDelegateCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v7.widget.AppCompatImageButton;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.Checkable;
+
+/**
+ * @hide
+ */
+public class CheckableImageButton extends AppCompatImageButton implements Checkable {
+
+    private static final int[] DRAWABLE_STATE_CHECKED = new int[]{android.R.attr.state_checked};
+
+    private boolean mChecked;
+
+    public CheckableImageButton(Context context) {
+        this(context, null);
+    }
+
+    public CheckableImageButton(Context context, AttributeSet attrs) {
+        this(context, attrs, android.support.v7.appcompat.R.attr.imageButtonStyle);
+    }
+
+    public CheckableImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        ViewCompat.setAccessibilityDelegate(this, new AccessibilityDelegateCompat() {
+            @Override
+            public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+                super.onInitializeAccessibilityEvent(host, event);
+                event.setChecked(isChecked());
+            }
+
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host,
+                    AccessibilityNodeInfoCompat info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.setCheckable(true);
+                info.setChecked(isChecked());
+            }
+        });
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        if (mChecked != checked) {
+            mChecked = checked;
+            refreshDrawableState();
+            sendAccessibilityEvent(
+                    AccessibilityEventCompat.TYPE_WINDOW_CONTENT_CHANGED);
+        }
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mChecked;
+    }
+
+    @Override
+    public void toggle() {
+        setChecked(!mChecked);
+    }
+
+    @Override
+    public int[] onCreateDrawableState(int extraSpace) {
+        if (mChecked) {
+            return mergeDrawableStates(
+                    super.onCreateDrawableState(extraSpace + DRAWABLE_STATE_CHECKED.length),
+                    DRAWABLE_STATE_CHECKED);
+        } else {
+            return super.onCreateDrawableState(extraSpace);
+        }
+    }
+}
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index f75bc1a..b25a4b2 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -157,7 +157,7 @@
                 R.styleable.FloatingActionButton, defStyleAttr,
                 R.style.Widget_Design_FloatingActionButton);
         mBackgroundTint = a.getColorStateList(R.styleable.FloatingActionButton_backgroundTint);
-        mBackgroundTintMode = parseTintMode(a.getInt(
+        mBackgroundTintMode = ViewUtils.parseTintMode(a.getInt(
                 R.styleable.FloatingActionButton_backgroundTintMode, -1), null);
         mRippleColor = a.getColor(R.styleable.FloatingActionButton_rippleColor, 0);
         mSize = a.getInt(R.styleable.FloatingActionButton_fabSize, SIZE_AUTO);
@@ -511,23 +511,6 @@
         return result;
     }
 
-    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
-        switch (value) {
-            case 3:
-                return PorterDuff.Mode.SRC_OVER;
-            case 5:
-                return PorterDuff.Mode.SRC_IN;
-            case 9:
-                return PorterDuff.Mode.SRC_ATOP;
-            case 14:
-                return PorterDuff.Mode.MULTIPLY;
-            case 15:
-                return PorterDuff.Mode.SCREEN;
-            default:
-                return defaultMode;
-        }
-    }
-
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if(getContentRect(mTouchArea) && !mTouchArea.contains((int) ev.getX(), (int) ev.getY())) {
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 171b62b..782e3d0 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -18,18 +18,21 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
+import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
 import android.support.annotation.StyleRes;
 import android.support.design.R;
 import android.support.v4.content.ContextCompat;
@@ -43,18 +46,23 @@
 import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.widget.Space;
+import android.support.v4.widget.TextViewCompat;
 import android.support.v7.widget.AppCompatDrawableManager;
+import android.support.v7.widget.TintTypedArray;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.text.method.PasswordTransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.EditText;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -90,12 +98,14 @@
 
     private static final String LOG_TAG = "TextInputLayout";
 
+    private final FrameLayout mInputFrame;
     private EditText mEditText;
 
     private boolean mHintEnabled;
     private CharSequence mHint;
 
     private Paint mTmpPaint;
+    private final Rect mTmpRect = new Rect();
 
     private LinearLayout mIndicatorArea;
     private int mIndicatorsAdded;
@@ -113,6 +123,18 @@
     private int mCounterOverflowTextAppearance;
     private boolean mCounterOverflowed;
 
+    private boolean mPasswordToggleEnabled;
+    private Drawable mPasswordToggleDrawable;
+    private CharSequence mPasswordToggleContentDesc;
+    private CheckableImageButton mPasswordToggleView;
+    private boolean mPasswordToggledVisible;
+    private Drawable mPasswordToggleDummyDrawable;
+
+    private ColorStateList mPasswordToggleTintList;
+    private boolean mHasPasswordToggleTintList;
+    private PorterDuff.Mode mPasswordToggleTintMode;
+    private boolean mHasPasswordToggleTintMode;
+
     private ColorStateList mDefaultTextColor;
     private ColorStateList mFocusedTextColor;
 
@@ -141,11 +163,14 @@
         setWillNotDraw(false);
         setAddStatesFromChildren(true);
 
+        mInputFrame = new FrameLayout(context);
+        addView(mInputFrame);
+
         mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
         mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
         mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | GravityCompat.START);
 
-        final TypedArray a = context.obtainStyledAttributes(attrs,
+        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                 R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
         mHintEnabled = a.getBoolean(R.styleable.TextInputLayout_hintEnabled, true);
         setHint(a.getText(R.styleable.TextInputLayout_android_hint));
@@ -175,10 +200,28 @@
                 R.styleable.TextInputLayout_counterTextAppearance, 0);
         mCounterOverflowTextAppearance = a.getResourceId(
                 R.styleable.TextInputLayout_counterOverflowTextAppearance, 0);
+
+        mPasswordToggleEnabled = a.getBoolean(
+                R.styleable.TextInputLayout_passwordToggleEnabled, true);
+        mPasswordToggleDrawable = a.getDrawable(R.styleable.TextInputLayout_passwordToggleDrawable);
+        mPasswordToggleContentDesc = a.getText(
+                R.styleable.TextInputLayout_passwordToggleContentDescription);
+        if (a.hasValue(R.styleable.TextInputLayout_passwordToggleTint)) {
+            mHasPasswordToggleTintList = true;
+            mPasswordToggleTintList = a.getColorStateList(
+                    R.styleable.TextInputLayout_passwordToggleTint);
+        }
+        if (a.hasValue(R.styleable.TextInputLayout_passwordToggleTintMode)) {
+            mHasPasswordToggleTintMode = true;
+            mPasswordToggleTintMode = ViewUtils.parseTintMode(
+                    a.getInt(R.styleable.TextInputLayout_passwordToggleTintMode, -1), null);
+        }
+
         a.recycle();
 
         setErrorEnabled(errorEnabled);
         setCounterEnabled(counterEnabled);
+        applyPasswordToggleTint();
 
         if (ViewCompat.getImportantForAccessibility(this)
                 == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
@@ -191,10 +234,16 @@
     }
 
     @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
+    public void addView(View child, int index, final ViewGroup.LayoutParams params) {
         if (child instanceof EditText) {
+            mInputFrame.addView(child, new FrameLayout.LayoutParams(params));
+
+            // Now use the EditText's LayoutParams as our own and update them to make enough space
+            // for the label
+            mInputFrame.setLayoutParams(params);
+            updateInputLayoutMargins();
+
             setEditText((EditText) child);
-            super.addView(child, 0, updateEditTextMargin(params));
         } else {
             // Carry on adding the View...
             super.addView(child, index, params);
@@ -232,8 +281,13 @@
 
         mEditText = editText;
 
+        final boolean hasPasswordTransformation = hasPasswordTransformation();
+
         // Use the EditText's typeface, and it's text size for our expanded text
-        mCollapsingTextHelper.setTypefaces(mEditText.getTypeface());
+        if (!hasPasswordTransformation) {
+            // We don't want a monospace font just because we have a password field
+            mCollapsingTextHelper.setTypefaces(mEditText.getTypeface());
+        }
         mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize());
 
         final int editTextGravity = mEditText.getGravity();
@@ -278,14 +332,17 @@
             adjustIndicatorPadding();
         }
 
+        updatePasswordToggleView();
+
         // Update the label visibility with no animation
         updateLabelState(false);
     }
 
-    private LayoutParams updateEditTextMargin(ViewGroup.LayoutParams lp) {
+    private void updateInputLayoutMargins() {
         // Create/update the LayoutParams so that we can add enough top margin
         // to the EditText so make room for the label
-        LayoutParams llp = lp instanceof LayoutParams ? (LayoutParams) lp : new LayoutParams(lp);
+        final LayoutParams lp = (LayoutParams) mInputFrame.getLayoutParams();
+        final int newTopMargin;
 
         if (mHintEnabled) {
             if (mTmpPaint == null) {
@@ -293,12 +350,15 @@
             }
             mTmpPaint.setTypeface(mCollapsingTextHelper.getCollapsedTypeface());
             mTmpPaint.setTextSize(mCollapsingTextHelper.getCollapsedTextSize());
-            llp.topMargin = (int) -mTmpPaint.ascent();
+            newTopMargin = (int) -mTmpPaint.ascent();
         } else {
-            llp.topMargin = 0;
+            newTopMargin = 0;
         }
 
-        return llp;
+        if (newTopMargin != lp.topMargin) {
+            lp.topMargin = newTopMargin;
+            mInputFrame.requestLayout();
+        }
     }
 
     private void updateLabelState(boolean animate) {
@@ -404,8 +464,7 @@
 
             // Now update the EditText top margin
             if (mEditText != null) {
-                final LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams());
-                mEditText.setLayoutParams(lp);
+                updateInputLayoutMargins();
             }
         }
     }
@@ -432,11 +491,8 @@
 
         if (mEditText != null) {
             updateLabelState(false);
-
             // Text size might have changed so update the top margin
-            LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams());
-            mEditText.setLayoutParams(lp);
-            mEditText.requestLayout();
+            updateInputLayoutMargins();
         }
     }
 
@@ -874,16 +930,280 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        updatePasswordToggleView();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    private void updatePasswordToggleView() {
+        if (shouldShowPasswordIcon()) {
+            if (mPasswordToggleView == null) {
+                mPasswordToggleView = (CheckableImageButton) LayoutInflater.from(getContext())
+                        .inflate(R.layout.design_text_input_password_icon, mInputFrame, false);
+                mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
+                mPasswordToggleView.setContentDescription(mPasswordToggleContentDesc);
+                mInputFrame.addView(mPasswordToggleView);
+
+                mPasswordToggleView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View view) {
+                        passwordVisibilityToggleRequested();
+                    }
+                });
+            }
+
+            mPasswordToggleView.setVisibility(VISIBLE);
+
+            // We need to add a dummy drawable as the end compound drawable so that the text is
+            // indented and doesn't display below the toggle view
+            if (mPasswordToggleDummyDrawable == null) {
+                mPasswordToggleDummyDrawable = new ColorDrawable();
+            }
+            mPasswordToggleDummyDrawable.setBounds(0, 0, mPasswordToggleView.getMeasuredWidth(), 1);
+
+            final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
+            TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
+                    mPasswordToggleDummyDrawable, compounds[2]);
+
+            // Copy over the EditText's padding so that we match
+            mPasswordToggleView.setPadding(mEditText.getPaddingLeft(),
+                    mEditText.getPaddingTop(), mEditText.getPaddingRight(),
+                    mEditText.getPaddingBottom());
+        } else {
+            if (mPasswordToggleView != null && mPasswordToggleView.getVisibility() == VISIBLE) {
+                mPasswordToggleView.setVisibility(View.GONE);
+            }
+
+            // Make sure that we remove the dummy end compound drawable
+            final Drawable[] compounds = TextViewCompat.getCompoundDrawablesRelative(mEditText);
+            TextViewCompat.setCompoundDrawablesRelative(mEditText, compounds[0], compounds[1],
+                    null, compounds[2]);
+        }
+    }
+
+    /**
+     * Set the icon to use for the password visibility toggle button.
+     *
+     * <p>If you use an icon you should also set a description for its action
+     * using {@link #setPasswordVisibilityToggleContentDescription(CharSequence)}.
+     * This is used for accessibility.</p>
+     *
+     * @param resId resource id of the drawable to set, or 0 to clear the icon
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
+     */
+    public void setPasswordVisibilityToggleDrawable(@DrawableRes int resId) {
+        setPasswordVisibilityToggleDrawable(resId != 0
+                ? AppCompatDrawableManager.get().getDrawable(getContext(), resId)
+                : null);
+    }
+
+    /**
+     * Set the icon to use for the password visibility toggle button.
+     *
+     * <p>If you use an icon you should also set a description for its action
+     * using {@link #setPasswordVisibilityToggleContentDescription(CharSequence)}.
+     * This is used for accessibility.</p>
+     *
+     * @param icon Drawable to set, may be null to clear the icon
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
+     */
+    public void setPasswordVisibilityToggleDrawable(@Nullable Drawable icon) {
+        mPasswordToggleDrawable = icon;
+        if (mPasswordToggleView != null) {
+            mPasswordToggleView.setImageDrawable(icon);
+        }
+    }
+
+    /**
+     * Set a content description for the navigation button if one is present.
+     *
+     * <p>The content description will be read via screen readers or other accessibility
+     * systems to explain the action of the password visibility toggle.</p>
+     *
+     * @param resId Resource ID of a content description string to set,
+     *              or 0 to clear the description
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleContentDescription
+     */
+    public void setPasswordVisibilityToggleContentDescription(@StringRes int resId) {
+        setPasswordVisibilityToggleContentDescription(
+                resId != 0 ? getResources().getText(resId) : null);
+    }
+
+    /**
+     * Set a content description for the navigation button if one is present.
+     *
+     * <p>The content description will be read via screen readers or other accessibility
+     * systems to explain the action of the password visibility toggle.</p>
+     *
+     * @param description Content description to set, or null to clear the content description
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleContentDescription
+     */
+    public void setPasswordVisibilityToggleContentDescription(@Nullable CharSequence description) {
+        mPasswordToggleContentDesc = description;
+        if (mPasswordToggleView != null) {
+            mPasswordToggleView.setContentDescription(description);
+        }
+    }
+
+    /**
+     * Returns the icon currently used for the password visibility toggle button.
+     *
+     * @see #setPasswordVisibilityToggleDrawable(Drawable)
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleDrawable
+     */
+    @Nullable
+    public Drawable getPasswordVisibilityToggleDrawable() {
+        return mPasswordToggleDrawable;
+    }
+
+    /**
+     * Returns the currently configured content description for the password visibility
+     * toggle button.
+     *
+     * <p>This will be used to describe the navigation action to users through mechanisms
+     * such as screen readers.</p>
+     */
+    @Nullable
+    public CharSequence getPasswordVisibilityToggleContentDescription() {
+        return mPasswordToggleContentDesc;
+    }
+
+    /**
+     * Returns whether the password visibility toggle functionality is currently enabled.
+     *
+     * @see #setPasswordVisibilityToggleEnabled(boolean)
+     */
+    public boolean isPasswordVisibilityToggleEnabled() {
+        return mPasswordToggleEnabled;
+    }
+
+    /**
+     * Returns whether the password visibility toggle functionality is enabled or not.
+     *
+     * <p>When enabled, a button is placed at the end of the EditText which enables the user
+     * to switch between the field's input being visibly disguised or not.</p>
+     *
+     * @param enabled true to enable the functionality
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleEnabled
+     */
+    public void setPasswordVisibilityToggleEnabled(final boolean enabled) {
+        if (mPasswordToggleEnabled != enabled) {
+            mPasswordToggleEnabled = enabled;
+
+            if (!enabled && mPasswordToggledVisible) {
+                // If the toggle is no longer enabled, but we remove the PasswordTransformation
+                // to make the password visible, add it back
+                mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+            }
+
+            // Reset the visibility tracking flag
+            mPasswordToggledVisible = false;
+
+            updatePasswordToggleView();
+        }
+    }
+
+    /**
+     * Applies a tint to the the password visibility toggle drawable. Does not modify the current
+     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     *
+     * <p>Subsequent calls to {@link #setPasswordVisibilityToggleDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and tint mode using
+     * {@link DrawableCompat#setTintList(Drawable, ColorStateList)}.</p>
+     *
+     * @param tintList the tint to apply, may be null to clear tint
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleTint
+     */
+    public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintList) {
+        mPasswordToggleTintList = tintList;
+        mHasPasswordToggleTintList = true;
+        applyPasswordToggleTint();
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setPasswordVisibilityToggleTintList(ColorStateList)} to the password
+     * visibility toggle drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.</p>
+     *
+     * @param mode the blending mode used to apply the tint, may be null to clear tint
+     *
+     * @attr ref android.support.design.R.styleable#TextInputLayout_passwordToggleTintMode
+     */
+    public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode) {
+        mPasswordToggleTintMode = mode;
+        mHasPasswordToggleTintMode = true;
+        applyPasswordToggleTint();
+    }
+
+   private void passwordVisibilityToggleRequested() {
+       if (mPasswordToggleEnabled) {
+           // Store the current cursor position
+           final int selection = mEditText.getSelectionEnd();
+
+           if (hasPasswordTransformation()) {
+               mEditText.setTransformationMethod(null);
+               mPasswordToggledVisible = true;
+           } else {
+               mEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
+               mPasswordToggledVisible = false;
+           }
+
+           mPasswordToggleView.setChecked(mPasswordToggledVisible);
+
+           // And restore the cursor position
+           mEditText.setSelection(selection);
+       }
+    }
+
+    private boolean hasPasswordTransformation() {
+        return mEditText != null
+                && mEditText.getTransformationMethod() instanceof PasswordTransformationMethod;
+    }
+
+    private boolean shouldShowPasswordIcon() {
+        return mPasswordToggleEnabled && (hasPasswordTransformation() || mPasswordToggledVisible);
+    }
+
+    private void applyPasswordToggleTint() {
+        if (mPasswordToggleDrawable != null
+                && (mHasPasswordToggleTintList || mHasPasswordToggleTintMode)) {
+            mPasswordToggleDrawable = DrawableCompat.wrap(mPasswordToggleDrawable).mutate();
+
+            if (mHasPasswordToggleTintList) {
+                DrawableCompat.setTintList(mPasswordToggleDrawable, mPasswordToggleTintList);
+            }
+            if (mHasPasswordToggleTintMode) {
+                DrawableCompat.setTintMode(mPasswordToggleDrawable, mPasswordToggleTintMode);
+            }
+
+            if (mPasswordToggleView != null
+                    && mPasswordToggleView.getDrawable() != mPasswordToggleDrawable) {
+                mPasswordToggleView.setImageDrawable(mPasswordToggleDrawable);
+            }
+        }
+    }
+
+    @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
         if (mHintEnabled && mEditText != null) {
-            final int l = mEditText.getLeft() + mEditText.getCompoundPaddingLeft();
-            final int r = mEditText.getRight() - mEditText.getCompoundPaddingRight();
+            final Rect rect = mTmpRect;
+            ViewGroupUtils.getDescendantRect(this, mEditText, rect);
 
-            mCollapsingTextHelper.setExpandedBounds(l,
-                    mEditText.getTop() + mEditText.getCompoundPaddingTop(),
-                    r, mEditText.getBottom() - mEditText.getCompoundPaddingBottom());
+            final int l = rect.left + mEditText.getCompoundPaddingLeft();
+            final int r = rect.right - mEditText.getCompoundPaddingRight();
+
+            mCollapsingTextHelper.setExpandedBounds(
+                    l, rect.top + mEditText.getCompoundPaddingTop(),
+                    r, rect.bottom - mEditText.getCompoundPaddingBottom());
 
             // Set the collapsed bounds to be the the full height (minus padding) to match the
             // EditText's editable area
diff --git a/design/src/android/support/design/widget/ViewUtils.java b/design/src/android/support/design/widget/ViewUtils.java
index e2eedb2..f49d836 100644
--- a/design/src/android/support/design/widget/ViewUtils.java
+++ b/design/src/android/support/design/widget/ViewUtils.java
@@ -16,6 +16,7 @@
 
 package android.support.design.widget;
 
+import android.graphics.PorterDuff;
 import android.os.Build;
 
 class ViewUtils {
@@ -38,4 +39,21 @@
         return (a == b) || (a != null && a.equals(b));
     }
 
+    static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
+        switch (value) {
+            case 3:
+                return PorterDuff.Mode.SRC_OVER;
+            case 5:
+                return PorterDuff.Mode.SRC_IN;
+            case 9:
+                return PorterDuff.Mode.SRC_ATOP;
+            case 14:
+                return PorterDuff.Mode.MULTIPLY;
+            case 15:
+                return PorterDuff.Mode.SCREEN;
+            default:
+                return defaultMode;
+        }
+    }
+
 }
diff --git a/design/tests/res/layout/design_text_input.xml b/design/tests/res/layout/design_text_input.xml
index 1312b50..79b6446 100644
--- a/design/tests/res/layout/design_text_input.xml
+++ b/design/tests/res/layout/design_text_input.xml
@@ -36,4 +36,19 @@
 
     </android.support.design.widget.TextInputLayout>
 
+    <android.support.design.widget.TextInputLayout
+        android:id="@+id/textinput_password"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:passwordToggleEnabled="true">
+
+        <android.support.design.widget.TextInputEditText
+            android:id="@+id/textinput_edittext_pwd"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/textinput_hint"
+            android:inputType="textPassword"/>
+
+    </android.support.design.widget.TextInputLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java b/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
index 3360029..92ba267 100755
--- a/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
+++ b/design/tests/src/android/support/design/testutils/TextInputLayoutActions.java
@@ -23,6 +23,7 @@
 import android.support.test.espresso.ViewAction;
 import android.support.v4.widget.DrawerLayout;
 import android.view.View;
+import android.widget.TextView;
 
 import org.hamcrest.Matcher;
 
@@ -75,4 +76,30 @@
             }
         };
     }
+
+    public static ViewAction setPasswordVisibilityToggleEnabled(final boolean enabled) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextInputLayout.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Sets the error";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextInputLayout layout = (TextInputLayout) view;
+                layout.setPasswordVisibilityToggleEnabled(enabled);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+
 }
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 558f474..d612932 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -16,22 +16,28 @@
 
 package android.support.design.widget;
 
+import android.app.Activity;
+import android.support.design.test.R;
+import android.support.test.annotation.UiThreadTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.widget.EditText;
+
+import org.junit.Test;
+
+import static android.support.design.testutils.TestUtilsActions.setText;
 import static android.support.design.testutils.TextInputLayoutActions.setError;
 import static android.support.design.testutils.TextInputLayoutActions.setErrorEnabled;
+import static android.support.design.testutils.TextInputLayoutActions.setPasswordVisibilityToggleEnabled;
 import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withChild;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.CoreMatchers.not;
-
-import android.support.design.test.R;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Test;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 @SmallTest
 public class TextInputLayoutTest extends BaseInstrumentationTestCase<TextInputLayoutActivity> {
@@ -39,6 +45,8 @@
     private static final String ERROR_MESSAGE_1 = "An error has occured";
     private static final String ERROR_MESSAGE_2 = "Some other error has occured";
 
+    private static final String INPUT_TEXT = "Random input text";
+
     public TextInputLayoutTest() {
         super(TextInputLayoutActivity.class);
     }
@@ -73,4 +81,43 @@
         onView(withText(ERROR_MESSAGE_2)).check(matches(isDisplayed()));
     }
 
+    @Test
+    @UiThreadTest
+    public void testPasswordToggleClick() {
+        // Set some text on the EditText
+        onView(withId(R.id.textinput_edittext_pwd)).perform(setText(INPUT_TEXT));
+
+        final Activity activity = mActivityTestRule.getActivity();
+        final EditText textInput = (EditText) activity.findViewById(R.id.textinput_edittext_pwd);
+
+        // Assert that the password is disguised
+        assertNotEquals(INPUT_TEXT, textInput.getLayout().getText());
+
+        // Now click the toggle button
+        onView(withId(R.id.text_input_password_toggle)).perform(click());
+
+        // And assert that the password is not disguised
+        assertEquals(INPUT_TEXT, textInput.getLayout().getText());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPasswordToggleDisable() {
+        final Activity activity = mActivityTestRule.getActivity();
+        final EditText textInput = (EditText) activity.findViewById(R.id.textinput_edittext_pwd);
+
+        // Set some text on the EditText
+        onView(withId(R.id.textinput_edittext_pwd)).perform(setText(INPUT_TEXT));
+        // Assert that the password is disguised
+        assertNotEquals(INPUT_TEXT, textInput.getLayout().getText());
+
+        // Disable the password toggle
+        onView(withId(R.id.text_input_password_toggle))
+                .perform(setPasswordVisibilityToggleEnabled(false));
+
+        // Check that the password toggle view is not visible
+        onView(withId(R.id.text_input_password_toggle)).check(matches(not(isDisplayed())));
+        // ...and that the password is not disguised
+        assertEquals(INPUT_TEXT, textInput.getLayout().getText());
+    }
 }
diff --git a/samples/SupportDesignDemos/res/layout/design_text_input.xml b/samples/SupportDesignDemos/res/layout/design_text_input.xml
index bd5f69b..0729d28 100644
--- a/samples/SupportDesignDemos/res/layout/design_text_input.xml
+++ b/samples/SupportDesignDemos/res/layout/design_text_input.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
      Copyright (C) 2015 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,62 +13,66 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              xmlns:app="http://schemas.android.com/apk/res-auto"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="vertical"
-              android:padding="16dp">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:padding="16dp">
 
-    <android.support.design.widget.TextInputLayout
+        <android.support.design.widget.TextInputLayout
             android:id="@+id/input_username"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             app:errorEnabled="true">
 
-        <android.support.design.widget.TextInputEditText
+            <android.support.design.widget.TextInputEditText
                 android:id="@+id/edit_username"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:hint="@string/form_username"/>
+                android:hint="@string/form_username" />
 
-    </android.support.design.widget.TextInputLayout>
+        </android.support.design.widget.TextInputLayout>
 
-    <LinearLayout android:layout_width="match_parent"
-                  android:layout_height="wrap_content"
-                  android:orientation="horizontal">
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
 
-        <Button
+            <Button
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/show_error"
-                android:onClick="showError"/>
+                android:onClick="showError"
+                android:text="@string/show_error" />
 
-        <Button
+            <Button
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:text="@string/clear_error"
-                android:onClick="clearError"/>
+                android:onClick="clearError"
+                android:text="@string/clear_error" />
 
-    </LinearLayout>
+        </LinearLayout>
 
-    <android.support.design.widget.TextInputLayout
+        <android.support.design.widget.TextInputLayout
             android:id="@+id/input_email"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="8dp"
             app:errorEnabled="true">
 
-        <android.support.design.widget.TextInputEditText
+            <android.support.design.widget.TextInputEditText
                 android:id="@+id/edit_email"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:hint="@string/form_email"/>
+                android:hint="@string/form_email" />
 
-    </android.support.design.widget.TextInputLayout>
+        </android.support.design.widget.TextInputLayout>
 
-    <android.support.design.widget.TextInputLayout
+        <android.support.design.widget.TextInputLayout
             android:id="@+id/input_description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -77,12 +80,49 @@
             app:counterEnabled="true"
             app:counterMaxLength="30">
 
-        <EditText
+            <EditText
                 android:id="@+id/edit_description"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:hint="@string/form_description"/>
+                android:hint="@string/form_description" />
 
-    </android.support.design.widget.TextInputLayout>
+        </android.support.design.widget.TextInputLayout>
 
-</LinearLayout>
\ No newline at end of file
+        <android.support.design.widget.TextInputLayout
+            android:id="@+id/input_password"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="8dp"
+            app:passwordToggleEnabled="true">
+
+            <EditText
+                android:id="@+id/edit_password"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:hint="@string/form_password"
+                android:inputType="textPassword" />
+
+        </android.support.design.widget.TextInputLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="setPasswordEnabled"
+                android:text="@string/password_enable" />
+
+            <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="setPasswordDisabled"
+                android:text="@string/password_disable" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/samples/SupportDesignDemos/res/values/strings.xml b/samples/SupportDesignDemos/res/values/strings.xml
index da855f8..6cd37d4 100644
--- a/samples/SupportDesignDemos/res/values/strings.xml
+++ b/samples/SupportDesignDemos/res/values/strings.xml
@@ -67,8 +67,11 @@
     <string name="form_username">Username</string>
     <string name="form_email">Email address</string>
     <string name="form_description">Description</string>
+    <string name="form_password">Password field</string>
     <string name="show_error">Show error</string>
     <string name="clear_error">Clear error</string>
+    <string name="password_enable">Enable Password</string>
+    <string name="password_disable">Disable Password</string>
 
     <string name="design_snackbar_basic">Snackbar/Usage</string>
     <string name="design_snackbar_fab">Snackbar/Coordinated with FAB</string>
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
index 63ac031..a5e382d 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TextInputLayoutUsage.java
@@ -29,6 +29,7 @@
 public class TextInputLayoutUsage extends AppCompatActivity {
 
     private TextInputLayout mUsernameInputLayout;
+    private TextInputLayout mPasswordInputLayout;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -36,6 +37,7 @@
         setContentView(R.layout.design_text_input);
 
         mUsernameInputLayout = (TextInputLayout) findViewById(R.id.input_username);
+        mPasswordInputLayout = (TextInputLayout) findViewById(R.id.input_password);
     }
 
     public void showError(View view) {
@@ -46,4 +48,12 @@
         mUsernameInputLayout.setError(null);
     }
 
+    public void setPasswordEnabled(View view) {
+        mPasswordInputLayout.setPasswordVisibilityToggleEnabled(true);
+    }
+
+    public void setPasswordDisabled(View view) {
+        mPasswordInputLayout.setPasswordVisibilityToggleEnabled(false);
+    }
+
 }