Merge "Support library gradle improvements" into mnc-ub-dev
diff --git a/annotations/src/android/support/annotation/VisibleForTesting.java b/annotations/src/android/support/annotation/VisibleForTesting.java
index bb02ab4..0c893ff 100644
--- a/annotations/src/android/support/annotation/VisibleForTesting.java
+++ b/annotations/src/android/support/annotation/VisibleForTesting.java
@@ -17,12 +17,12 @@
 
 import java.lang.annotation.Retention;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
 
 /**
  * Denotes that the class, method or field has its visibility relaxed, so that it is more widely
  * visible than otherwise necessary to make code testable.
  */
-@Retention(SOURCE)
+@Retention(CLASS)
 public @interface VisibleForTesting {
 }
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index 7ff70c2..7a9e3c1 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -109,7 +109,7 @@
         <attr name="android:text" />
         <!-- Icon to display in the tab. -->
         <attr name="android:icon" />
-        <!-- A reference to a layout to be displayed in the tab. -->
+        <!-- A reference to a layout resource to be displayed in the tab. -->
         <attr name="android:layout" />
     </declare-styleable>
 
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index cb95f4f..609795f 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -238,12 +238,14 @@
         updateMenuView(false);
     }
 
+    @Nullable
     public Drawable getItemBackground() {
         return mItemBackground;
     }
 
-    public void setItemBackground(Drawable itemBackground) {
+    public void setItemBackground(@Nullable Drawable itemBackground) {
         mItemBackground = itemBackground;
+        updateMenuView(false);
     }
 
     public void setUpdateSuspended(boolean updateSuspended) {
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 9a38c7d..8768826 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -105,8 +105,7 @@
     public CollapsingTextHelper(View view) {
         mView = view;
 
-        mTextPaint = new TextPaint();
-        mTextPaint.setAntiAlias(true);
+        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
 
         mCollapsedBounds = new Rect();
         mExpandedBounds = new Rect();
@@ -436,10 +435,6 @@
 
             final float ascent;
             final float descent;
-
-            // Update the TextPaint to the current text size
-            mTextPaint.setTextSize(mCurrentTextSize);
-
             if (drawTexture) {
                 ascent = mTextureAscent * mScale;
                 descent = mTextureDescent * mScale;
@@ -536,6 +531,8 @@
         if (mTextToDraw == null || updateDrawText) {
             mTextPaint.setTextSize(mCurrentTextSize);
             mTextPaint.setTypeface(mCurrentTypeface);
+            // Use linear text scaling if we're scaling the canvas
+            mTextPaint.setLinearText(mScale != 1f);
 
             // If we don't currently have text to draw, or the text size has changed, ellipsize...
             final CharSequence title = TextUtils.ellipsize(mText, mTextPaint,
diff --git a/design/src/android/support/design/widget/NavigationView.java b/design/src/android/support/design/widget/NavigationView.java
index a73dc65..42f4eed 100644
--- a/design/src/android/support/design/widget/NavigationView.java
+++ b/design/src/android/support/design/widget/NavigationView.java
@@ -297,7 +297,7 @@
     }
 
     /**
-     * Returns the tint which is applied to our item's icons.
+     * Returns the tint which is applied to our menu items' icons.
      *
      * @see #setItemIconTintList(ColorStateList)
      *
@@ -309,7 +309,7 @@
     }
 
     /**
-     * Set the tint which is applied to our item's icons.
+     * Set the tint which is applied to our menu items' icons.
      *
      * @param tint the tint to apply.
      *
@@ -320,7 +320,7 @@
     }
 
     /**
-     * Returns the tint which is applied to our item's icons.
+     * Returns the tint which is applied to our menu items' icons.
      *
      * @see #setItemTextColor(ColorStateList)
      *
@@ -332,7 +332,7 @@
     }
 
     /**
-     * Set the text color which is text to our items.
+     * Set the text color to be used on our menu items.
      *
      * @see #getItemTextColor()
      *
@@ -343,18 +343,19 @@
     }
 
     /**
-     * Returns the background drawable for the menu items.
+     * Returns the background drawable for our menu items.
      *
      * @see #setItemBackgroundResource(int)
      *
      * @attr ref R.styleable#NavigationView_itemBackground
      */
+    @Nullable
     public Drawable getItemBackground() {
         return mPresenter.getItemBackground();
     }
 
     /**
-     * Set the background of the menu items to the given resource.
+     * Set the background of our menu items to the given resource.
      *
      * @param resId The identifier of the resource.
      *
@@ -365,12 +366,12 @@
     }
 
     /**
-     * Set the background of the menu items to a given resource. The resource should refer to
-     * a Drawable object or 0 to use the background background.
+     * Set the background of our menu items to a given resource. The resource should refer to
+     * a Drawable object or null to use the default background set on this navigation menu.
      *
      * @attr ref R.styleable#NavigationView_itemBackground
      */
-    public void setItemBackground(Drawable itemBackground) {
+    public void setItemBackground(@Nullable Drawable itemBackground) {
         mPresenter.setItemBackground(itemBackground);
     }
 
diff --git a/design/tests/AndroidManifest.xml b/design/tests/AndroidManifest.xml
index af26af3..4f65f92 100755
--- a/design/tests/AndroidManifest.xml
+++ b/design/tests/AndroidManifest.xml
@@ -39,6 +39,10 @@
                 android:name="android.support.design.widget.BottomSheetBehaviorActivity"/>
 
         <activity
+            android:theme="@style/Theme.AppCompat.NoActionBar"
+            android:name="android.support.design.widget.NavigationViewActivity"/>
+
+        <activity
                 android:name="android.support.v7.app.AppCompatActivity"/>
 
     </application>
diff --git a/design/tests/res/color/color_state_list_lilac.xml b/design/tests/res/color/color_state_list_lilac.xml
new file mode 100644
index 0000000..f0b2791
--- /dev/null
+++ b/design/tests/res/color/color_state_list_lilac.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">
+    <item android:state_enabled="false" android:color="@color/lilac_disabled" />
+    <item android:color="@color/lilac_default"/>
+</selector>
+
diff --git a/design/tests/res/color/color_state_list_red_translucent.xml b/design/tests/res/color/color_state_list_red_translucent.xml
new file mode 100644
index 0000000..fdf8b2b
--- /dev/null
+++ b/design/tests/res/color/color_state_list_red_translucent.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:color="@color/red_translucent"/>
+</selector>
+
diff --git a/design/tests/res/color/color_state_list_sand.xml b/design/tests/res/color/color_state_list_sand.xml
new file mode 100644
index 0000000..eb472c2
--- /dev/null
+++ b/design/tests/res/color/color_state_list_sand.xml
@@ -0,0 +1,22 @@
+<?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_enabled="false" android:color="@color/sand_disabled" />
+    <item android:state_checked="true" android:color="@color/sand_checked" />
+    <item android:color="@color/sand_default" />
+</selector>
+
diff --git a/design/tests/res/drawable/test_background_blue.xml b/design/tests/res/drawable/test_background_blue.xml
new file mode 100644
index 0000000..fe4bca2
--- /dev/null
+++ b/design/tests/res/drawable/test_background_blue.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid
+        android:color="@color/test_blue" />
+</shape>
\ No newline at end of file
diff --git a/design/tests/res/drawable/test_background_green.xml b/design/tests/res/drawable/test_background_green.xml
new file mode 100644
index 0000000..b90d9bc
--- /dev/null
+++ b/design/tests/res/drawable/test_background_green.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid
+        android:color="@color/test_green" />
+</shape>
\ No newline at end of file
diff --git a/design/tests/res/drawable/test_drawable_blue.xml b/design/tests/res/drawable/test_drawable_blue.xml
new file mode 100644
index 0000000..f228e34
--- /dev/null
+++ b/design/tests/res/drawable/test_drawable_blue.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size
+        android:width="@dimen/drawable_large_size"
+        android:height="@dimen/drawable_small_size" />
+    <solid
+        android:color="@color/test_blue" />
+</shape>
\ No newline at end of file
diff --git a/design/tests/res/drawable/test_drawable_green.xml b/design/tests/res/drawable/test_drawable_green.xml
new file mode 100644
index 0000000..1d83f0f
--- /dev/null
+++ b/design/tests/res/drawable/test_drawable_green.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size
+        android:width="@dimen/drawable_medium_size"
+        android:height="@dimen/drawable_large_size" />
+    <solid
+        android:color="@color/test_green" />
+</shape>
\ No newline at end of file
diff --git a/design/tests/res/drawable/test_drawable_red.xml b/design/tests/res/drawable/test_drawable_red.xml
new file mode 100644
index 0000000..58dbe73
--- /dev/null
+++ b/design/tests/res/drawable/test_drawable_red.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <size
+        android:width="@dimen/drawable_small_size"
+        android:height="@dimen/drawable_medium_size" />
+    <solid
+        android:color="@color/test_red" />
+</shape>
\ No newline at end of file
diff --git a/design/tests/res/drawable/test_drawable_state_list.xml b/design/tests/res/drawable/test_drawable_state_list.xml
new file mode 100644
index 0000000..f125913
--- /dev/null
+++ b/design/tests/res/drawable/test_drawable_state_list.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">
+    <item android:state_checked="true" android:drawable="@drawable/test_background_blue" />
+    <item android:drawable="@drawable/test_background_green" />
+</selector>
+
diff --git a/design/tests/res/layout/design_navigation_view.xml b/design/tests/res/layout/design_navigation_view.xml
new file mode 100644
index 0000000..1789843
--- /dev/null
+++ b/design/tests/res/layout/design_navigation_view.xml
@@ -0,0 +1,54 @@
+<?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.v4.widget.DrawerLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+    <!-- As the main content view, the view below consumes the entire
+         space available using match_parent in both dimensions. Note that
+         this child does not specify android:layout_gravity attribute. -->
+    <FrameLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <!-- android:layout_gravity="start" tells DrawerLayout to treat
+         this as a sliding drawer on the starting side, which is
+         left for left-to-right locales. The navigation view extends
+         the full height of the container. A
+         solid background is used for contrast with the content view.
+         android:fitsSystemWindows="true" tells the system to have
+         DrawerLayout span the full height of the screen, including the
+         system status bar on Lollipop+ versions of the plaform. -->
+    <android.support.design.widget.NavigationView
+        android:id="@+id/start_drawer"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"
+        android:background="#333"
+        android:fitsSystemWindows="true"
+        app:menu="@menu/navigation_view_content"
+        app:itemIconTint="@color/emerald_translucent"
+        app:itemTextColor="@color/emerald_text"
+        app:itemBackground="@color/sand_default"
+        app:itemTextAppearance="@style/TextMediumStyle" />
+
+</android.support.v4.widget.DrawerLayout>
+
diff --git a/design/tests/res/layout/design_navigation_view_header1.xml b/design/tests/res/layout/design_navigation_view_header1.xml
new file mode 100644
index 0000000..2fd6f20
--- /dev/null
+++ b/design/tests/res/layout/design_navigation_view_header1.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/header1"
+    android:layout_width="match_parent"
+    android:layout_height="120dip"
+    android:background="@color/test_red" />
+
diff --git a/design/tests/res/layout/design_navigation_view_header2.xml b/design/tests/res/layout/design_navigation_view_header2.xml
new file mode 100644
index 0000000..77ad32a
--- /dev/null
+++ b/design/tests/res/layout/design_navigation_view_header2.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/header2"
+    android:layout_width="match_parent"
+    android:layout_height="100dip"
+    android:background="@color/test_blue" />
+
diff --git a/design/tests/res/layout/design_navigation_view_header3.xml b/design/tests/res/layout/design_navigation_view_header3.xml
new file mode 100644
index 0000000..b1fd676
--- /dev/null
+++ b/design/tests/res/layout/design_navigation_view_header3.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/header3"
+    android:layout_width="match_parent"
+    android:layout_height="80dip"
+    android:background="@color/test_green" />
+
diff --git a/design/tests/res/menu/navigation_view_content.xml b/design/tests/res/menu/navigation_view_content.xml
new file mode 100644
index 0000000..ee396f0
--- /dev/null
+++ b/design/tests/res/menu/navigation_view_content.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 Google Inc.
+
+     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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <group android:checkableBehavior="single">
+        <item android:id="@+id/destination_home"
+              android:title="@string/navigate_home"
+              android:icon="@drawable/test_drawable_red" />
+        <item android:id="@+id/destination_profile"
+              android:title="@string/navigate_profile"
+              android:icon="@drawable/test_drawable_green" />
+        <item android:id="@+id/destination_people"
+              android:title="@string/navigate_people"
+              android:icon="@drawable/test_drawable_blue" />
+        <item android:id="@+id/destination_settings"
+              android:title="@string/navigate_settings" />
+    </group>
+</menu>
diff --git a/design/tests/res/values/colors.xml b/design/tests/res/values/colors.xml
new file mode 100644
index 0000000..ea8da2a
--- /dev/null
+++ b/design/tests/res/values/colors.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<resources>
+    <color name="emerald_translucent">#8020A060</color>
+    <color name="emerald_text">#30B050</color>
+    <color name="red_translucent">#90FF2040</color>
+
+    <color name="lilac_default">#F080F0</color>
+    <color name="lilac_disabled">#F0A0FF</color>
+    <color name="sand_default">#F0B000</color>
+    <color name="sand_disabled">#FFC080</color>
+    <color name="sand_checked">#FFD0A0</color>
+
+    <color name="test_red">#FF6030</color>
+    <color name="test_green">#50E080</color>
+    <color name="test_blue">#3050CF</color>
+</resources>
diff --git a/design/tests/res/values/dimens.xml b/design/tests/res/values/dimens.xml
index c20e4f8..94c2734 100644
--- a/design/tests/res/values/dimens.xml
+++ b/design/tests/res/values/dimens.xml
@@ -18,4 +18,11 @@
     <dimen name="tab_width_limit_medium">100dip</dimen>
     <dimen name="tab_width_limit_large">120dip</dimen>
     <dimen name="bottom_sheet_peek_height">128dp</dimen>
+
+    <dimen name="text_small_size">16sp</dimen>
+    <dimen name="text_medium_size">20sp</dimen>
+
+    <dimen name="drawable_small_size">12dip</dimen>
+    <dimen name="drawable_medium_size">16dip</dimen>
+    <dimen name="drawable_large_size">20dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/design/tests/res/values/strings.xml b/design/tests/res/values/strings.xml
index 854e711..05dd55e 100644
--- a/design/tests/res/values/strings.xml
+++ b/design/tests/res/values/strings.xml
@@ -15,4 +15,9 @@
 -->
 <resources>
     <string name="tab_layout_text">Tab text!</string>
+
+    <string name="navigate_home">Home</string>
+    <string name="navigate_profile">Profile</string>
+    <string name="navigate_people">People</string>
+    <string name="navigate_settings">Settings</string>
 </resources>
\ No newline at end of file
diff --git a/design/tests/res/values/styles.xml b/design/tests/res/values/styles.xml
new file mode 100644
index 0000000..4fc946b
--- /dev/null
+++ b/design/tests/res/values/styles.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.
+-->
+<resources>
+    <style name="TextSmallStyle" parent="@android:style/TextAppearance">
+        <item name="android:textSize">@dimen/text_small_size</item>
+    </style>
+
+    <style name="TextMediumStyle" parent="@android:style/TextAppearance.Medium">
+        <item name="android:textSize">@dimen/text_medium_size</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/DrawerLayoutActions.java b/design/tests/src/android/support/design/testutils/DrawerLayoutActions.java
new file mode 100755
index 0000000..e4ea867
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/DrawerLayoutActions.java
@@ -0,0 +1,83 @@
+/*
+ * 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.testutils;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.v4.widget.DrawerLayout;
+import android.view.View;
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+
+public class DrawerLayoutActions {
+    /**
+     * Opens the drawer at the specified edge gravity.
+     */
+    public static ViewAction openDrawer(final int drawerEdgeGravity) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(DrawerLayout.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Opens the drawer";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                DrawerLayout drawerLayout = (DrawerLayout) view;
+                drawerLayout.openDrawer(drawerEdgeGravity);
+
+                // Wait for a full second to let the inner ViewDragHelper complete the operation
+                uiController.loopMainThreadForAtLeast(1000);
+            }
+        };
+    }
+
+    /**
+     * Closes the drawer at the specified edge gravity.
+     */
+    public static ViewAction closeDrawer(final int drawerEdgeGravity) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(DrawerLayout.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Closes the drawer";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                DrawerLayout drawerLayout = (DrawerLayout) view;
+                drawerLayout.closeDrawer(drawerEdgeGravity);
+
+                // Wait for a full second to let the inner ViewDragHelper complete the operation
+                uiController.loopMainThreadForAtLeast(1000);
+            }
+        };
+    }
+}
diff --git a/design/tests/src/android/support/design/testutils/NavigationViewActions.java b/design/tests/src/android/support/design/testutils/NavigationViewActions.java
new file mode 100644
index 0000000..66e4630
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/NavigationViewActions.java
@@ -0,0 +1,282 @@
+/*
+ * 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.testutils;
+
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.IdRes;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StyleRes;
+import android.support.design.widget.NavigationView;
+import android.support.design.widget.TabLayout;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.LayoutInflater;
+import android.view.View;
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+
+public class NavigationViewActions {
+    /**
+     * Sets item text appearance on the content of the navigation view.
+     */
+    public static ViewAction setItemTextAppearance(final @StyleRes int resId) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Set item text appearance";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.setItemTextAppearance(resId);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets item text color on the content of the navigation view.
+     */
+    public static ViewAction setItemTextColor(final ColorStateList textColor) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Set item text color";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.setItemTextColor(textColor);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets item background on the content of the navigation view.
+     */
+    public static ViewAction setItemBackground(final @Nullable Drawable itemBackground) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Set item background";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.setItemBackground(itemBackground);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets item background on the content of the navigation view.
+     */
+    public static ViewAction setItemBackgroundResource(final @DrawableRes int resId) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Set item background";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.setItemBackgroundResource(resId);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets item icon tint list on the content of the navigation view.
+     */
+    public static ViewAction setItemIconTintList(final @Nullable ColorStateList tint) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Set item icon tint list";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.setItemIconTintList(tint);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Add the specified view as a header to the navigation view.
+     */
+    public static ViewAction addHeaderView(final @NonNull LayoutInflater inflater,
+            final @LayoutRes int res) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Add header view";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.addHeaderView(inflater.inflate(res, null, false));
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Inflates a view from the specified layout ID and adds it as a header to the navigation view.
+     */
+    public static ViewAction inflateHeaderView(final @LayoutRes int res) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Inflate and add header view";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.inflateHeaderView(res);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Removes a previously added header view from the navigation view.
+     */
+    public static ViewAction removeHeaderView(final @Nullable View headerView) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Remove header view";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.removeHeaderView(headerView);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets checked item on the navigation view.
+     */
+    public static ViewAction setCheckedItem(final @IdRes int id) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "Set checked item";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                NavigationView navigationView = (NavigationView) view;
+                navigationView.setCheckedItem(id);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/design/tests/src/android/support/design/testutils/TestUtils.java b/design/tests/src/android/support/design/testutils/TestUtils.java
new file mode 100644
index 0000000..3255fb1
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/TestUtils.java
@@ -0,0 +1,95 @@
+/*
+ * 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.testutils;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import junit.framework.Assert;
+
+public class TestUtils {
+    /**
+     * Checks whether all the pixels in the specified drawable are of the same specified color.
+     *
+     * In case there is a color mismatch, the behavior of this method depends on the
+     * <code>throwExceptionIfFails</code> parameter. If it is <code>true</code>, this method will
+     * throw an <code>Exception</code> describing the mismatch. Otherwise this method will call
+     * <code>Assert.fail</code> with detailed description of the mismatch.
+     */
+    public static void assertAllPixelsOfColor(String failMessagePrefix, @NonNull Drawable drawable,
+            int drawableWidth, int drawableHeight, boolean callSetBounds, @ColorInt int color,
+            int allowedComponentVariance, boolean throwExceptionIfFails) {
+        // Create a bitmap
+        Bitmap bitmap = Bitmap.createBitmap(drawableWidth, drawableHeight, Bitmap.Config.ARGB_8888);
+        // Create a canvas that wraps the bitmap
+        Canvas canvas = new Canvas(bitmap);
+        if (callSetBounds) {
+            // Configure the drawable to have bounds that match the passed size
+            drawable.setBounds(0, 0, drawableWidth, drawableHeight);
+        }
+        // And ask the drawable to draw itself to the canvas / bitmap
+        drawable.draw(canvas);
+
+        try {
+            int[] rowPixels = new int[drawableWidth];
+            for (int row = 0; row < drawableHeight; row++) {
+                bitmap.getPixels(rowPixels, 0, drawableWidth, 0, row, drawableWidth, 1);
+                for (int column = 0; column < drawableWidth; column++) {
+                    int sourceAlpha = Color.alpha(rowPixels[column]);
+                    int sourceRed = Color.red(rowPixels[column]);
+                    int sourceGreen = Color.green(rowPixels[column]);
+                    int sourceBlue = Color.blue(rowPixels[column]);
+
+                    int expectedAlpha = Color.alpha(color);
+                    int expectedRed = Color.red(color);
+                    int expectedGreen = Color.green(color);
+                    int expectedBlue = Color.blue(color);
+
+                    int varianceAlpha = Math.abs(sourceAlpha - expectedAlpha);
+                    int varianceRed = Math.abs(sourceRed - expectedRed);
+                    int varianceGreen = Math.abs(sourceGreen - expectedGreen);
+                    int varianceBlue = Math.abs(sourceBlue - expectedBlue);
+
+                    boolean isColorMatch = (varianceAlpha <= allowedComponentVariance)
+                            && (varianceRed <= allowedComponentVariance)
+                            && (varianceGreen <= allowedComponentVariance)
+                            && (varianceBlue <= allowedComponentVariance);
+
+                    if (!isColorMatch) {
+                        String mismatchDescription = failMessagePrefix
+                                + ": expected all drawable colors to be ["
+                                + expectedAlpha + "," + expectedRed + ","
+                                + expectedGreen + "," + expectedBlue
+                                + "] but at position (" + row + "," + column + ") found ["
+                                + sourceAlpha + "," + sourceRed + ","
+                                + sourceGreen + "," + sourceBlue + "]";
+                        if (throwExceptionIfFails) {
+                            throw new RuntimeException(mismatchDescription);
+                        } else {
+                            Assert.fail(mismatchDescription);
+                        }
+                    }
+                }
+            }
+        } finally {
+            bitmap.recycle();
+        }
+    }
+}
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java b/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java
index 049e7a5..7d4b75d 100644
--- a/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java
+++ b/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java
@@ -16,10 +16,18 @@
 
 package android.support.design.testutils;
 
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.support.test.espresso.matcher.BoundedMatcher;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.TextViewCompat;
 import android.view.View;
+import android.view.ViewParent;
+import android.widget.TextView;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
 
 public class TestUtilsMatchers {
     /**
@@ -71,4 +79,163 @@
             }
         };
     }
+
+    /**
+     * Returns a matcher that matches TextViews with the specified text size.
+     */
+    public static Matcher withTextSize(final float textSize) {
+        return new BoundedMatcher<View, TextView>(TextView.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final TextView view) {
+                final float ourTextSize = view.getTextSize();
+                if (Math.abs(textSize - ourTextSize) > 1.0f) {
+                    failedCheckDescription =
+                            "text size " + ourTextSize + " is different than expected " + textSize;
+                    return false;
+                }
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches TextViews with the specified text color.
+     */
+    public static Matcher withTextColor(final @ColorInt int textColor) {
+        return new BoundedMatcher<View, TextView>(TextView.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final TextView view) {
+                final @ColorInt int ourTextColor = view.getCurrentTextColor();
+                if (ourTextColor != textColor) {
+                    int ourAlpha = Color.alpha(ourTextColor);
+                    int ourRed = Color.red(ourTextColor);
+                    int ourGreen = Color.green(ourTextColor);
+                    int ourBlue = Color.blue(ourTextColor);
+
+                    int expectedAlpha = Color.alpha(textColor);
+                    int expectedRed = Color.red(textColor);
+                    int expectedGreen = Color.green(textColor);
+                    int expectedBlue = Color.blue(textColor);
+
+                    failedCheckDescription =
+                            "expected color to be ["
+                                    + expectedAlpha + "," + expectedRed + ","
+                                    + expectedGreen + "," + expectedBlue
+                                    + "] but found ["
+                                    + ourAlpha + "," + ourRed + ","
+                                    + ourGreen + "," + ourBlue + "]";
+                    return false;
+                }
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches TextViews whose start drawable is filled with the specified
+     * fill color.
+     */
+    public static Matcher withStartDrawableFilledWith(final @ColorInt int fillColor,
+            final int allowedComponentVariance) {
+        return new BoundedMatcher<View, TextView>(TextView.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final TextView view) {
+                Drawable[] compoundDrawables = view.getCompoundDrawables();
+                boolean isRtl =
+                        (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL);
+                Drawable startDrawable = isRtl ? compoundDrawables[2] : compoundDrawables[0];
+                if (startDrawable == null) {
+                    failedCheckDescription = "no start drawable";
+                    return false;
+                }
+                try {
+                    TestUtils.assertAllPixelsOfColor("",
+                            startDrawable, startDrawable.getIntrinsicWidth(),
+                            startDrawable.getIntrinsicHeight(), true,
+                            fillColor, allowedComponentVariance, true);
+                } catch (Throwable t) {
+                    failedCheckDescription = t.getMessage();
+                    return false;
+                }
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches Views with the specified background fill color.
+     */
+    public static Matcher withBackgroundFill(final @ColorInt int fillColor) {
+        return new BoundedMatcher<View, View>(View.class) {
+            private String failedCheckDescription;
+
+            @Override
+            public void describeTo(final Description description) {
+                description.appendText(failedCheckDescription);
+            }
+
+            @Override
+            public boolean matchesSafely(final View view) {
+                Drawable background = view.getBackground();
+                try {
+                    TestUtils.assertAllPixelsOfColor("",
+                            background, view.getWidth(), view.getHeight(), true,
+                            fillColor, 0, true);
+                } catch (Throwable t) {
+                    failedCheckDescription = t.getMessage();
+                    return false;
+                }
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Returns a matcher that matches {@link View}s based on the given parent type.
+     *
+     * @param parentMatcher the type of the parent to match on
+     */
+    public static Matcher<View> isChildOfA(final Matcher<View> parentMatcher) {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("is child of a: ");
+                parentMatcher.describeTo(description);
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                final ViewParent viewParent = view.getParent();
+                if (!(viewParent instanceof View)) {
+                    return false;
+                }
+                if (parentMatcher.matches(viewParent)) {
+                    return true;
+                }
+                return false;
+            }
+        };
+    }
+
 }
diff --git a/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java b/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
index bda213c..0c09e7b 100644
--- a/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
+++ b/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
@@ -17,26 +17,17 @@
 package android.support.design.widget;
 
 import android.app.Activity;
-import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.test.ActivityInstrumentationTestCase2;
-import org.junit.Before;
+import org.junit.Rule;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-public abstract class BaseInstrumentationTestCase<A extends Activity>
-        extends ActivityInstrumentationTestCase2<A> {
+public abstract class BaseInstrumentationTestCase<A extends Activity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
 
     protected BaseInstrumentationTestCase(Class<A> activityClass) {
-        super(activityClass);
+        mActivityTestRule = new ActivityTestRule<A>(activityClass);
     }
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
-        getActivity();
-    }
-
 }
diff --git a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
index 9634a58..3d21033 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetBehaviorTest.java
@@ -16,11 +16,9 @@
 
 package android.support.design.widget;
 
-import org.hamcrest.Matcher;
-import org.junit.Test;
-
 import android.support.annotation.NonNull;
 import android.support.design.test.R;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.espresso.Espresso;
 import android.support.test.espresso.IdlingResource;
 import android.support.test.espresso.action.CoordinatesProvider;
@@ -35,6 +33,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
+import org.hamcrest.Matcher;
+import org.junit.Test;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.not;
@@ -196,9 +196,9 @@
 
     @Test
     @MediumTest
-    public void testInvisible() throws Throwable {
+    public void testInvisible() {
         // Make the bottomsheet invisible
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 getBottomSheet().setVisibility(View.INVISIBLE);
@@ -217,7 +217,7 @@
                         }, Press.FINGER),
                         not(ViewMatchers.isDisplayed())));
         // Check that the bottom sheet stays the same collapsed state
-        runTestOnUiThread(new Runnable() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
                 assertThat(getBehavior().getState(), is(BottomSheetBehavior.STATE_COLLAPSED));
@@ -225,10 +225,15 @@
         });
     }
 
-    private void checkSetState(int state, Matcher<View> matcher) {
+    private void checkSetState(final int state, Matcher<View> matcher) {
         registerIdlingResourceCallback();
         try {
-            getBehavior().setState(state);
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+                @Override
+                public void run() {
+                    getBehavior().setState(state);
+                }
+            });
             Espresso.onView(ViewMatchers.withId(R.id.bottom_sheet))
                     .check(ViewAssertions.matches(matcher));
             assertThat(getBehavior().getState(), is(state));
@@ -251,15 +256,15 @@
     }
 
     private ViewGroup getBottomSheet() {
-        return getActivity().mBottomSheet;
+        return mActivityTestRule.getActivity().mBottomSheet;
     }
 
     private BottomSheetBehavior getBehavior() {
-        return getActivity().mBehavior;
+        return mActivityTestRule.getActivity().mBehavior;
     }
 
     private CoordinatorLayout getCoordinatorLayout() {
-        return getActivity().mCoordinatorLayout;
+        return mActivityTestRule.getActivity().mCoordinatorLayout;
     }
 
 }
diff --git a/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java b/design/tests/src/android/support/design/widget/NavigationViewActivity.java
similarity index 64%
copy from v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java
copy to design/tests/src/android/support/design/widget/NavigationViewActivity.java
index 7366127..c3e810c 100644
--- a/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java
+++ b/design/tests/src/android/support/design/widget/NavigationViewActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.support.design.widget;
 
-package android.support.v4.widget.test;
+import android.support.design.test.R;
 
-
-import android.app.Activity;
-
-public class TextViewTestActivity extends Activity {
-
+public class NavigationViewActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.design_navigation_view;
+    }
 }
diff --git a/design/tests/src/android/support/design/widget/NavigationViewTest.java b/design/tests/src/android/support/design/widget/NavigationViewTest.java
new file mode 100755
index 0000000..fd913dc
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/NavigationViewTest.java
@@ -0,0 +1,491 @@
+/*
+ * 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.res.Resources;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IdRes;
+import android.support.design.test.R;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.widget.RecyclerView;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static android.support.design.testutils.DrawerLayoutActions.closeDrawer;
+import static android.support.design.testutils.DrawerLayoutActions.openDrawer;
+import static android.support.design.testutils.NavigationViewActions.*;
+import static android.support.design.testutils.TestUtilsMatchers.*;
+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.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static org.hamcrest.core.AllOf.allOf;
+import static org.junit.Assert.*;
+
+public class NavigationViewTest
+        extends BaseInstrumentationTestCase<NavigationViewActivity> {
+    private static final int[] MENU_CONTENT_ITEM_IDS = { R.id.destination_home,
+            R.id.destination_profile, R.id.destination_people, R.id.destination_settings };
+    private Map<Integer, String> mMenuStringContent;
+
+    private DrawerLayout mDrawerLayout;
+
+    private NavigationView mNavigationView;
+
+    private int mLastSelectedNavigationItemId;
+
+    public NavigationViewTest() {
+        super(NavigationViewActivity.class);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        final NavigationViewActivity activity = mActivityTestRule.getActivity();
+        mDrawerLayout = (DrawerLayout) activity.findViewById(R.id.drawer_layout);
+        mNavigationView = (NavigationView) mDrawerLayout.findViewById(R.id.start_drawer);
+
+        // Close the drawer to reset the state for the next test
+        onView(withId(R.id.drawer_layout)).perform(closeDrawer(GravityCompat.START));
+
+        final Resources res = activity.getResources();
+        mMenuStringContent = new HashMap<>(MENU_CONTENT_ITEM_IDS.length);
+        mMenuStringContent.put(R.id.destination_home, res.getString(R.string.navigate_home));
+        mMenuStringContent.put(R.id.destination_profile, res.getString(R.string.navigate_profile));
+        mMenuStringContent.put(R.id.destination_people, res.getString(R.string.navigate_people));
+        mMenuStringContent.put(R.id.destination_settings,
+                res.getString(R.string.navigate_settings));
+    }
+
+    @Test
+    @SmallTest
+    public void testBasics() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        // Check the contents of the Menu object
+        final Menu menu = mNavigationView.getMenu();
+        assertNotNull("Menu should not be null", menu);
+        assertEquals("Should have matching number of items", MENU_CONTENT_ITEM_IDS.length,
+                menu.size());
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            final MenuItem currItem = menu.getItem(i);
+            assertEquals("ID for Item #" + i, MENU_CONTENT_ITEM_IDS[i], currItem.getItemId());
+        }
+
+        // Check that we have the expected menu items in our NavigationView
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
+                    isDescendantOfA(withId(R.id.start_drawer)))).check(matches(isDisplayed()));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testTextAppearance() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        final Resources res = mActivityTestRule.getActivity().getResources();
+        final int defaultTextSize = res.getDimensionPixelSize(R.dimen.text_medium_size);
+
+        // Check the default style of the menu items in our NavigationView
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
+                    isDescendantOfA(withId(R.id.start_drawer)))).check(
+                    matches(withTextSize(defaultTextSize)));
+        }
+
+        // Set a new text appearance on our NavigationView
+        onView(withId(R.id.start_drawer)).perform(setItemTextAppearance(R.style.TextSmallStyle));
+
+        // And check that all the menu items have the new style
+        final int newTextSize = res.getDimensionPixelSize(R.dimen.text_small_size);
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
+                    isDescendantOfA(withId(R.id.start_drawer)))).check(
+                    matches(withTextSize(newTextSize)));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testTextColor() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        final Resources res = mActivityTestRule.getActivity().getResources();
+        final @ColorInt int defaultTextColor = ResourcesCompat.getColor(res,
+                R.color.emerald_text, null);
+
+        // Check the default text color of the menu items in our NavigationView
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
+                    isDescendantOfA(withId(R.id.start_drawer)))).check(
+                    matches(withTextColor(defaultTextColor)));
+        }
+
+        // Set a new text color on our NavigationView
+        onView(withId(R.id.start_drawer)).perform(setItemTextColor(
+                ResourcesCompat.getColorStateList(res, R.color.color_state_list_lilac, null)));
+
+        // And check that all the menu items have the new color
+        final @ColorInt int newTextColor = ResourcesCompat.getColor(res,
+                R.color.lilac_default, null);
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            onView(allOf(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
+                    isDescendantOfA(withId(R.id.start_drawer)))).check(
+                    matches(withTextColor(newTextColor)));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBackground() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        final Resources res = mActivityTestRule.getActivity().getResources();
+        final @ColorInt int defaultFillColor = ResourcesCompat.getColor(res,
+                R.color.sand_default, null);
+
+        // Check the default fill color of the menu items in our NavigationView
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            // Note that here we're tying ourselves to the implementation details of the
+            // internal structure of the NavigationView. Specifically, we're looking at the
+            // direct child of RecyclerView which is expected to have the background set
+            // on it. If the internal implementation of NavigationView changes, the second
+            // Matcher below will need to be tweaked.
+            Matcher menuItemMatcher = allOf(
+                    hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
+                    isChildOfA(isAssignableFrom(RecyclerView.class)),
+                    isDescendantOfA(withId(R.id.start_drawer)));
+
+            onView(menuItemMatcher).check(matches(withBackgroundFill(defaultFillColor)));
+        }
+
+        // Set a new background (flat fill color) on our NavigationView
+        onView(withId(R.id.start_drawer)).perform(setItemBackgroundResource(
+                R.drawable.test_background_blue));
+
+        // And check that all the menu items have the new fill
+        final @ColorInt int newFillColorBlue = ResourcesCompat.getColor(res,
+                R.color.test_blue, null);
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            Matcher menuItemMatcher = allOf(
+                    hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
+                    isChildOfA(isAssignableFrom(RecyclerView.class)),
+                    isDescendantOfA(withId(R.id.start_drawer)));
+
+            onView(menuItemMatcher).check(matches(withBackgroundFill(newFillColorBlue)));
+        }
+
+        // Set another new background on our NavigationView
+        onView(withId(R.id.start_drawer)).perform(setItemBackground(
+                ResourcesCompat.getDrawable(res, R.drawable.test_background_green, null)));
+
+        // And check that all the menu items have the new fill
+        final @ColorInt int newFillColorGreen = ResourcesCompat.getColor(res,
+                R.color.test_green, null);
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            Matcher menuItemMatcher = allOf(
+                    hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
+                    isChildOfA(isAssignableFrom(RecyclerView.class)),
+                    isDescendantOfA(withId(R.id.start_drawer)));
+
+            onView(menuItemMatcher).check(matches(withBackgroundFill(newFillColorGreen)));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testIconTinting() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        final Resources res = mActivityTestRule.getActivity().getResources();
+        final @ColorInt int defaultTintColor = ResourcesCompat.getColor(res,
+                R.color.emerald_translucent, null);
+
+        // We're allowing a margin of error in checking the color of the items' icons.
+        // This is due to the translucent color being used in the icon tinting
+        // and off-by-one discrepancies of SRC_IN when it's compositing
+        // translucent color. Note that all the checks below are written for the current
+        // logic on NavigationView that uses the default SRC_IN tint mode - effectively
+        // replacing all non-transparent pixels in the destination (original icon) with
+        // our translucent tint color.
+        final int allowedComponentVariance = 1;
+
+        // Note that here we're tying ourselves to the implementation details of the
+        // internal structure of the NavigationView. Specifically, we're checking the
+        // start drawable of the text view with the specific text. If the internal
+        // implementation of NavigationView changes, the second Matcher in the lookups
+        // below will need to be tweaked.
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(defaultTintColor, allowedComponentVariance)));
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(defaultTintColor, allowedComponentVariance)));
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(defaultTintColor, allowedComponentVariance)));
+
+        final @ColorInt int newTintColor = ResourcesCompat.getColor(res,
+                R.color.red_translucent, null);
+
+        onView(withId(R.id.start_drawer)).perform(setItemIconTintList(
+                ResourcesCompat.getColorStateList(res, R.color.color_state_list_red_translucent,
+                        null)));
+        // Check that all menu items with icons now have icons tinted with the newly set color
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(newTintColor, allowedComponentVariance)));
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(newTintColor, allowedComponentVariance)));
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(newTintColor, allowedComponentVariance)));
+
+        // And now remove all icon tinting
+        onView(withId(R.id.start_drawer)).perform(setItemIconTintList(null));
+        // And verify that all menu items with icons now have the original colors for their icons.
+        // Note that since there is no tinting at this point, we don't allow any color variance
+        // in these checks.
+        final @ColorInt int redIconColor = ResourcesCompat.getColor(res, R.color.test_red, null);
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_home)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(redIconColor, 0)));
+        final @ColorInt int greenIconColor = ResourcesCompat.getColor(res, R.color.test_green,
+                null);
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(greenIconColor, 0)));
+        final @ColorInt int blueIconColor = ResourcesCompat.getColor(res, R.color.test_blue, null);
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+                isDescendantOfA(withId(R.id.start_drawer)))).check(matches(
+                    withStartDrawableFilledWith(blueIconColor, 0)));
+    }
+
+    /**
+     * Gets the list of header IDs (which can be empty) and verifies that the actual header content
+     * of our navigation view matches the expected header content.
+     */
+    private void verifyHeaders(@IdRes int ... expectedHeaderIds) {
+        final int expectedHeaderCount = (expectedHeaderIds != null) ? expectedHeaderIds.length : 0;
+        final int actualHeaderCount = mNavigationView.getHeaderCount();
+        assertEquals("Header count", expectedHeaderCount, actualHeaderCount);
+
+        if (expectedHeaderCount > 0) {
+            for (int i = 0; i < expectedHeaderCount; i++) {
+                final View currentHeader = mNavigationView.getHeaderView(i);
+                assertEquals("Header at #" + i, expectedHeaderIds[i], currentHeader.getId());
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testHeaders() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        // We should have no headers at the start
+        verifyHeaders();
+
+        // Inflate one header and check that it's there in the navigation view
+        onView(withId(R.id.start_drawer)).perform(
+                inflateHeaderView(R.layout.design_navigation_view_header1));
+        verifyHeaders(R.id.header1);
+
+        final LayoutInflater inflater = LayoutInflater.from(mActivityTestRule.getActivity());
+
+        // Add one more header and check that it's there in the navigation view
+        onView(withId(R.id.start_drawer)).perform(
+                addHeaderView(inflater, R.layout.design_navigation_view_header2));
+        verifyHeaders(R.id.header1, R.id.header2);
+
+        final View header1 = mNavigationView.findViewById(R.id.header1);
+        // Remove the first header and check that we still have the second header
+        onView(withId(R.id.start_drawer)).perform(removeHeaderView(header1));
+        verifyHeaders(R.id.header2);
+
+        // Add one more header and check that we now have two headers
+        onView(withId(R.id.start_drawer)).perform(
+                inflateHeaderView(R.layout.design_navigation_view_header3));
+        verifyHeaders(R.id.header2, R.id.header3);
+
+        // Add another "copy" of the header from the just-added layout and check that we now
+        // have three headers
+        onView(withId(R.id.start_drawer)).perform(
+                addHeaderView(inflater, R.layout.design_navigation_view_header3));
+        verifyHeaders(R.id.header2, R.id.header3, R.id.header3);
+    }
+
+    @Test
+    @SmallTest
+    public void testNavigationSelectionListener() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        // Click one of our items
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+                isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
+        // Check that the drawer is still open
+        assertTrue("Drawer is still open after click",
+                mDrawerLayout.isDrawerOpen(GravityCompat.START));
+
+        // Register a listener
+        mNavigationView.setNavigationItemSelectedListener(
+                new NavigationView.OnNavigationItemSelectedListener() {
+                    @Override
+                    public boolean onNavigationItemSelected(MenuItem item) {
+                        mLastSelectedNavigationItemId = item.getItemId();
+                        return false;
+                    }
+                });
+
+        // Click one of our items
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_profile)),
+                isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
+        // Check that the drawer is still open
+        assertTrue("Drawer is still open after click",
+                mDrawerLayout.isDrawerOpen(GravityCompat.START));
+        // And that our listener has been notified of the click
+        assertEquals("Selected item ID", R.id.destination_profile, mLastSelectedNavigationItemId);
+
+        // Reset the tracker field and set null listener
+        mLastSelectedNavigationItemId = -1;
+        mNavigationView.setNavigationItemSelectedListener(null);
+
+        // Click one of our items
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_settings)),
+                isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
+        // Check that the drawer is still open
+        assertTrue("Drawer is still open after click",
+                mDrawerLayout.isDrawerOpen(GravityCompat.START));
+        // And that our previous listener has not been notified of the click
+        assertEquals("Selected item ID", -1, mLastSelectedNavigationItemId);
+    }
+
+    private void verifyCheckedAppearance(@IdRes int checkedItemId,
+            @ColorInt int uncheckedItemForeground, @ColorInt int checkedItemForeground,
+            @ColorInt int uncheckedItemBackground, @ColorInt int checkedItemBackground) {
+        for (int i = 0; i < MENU_CONTENT_ITEM_IDS.length; i++) {
+            final boolean expectedToBeChecked = (MENU_CONTENT_ITEM_IDS[i] == checkedItemId);
+            final @ColorInt int expectedItemForeground =
+                    expectedToBeChecked ? checkedItemForeground : uncheckedItemForeground;
+            final @ColorInt int expectedItemBackground =
+                    expectedToBeChecked ? checkedItemBackground : uncheckedItemBackground;
+
+            // For the background fill check we need to select a view that has its background
+            // set by the current implementation (see disclaimer in testBackground)
+            Matcher menuItemMatcher = allOf(
+                    hasDescendant(withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i]))),
+                    isChildOfA(isAssignableFrom(RecyclerView.class)),
+                    isDescendantOfA(withId(R.id.start_drawer)));
+            onView(menuItemMatcher).check(matches(withBackgroundFill(expectedItemBackground)));
+
+            // And for the foreground color check we need to select a view with the text content
+            Matcher menuItemTextMatcher = allOf(
+                    withText(mMenuStringContent.get(MENU_CONTENT_ITEM_IDS[i])),
+                    isDescendantOfA(withId(R.id.start_drawer)));
+            onView(menuItemTextMatcher).check(matches(withTextColor(expectedItemForeground)));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testCheckedAppearance() {
+        // Open our drawer
+        onView(withId(R.id.drawer_layout)).perform(openDrawer(GravityCompat.START));
+
+        // Reconfigure our navigation view to use foreground (text) and background visuals
+        // with explicitly different colors for the checked state
+        final Resources res = mActivityTestRule.getActivity().getResources();
+        onView(withId(R.id.start_drawer)).perform(setItemTextColor(
+                ResourcesCompat.getColorStateList(res, R.color.color_state_list_sand, null)));
+        onView(withId(R.id.start_drawer)).perform(setItemBackgroundResource(
+                R.drawable.test_drawable_state_list));
+
+        final @ColorInt int uncheckedItemForeground = ResourcesCompat.getColor(res,
+                R.color.sand_default, null);
+        final @ColorInt int checkedItemForeground = ResourcesCompat.getColor(res,
+                R.color.sand_checked, null);
+        final @ColorInt int uncheckedItemBackground = ResourcesCompat.getColor(res,
+                R.color.test_green, null);
+        final @ColorInt int checkedItemBackground = ResourcesCompat.getColor(res,
+                R.color.test_blue, null);
+
+        // Verify that all items are rendered with unchecked visuals
+        verifyCheckedAppearance(0, uncheckedItemForeground, checkedItemForeground,
+                uncheckedItemBackground, checkedItemBackground);
+
+        // Mark one of the items as checked
+        onView(withId(R.id.start_drawer)).perform(setCheckedItem(R.id.destination_profile));
+        // And verify that it's now rendered with checked visuals
+        verifyCheckedAppearance(R.id.destination_profile,
+                uncheckedItemForeground, checkedItemForeground,
+                uncheckedItemBackground, checkedItemBackground);
+
+        // Register a navigation listener that "marks" the selected item
+        mNavigationView.setNavigationItemSelectedListener(
+                new NavigationView.OnNavigationItemSelectedListener() {
+                    @Override
+                    public boolean onNavigationItemSelected(MenuItem item) {
+                        return true;
+                    }
+                });
+
+        // Click one of our items
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_people)),
+                isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
+        // and verify that it's now checked
+        verifyCheckedAppearance(R.id.destination_people,
+                uncheckedItemForeground, checkedItemForeground,
+                uncheckedItemBackground, checkedItemBackground);
+
+        // Register a navigation listener that doesn't "mark" the selected item
+        mNavigationView.setNavigationItemSelectedListener(
+                new NavigationView.OnNavigationItemSelectedListener() {
+                    @Override
+                    public boolean onNavigationItemSelected(MenuItem item) {
+                        return false;
+                    }
+                });
+
+        // Click another items
+        onView(allOf(withText(mMenuStringContent.get(R.id.destination_settings)),
+                isDescendantOfA(withId(R.id.start_drawer)))).perform(click());
+        // and verify that the checked state remains on the previously clicked item
+        // since the current navigation listener returns false from its callback
+        // implementation
+        verifyCheckedAppearance(R.id.destination_people,
+                uncheckedItemForeground, checkedItemForeground,
+                uncheckedItemBackground, checkedItemBackground);
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/SnackbarBucketTests.java b/design/tests/src/android/support/design/widget/SnackbarBucketTests.java
index e4b1b68..db8d33a 100644
--- a/design/tests/src/android/support/design/widget/SnackbarBucketTests.java
+++ b/design/tests/src/android/support/design/widget/SnackbarBucketTests.java
@@ -16,9 +16,6 @@
 
 package android.support.design.widget;
 
-import org.junit.Test;
-
-import android.content.Intent;
 import android.os.SystemClock;
 import android.support.design.test.R;
 import android.support.test.espresso.ViewAction;
@@ -26,6 +23,7 @@
 import android.support.test.espresso.action.ViewActions;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
+import org.junit.Test;
 
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -33,6 +31,8 @@
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class SnackbarBucketTests extends BaseInstrumentationTestCase<SnackbarBucketTestsActivity> {
 
@@ -121,6 +121,6 @@
     }
 
     private CoordinatorLayout getCoordinatorLayout() {
-        return getActivity().mCoordinatorLayout;
+        return mActivityTestRule.getActivity().mCoordinatorLayout;
     }
 }
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithLayoutItems.java b/design/tests/src/android/support/design/widget/TabLayoutWithLayoutItems.java
index 61e5768..603c2d4 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutWithLayoutItems.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithLayoutItems.java
@@ -16,12 +16,14 @@
 
 package android.support.design.widget;
 
-import org.junit.Test;
-
 import android.support.design.test.R;
+import android.support.test.InstrumentationRegistry;
 import android.support.v7.app.AppCompatActivity;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.LayoutInflater;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
 
 public class TabLayoutWithLayoutItems extends BaseInstrumentationTestCase<AppCompatActivity> {
 
@@ -31,11 +33,12 @@
 
     @Test
     @SmallTest
-    public void testInflateTabLayoutWithTabItems() throws Throwable {
-        runTestOnUiThread(new Runnable() {
+    public void testInflateTabLayoutWithTabItems() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                final LayoutInflater inflater = LayoutInflater.from(getActivity());
+                final LayoutInflater inflater =
+                        LayoutInflater.from(mActivityTestRule.getActivity());
                 final TabLayout tabLayout = (TabLayout) inflater.inflate(
                         R.layout.design_tabs_items, null);
 
@@ -43,7 +46,8 @@
 
                 // Tab 0 has text, but no icon or custom view
                 TabLayout.Tab tab = tabLayout.getTabAt(0);
-                assertEquals(getActivity().getString(R.string.tab_layout_text), tab.getText());
+                assertEquals(mActivityTestRule.getActivity().getString(R.string.tab_layout_text),
+                        tab.getText());
                 assertNull(tab.getIcon());
                 assertNull(tab.getCustomView());
 
@@ -66,12 +70,20 @@
     @Test(expected = IllegalArgumentException.class)
     @SmallTest
     public void testInflateTabLayoutWithNonTabItem() throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
+        final Throwable[] exceptions = new Throwable[1];
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
             public void run() {
-                final LayoutInflater inflater = LayoutInflater.from(getActivity());
-                inflater.inflate(R.layout.design_tabs_with_non_tabitems, null);
+                try {
+                    final LayoutInflater inflater =
+                            LayoutInflater.from(mActivityTestRule.getActivity());
+                    inflater.inflate(R.layout.design_tabs_with_non_tabitems, null);
+                } catch (Throwable throwable) {
+                    exceptions[0] = throwable;
+                }
             }
         });
+        if (exceptions[0] != null) {
+            throw exceptions[0];
+        }
     }
 }
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
index 6fe99a5..ff92b36 100644
--- a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
@@ -1,3 +1,18 @@
+/*
+ * 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.support.design.test.R;
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
index 0a86bce..15d3780 100755
--- a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
@@ -34,6 +34,7 @@
 import android.widget.HorizontalScrollView;
 import android.widget.TextView;
 import org.hamcrest.Matcher;
+import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
@@ -43,6 +44,7 @@
 import static android.support.test.espresso.matcher.ViewMatchers.*;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
 
 public class TabLayoutWithViewPagerTest
         extends BaseInstrumentationTestCase<TabLayoutWithViewPagerActivity> {
@@ -165,10 +167,9 @@
         super(TabLayoutWithViewPagerActivity.class);
     }
 
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
-        final TabLayoutWithViewPagerActivity activity = getActivity();
+        final TabLayoutWithViewPagerActivity activity = mActivityTestRule.getActivity();
         mTabLayout = (TabLayout) activity.findViewById(R.id.tabs);
         mViewPager = (ViewPager) activity.findViewById(R.id.tabs_viewpager);
 
@@ -395,7 +396,7 @@
             @DimenRes int tabMaxWidthResId) {
         assertEquals("Scrollable tab mode", TabLayout.MODE_SCROLLABLE, mTabLayout.getTabMode());
 
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivityTestRule.getActivity().getResources();
         final int minTabWidth = (tabMinWidthResId == 0) ? -1 :
                 res.getDimensionPixelSize(tabMinWidthResId);
         final int maxTabWidth = (tabMaxWidthResId == 0) ? -1 :
diff --git a/v17/leanback/res/layout/lb_guidedstep_fragment.xml b/v17/leanback/res/layout/lb_guidedstep_fragment.xml
index d4983e5..2ffae70 100644
--- a/v17/leanback/res/layout/lb_guidedstep_fragment.xml
+++ b/v17/leanback/res/layout/lb_guidedstep_fragment.xml
@@ -18,55 +18,66 @@
 <android.support.v17.leanback.app.GuidedStepRootLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/guidedstep_root"
-    android:orientation="horizontal"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-    <LinearLayout
-        android:id="@+id/content_frame"
-        android:orientation="horizontal"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:gravity="bottom"
+    android:weightSum="2">
+
+    <FrameLayout
+        android:id="@+id/guidedstep_background_view_root"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" >
+        android:layout_height="0dp"
+        android:layout_weight="?attr/guidedStepHeightWeight">
 
-        <android.support.v17.leanback.widget.NonOverlappingFrameLayout
-            android:id="@+id/content_fragment"
-            android:layout_toStartOf="@+id/action_fragment"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="match_parent"
-            android:layout_alignParentStart="true" />
-
-        <android.support.v17.leanback.widget.NonOverlappingFrameLayout
-            android:id="@+id/action_fragment_root"
-            android:transitionName="action_fragment_root"
-            android:transitionGroup="false"
+        <LinearLayout
+            android:id="@+id/content_frame"
             android:orientation="horizontal"
-            android:clipToPadding="false"
-            android:clipChildren="false"
-            android:paddingStart="@dimen/lb_guidedactions_section_shadow_width"
-            android:layout_width="0dp"
-            android:layout_weight="?attr/guidedActionContentWidthWeight"
-            android:layout_height="match_parent"
-            android:layout_alignParentEnd="true">
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
 
-            <android.support.v17.leanback.widget.NonOverlappingView
-                android:id="@+id/action_fragment_background"
-                android:transitionName="action_fragment_background"
-                android:orientation="horizontal"
-                android:outlineProvider="paddedBounds"
-                android:layout_width="match_parent"
+            <android.support.v17.leanback.widget.NonOverlappingFrameLayout
+                android:id="@+id/content_fragment"
+                android:layout_width="0dp"
+                android:layout_weight="1"
                 android:layout_height="match_parent"
-                android:background="?attr/guidedActionsBackground"
-                android:elevation="?attr/guidedActionsElevation" />
+                android:layout_alignParentStart="true" />
 
-            <android.support.v17.leanback.widget.NonOverlappingLinearLayout
-                android:id="@+id/action_fragment"
-                android:transitionName="action_fragment"
+            <android.support.v17.leanback.widget.NonOverlappingFrameLayout
+                android:id="@+id/action_fragment_root"
+                android:transitionName="action_fragment_root"
                 android:transitionGroup="false"
                 android:orientation="horizontal"
-                android:layout_width="match_parent"
+                android:clipToPadding="false"
+                android:clipChildren="false"
+                android:paddingStart="@dimen/lb_guidedactions_section_shadow_width"
+                android:layout_width="0dp"
+                android:layout_weight="?attr/guidedActionContentWidthWeight"
                 android:layout_height="match_parent"
-                android:elevation="?attr/guidedActionsElevation" />
-        </android.support.v17.leanback.widget.NonOverlappingFrameLayout>
+                android:layout_alignParentEnd="true">
 
-    </LinearLayout>
-</android.support.v17.leanback.app.GuidedStepRootLayout>
\ No newline at end of file
+                <android.support.v17.leanback.widget.NonOverlappingView
+                    android:id="@+id/action_fragment_background"
+                    android:transitionName="action_fragment_background"
+                    android:orientation="horizontal"
+                    android:outlineProvider="paddedBounds"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:background="?attr/guidedActionsBackground"
+                    android:elevation="?attr/guidedActionsElevation" />
+
+                <android.support.v17.leanback.widget.NonOverlappingLinearLayout
+                    android:id="@+id/action_fragment"
+                    android:transitionName="action_fragment"
+                    android:transitionGroup="false"
+                    android:orientation="horizontal"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:elevation="?attr/guidedActionsElevation" />
+            </android.support.v17.leanback.widget.NonOverlappingFrameLayout>
+
+        </LinearLayout>
+
+    </FrameLayout>
+
+</android.support.v17.leanback.app.GuidedStepRootLayout>
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index ebffa93..cde7a2d 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -318,6 +318,11 @@
              can set this to <code>@style/Theme.Leanback.GuidedStep</code> in order to specify the
              default GuidedStepFragment styles. -->
         <attr name="guidedStepTheme" format="reference" />
+        <!-- Used to control the height of the fragment. By default this fragment will take
+             up the full height of it's parent. The height of this fragment is governed by
+             this property. Default weight is set to 2.0, so inorder to render the fragment
+             in half screen mode, this attribute should be set to 1.0 -->
+        <attr name="guidedStepHeightWeight" format="float" />
 
         <!-- @hide
              Theme attribute used to inspect theme inheritance. -->
@@ -475,7 +480,6 @@
         <attr name="guidedActionContentWidth" format="reference" />
         <!-- Deprecated theme attribute, do not use -->
         <attr name="guidedActionContentWidthNoIcon" format="reference" />
-
     </declare-styleable>
 
     <declare-styleable name="lbDatePicker">
@@ -485,4 +489,4 @@
         <attr name="datePickerFormat" format="string"/>
     </declare-styleable>
 
-</resources>
\ No newline at end of file
+</resources>
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index 87b3549..5823d2b 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -237,6 +237,8 @@
     <dimen name="lb_guidedactions_elevation">12dp</dimen>
     <dimen name="lb_guidedactions_vertical_padding">12dp</dimen>
 
+    <item name="lb_guidedstep_height_weight" format="float" type="string">2.0</item>
+    <item name="lb_guidedstep_height_weight_translucent" format="float" type="string">1.0</item>
     <item name="lb_guidedactions_item_disabled_text_alpha" format="float" type="string">0.25</item>
     <item name="lb_guidedactions_item_disabled_description_text_alpha" format="float" type="string">0.25</item>
     <item name="lb_guidedactions_item_unselected_text_alpha" format="float" type="string">1.00</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index 42b49bc..1c52769 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -121,6 +121,7 @@
 
     <style name="Theme.Leanback.GuidedStep" parent="Theme.LeanbackBase">
         <item name="guidedStepThemeFlag">true</item>
+        <item name="guidedStepHeightWeight">@string/lb_guidedstep_height_weight</item>
 
         <item name="android:windowEnterTransition">@transition/lb_guidedstep_activity_enter</item>
 
@@ -169,4 +170,9 @@
         <item name="guidedActionVerticalPadding">@dimen/lb_guidedactions_vertical_padding</item>
     </style>
 
+    <style name="Theme.Leanback.GuidedStep.Half" parent="Theme.Leanback.GuidedStep">
+      <item name="guidedStepHeightWeight">@string/lb_guidedstep_height_weight_translucent</item>
+      <item name="android:windowIsTranslucent">true</item>
+      <item name="android:windowBackground">@android:color/transparent</item>
+    </style>
 </resources>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index 8042242..515946c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -25,8 +25,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
-import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
@@ -46,8 +46,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.RelativeLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -582,7 +582,6 @@
     public static int addAsRoot(Activity activity, GuidedStepFragment fragment, int id) {
         // Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition.
         activity.getWindow().getDecorView();
-
         FragmentManager fragmentManager = activity.getFragmentManager();
         FragmentTransaction ft = fragmentManager.beginTransaction();
         fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT);
@@ -965,8 +964,10 @@
 
         GuidedStepRootLayout root = (GuidedStepRootLayout) inflater.inflate(
                 R.layout.lb_guidedstep_fragment, container, false);
+
         root.setFocusOutStart(isFocusOutStartAllowed());
         root.setFocusOutEnd(isFocusOutEndAllowed());
+
         ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment);
         ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment);
 
@@ -1067,9 +1068,12 @@
 
         setSelectedButtonActionPosition(0);
 
+        // Add the background view.
         View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
         if (backgroundView != null) {
-            root.addView(backgroundView, 0);
+            FrameLayout backgroundViewRoot = (FrameLayout)root.findViewById(
+                R.id.guidedstep_background_view_root);
+            backgroundViewRoot.addView(backgroundView, 0);
         }
         return root;
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java
index 9fc3720..e644636 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepRootLayout.java
@@ -18,13 +18,13 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
-import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 
 /**
  * Utility class used by GuidedStepFragment to disable focus out left/right.
  * @hide
  */
-class GuidedStepRootLayout extends FrameLayout {
+class GuidedStepRootLayout extends LinearLayout {
 
     private boolean mFocusOutStart = false;
     private boolean mFocusOutEnd = false;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index b76c990..bedd570 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -27,8 +27,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
-import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
 import android.support.v17.leanback.widget.GuidedAction;
@@ -48,8 +48,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.RelativeLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -584,7 +584,6 @@
     public static int addAsRoot(FragmentActivity activity, GuidedStepSupportFragment fragment, int id) {
         // Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition.
         activity.getWindow().getDecorView();
-
         FragmentManager fragmentManager = activity.getSupportFragmentManager();
         FragmentTransaction ft = fragmentManager.beginTransaction();
         fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT);
@@ -967,8 +966,10 @@
 
         GuidedStepRootLayout root = (GuidedStepRootLayout) inflater.inflate(
                 R.layout.lb_guidedstep_fragment, container, false);
+
         root.setFocusOutStart(isFocusOutStartAllowed());
         root.setFocusOutEnd(isFocusOutEndAllowed());
+
         ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment);
         ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment);
 
@@ -1069,9 +1070,12 @@
 
         setSelectedButtonActionPosition(0);
 
+        // Add the background view.
         View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
         if (backgroundView != null) {
-            root.addView(backgroundView, 0);
+            FrameLayout backgroundViewRoot = (FrameLayout)root.findViewById(
+                R.id.guidedstep_background_view_root);
+            backgroundViewRoot.addView(backgroundView, 0);
         }
         return root;
     }
diff --git a/v4/api23/android/support/v4/widget/TextViewCompatApi23.java b/v4/api23/android/support/v4/widget/TextViewCompatApi23.java
index 8370bfc..ad21409 100644
--- a/v4/api23/android/support/v4/widget/TextViewCompatApi23.java
+++ b/v4/api23/android/support/v4/widget/TextViewCompatApi23.java
@@ -18,10 +18,11 @@
 
 import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
+import android.support.annotation.StyleRes;
 import android.widget.TextView;
 
 class TextViewCompatApi23 {
-    public static void setTextAppearance(@NonNull TextView textView, @IdRes int resId) {
+    public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
         textView.setTextAppearance(resId);
     }
 }
diff --git a/v4/build.gradle b/v4/build.gradle
index 993eb5a..1bb53ca 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -65,8 +65,12 @@
     // when manipulating the libraryVariants.
     compile files(internalJar.archivePath)
 
-    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
-    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
+    androidTestCompile ('com.android.support.test:runner:0.4.1') {
+        exclude module: 'support-annotations'
+    }
+    androidTestCompile ('com.android.support.test.espresso:espresso-core:2.2.1') {
+        exclude module: 'support-annotations'
+    }
     testCompile 'junit:junit:4.12'
 }
 
diff --git a/v4/java/android/support/v4/app/FragmentActivity.java b/v4/java/android/support/v4/app/FragmentActivity.java
index 4d6585a..0fbe357 100644
--- a/v4/java/android/support/v4/app/FragmentActivity.java
+++ b/v4/java/android/support/v4/app/FragmentActivity.java
@@ -784,7 +784,9 @@
      */
     @Override
     public void startActivityForResult(Intent intent, int requestCode) {
-        if (mStartedActivityFromFragment) {
+        // If this was started from a Fragment we've already checked the upper 16 bits were not in
+        // use, and then repurposed them for the Fragment's index.
+        if (!mStartedActivityFromFragment) {
             if (requestCode != -1 && (requestCode&0xffff0000) != 0) {
                 throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
             }
diff --git a/v4/java/android/support/v4/widget/TextViewCompat.java b/v4/java/android/support/v4/widget/TextViewCompat.java
index 5df1ae6..7af3087 100644
--- a/v4/java/android/support/v4/widget/TextViewCompat.java
+++ b/v4/java/android/support/v4/widget/TextViewCompat.java
@@ -18,9 +18,11 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.IdRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.StyleRes;
 import android.widget.TextView;
 
 /**
@@ -40,10 +42,11 @@
                 @Nullable Drawable start, @Nullable Drawable top, @Nullable Drawable end,
                 @Nullable Drawable bottom);
         void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                int start, int top, int end, int bottom);
+                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
+                @DrawableRes int bottom);
         int getMaxLines(TextView textView);
         int getMinLines(TextView textView);
-        void setTextAppearance(@NonNull TextView textView, @IdRes int resId);
+        void setTextAppearance(@NonNull TextView textView, @StyleRes int resId);
     }
 
     static class BaseTextViewCompatImpl implements TextViewCompatImpl {
@@ -63,7 +66,8 @@
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                int start, int top, int end, int bottom) {
+                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
+                @DrawableRes int bottom) {
             textView.setCompoundDrawablesWithIntrinsicBounds(start, top, end, bottom);
         }
 
@@ -78,7 +82,7 @@
         }
 
         @Override
-        public void setTextAppearance(TextView textView, int resId) {
+        public void setTextAppearance(TextView textView, @StyleRes int resId) {
             TextViewCompatDonut.setTextAppearance(textView, resId);
         }
     }
@@ -113,7 +117,8 @@
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                int start, int top, int end, int bottom) {
+                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
+                @DrawableRes int bottom) {
             TextViewCompatJbMr1.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
                     start, top, end, bottom);
         }
@@ -138,7 +143,8 @@
 
         @Override
         public void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-                int start, int top, int end, int bottom) {
+                @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
+                @DrawableRes int bottom) {
             TextViewCompatJbMr2.setCompoundDrawablesRelativeWithIntrinsicBounds(textView,
                     start, top, end, bottom);
         }
@@ -146,7 +152,7 @@
 
     static class Api23TextViewCompatImpl extends JbMr2TextViewCompatImpl {
         @Override
-        public void setTextAppearance(@NonNull TextView textView, @IdRes int resId) {
+        public void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
             TextViewCompatApi23.setTextAppearance(textView, resId);
         }
     }
@@ -228,7 +234,8 @@
      * @attr ref android.R.styleable#TextView_drawableBottom
      */
     public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            int start, int top, int end, int bottom) {
+            @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
+            @DrawableRes int bottom) {
         IMPL.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, start, top, end, bottom);
     }
 
@@ -259,7 +266,7 @@
      * @param textView The TextView against which to invoke the method.
      * @param resId    The resource identifier of the style to apply.
      */
-    public static void setTextAppearance(@NonNull TextView textView, @IdRes int resId) {
+    public static void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
         IMPL.setTextAppearance(textView, resId);
     }
 }
diff --git a/v4/jellybean-mr1/android/support/v4/media/routing/MediaRouterJellybeanMr1.java b/v4/jellybean-mr1/android/support/v4/media/routing/MediaRouterJellybeanMr1.java
deleted file mode 100644
index 6e5cfd5..0000000
--- a/v4/jellybean-mr1/android/support/v4/media/routing/MediaRouterJellybeanMr1.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2013 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.v4.media.routing;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Build;
-import android.os.Handler;
-import android.util.Log;
-import android.view.Display;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-class MediaRouterJellybeanMr1 extends MediaRouterJellybean {
-    private static final String TAG = "MediaRouterJellybeanMr1";
-
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
-    }
-
-    public static final class RouteInfo {
-        public static boolean isEnabled(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled();
-        }
-
-        public static Display getPresentationDisplay(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay();
-        }
-    }
-
-    public static interface Callback extends MediaRouterJellybean.Callback {
-        public void onRoutePresentationDisplayChanged(Object routeObj);
-    }
-
-    /**
-     * Workaround the fact that the version of MediaRouter.addCallback() that accepts a
-     * flag to perform an active scan does not exist in JB MR1 so we need to force
-     * wifi display scans directly through the DisplayManager.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class ActiveScanWorkaround implements Runnable {
-        // Time between wifi display scans when actively scanning in milliseconds.
-        private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
-
-        private final DisplayManager mDisplayManager;
-        private final Handler mHandler;
-        private Method mScanWifiDisplaysMethod;
-
-        private boolean mActivelyScanningWifiDisplays;
-
-        public ActiveScanWorkaround(Context context, Handler handler) {
-            if (Build.VERSION.SDK_INT != 17) {
-                throw new UnsupportedOperationException();
-            }
-
-            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
-            mHandler = handler;
-            try {
-                mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays");
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public void setActiveScanRouteTypes(int routeTypes) {
-            // On JB MR1, there is no API to scan wifi display routes.
-            // Instead we must make a direct call into the DisplayManager to scan
-            // wifi displays on this version but only when live video routes are requested.
-            // See also the JellybeanMr2Impl implementation of this method.
-            // This was fixed in JB MR2 by adding a new overload of addCallback() to
-            // enable active scanning on request.
-            if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                if (!mActivelyScanningWifiDisplays) {
-                    if (mScanWifiDisplaysMethod != null) {
-                        mActivelyScanningWifiDisplays = true;
-                        mHandler.post(this);
-                    } else {
-                        Log.w(TAG, "Cannot scan for wifi displays because the "
-                                + "DisplayManager.scanWifiDisplays() method is "
-                                + "not available on this device.");
-                    }
-                }
-            } else {
-                if (mActivelyScanningWifiDisplays) {
-                    mActivelyScanningWifiDisplays = false;
-                    mHandler.removeCallbacks(this);
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            if (mActivelyScanningWifiDisplays) {
-                try {
-                    mScanWifiDisplaysMethod.invoke(mDisplayManager);
-                } catch (IllegalAccessException ex) {
-                    Log.w(TAG, "Cannot scan for wifi displays.", ex);
-                } catch (InvocationTargetException ex) {
-                    Log.w(TAG, "Cannot scan for wifi displays.", ex);
-                }
-                mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
-            }
-        }
-    }
-
-    /**
-     * Workaround the fact that the isConnecting() method does not exist in JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class IsConnectingWorkaround {
-        private Method mGetStatusCodeMethod;
-        private int mStatusConnecting;
-
-        public IsConnectingWorkaround() {
-            if (Build.VERSION.SDK_INT != 17) {
-                throw new UnsupportedOperationException();
-            }
-
-            try {
-                Field statusConnectingField =
-                        android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING");
-                mStatusConnecting = statusConnectingField.getInt(null);
-                mGetStatusCodeMethod =
-                        android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode");
-            } catch (NoSuchFieldException ex) {
-            } catch (NoSuchMethodException ex) {
-            } catch (IllegalAccessException ex) {
-            }
-        }
-
-        public boolean isConnecting(Object routeObj) {
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo)routeObj;
-
-            if (mGetStatusCodeMethod != null) {
-                try {
-                    int statusCode = (Integer)mGetStatusCodeMethod.invoke(route);
-                    return statusCode == mStatusConnecting;
-                } catch (IllegalAccessException ex) {
-                } catch (InvocationTargetException ex) {
-                }
-            }
-
-            // Assume not connecting.
-            return false;
-        }
-    }
-
-    static class CallbackProxy<T extends Callback>
-            extends MediaRouterJellybean.CallbackProxy<T> {
-        public CallbackProxy(T callback) {
-            super(callback);
-        }
-
-        @Override
-        public void onRoutePresentationDisplayChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRoutePresentationDisplayChanged(route);
-        }
-    }
-}
diff --git a/v4/jellybean-mr2/android/support/v4/media/routing/MediaRouterJellybeanMr2.java b/v4/jellybean-mr2/android/support/v4/media/routing/MediaRouterJellybeanMr2.java
deleted file mode 100644
index 92a1607..0000000
--- a/v4/jellybean-mr2/android/support/v4/media/routing/MediaRouterJellybeanMr2.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2013 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.v4.media.routing;
-
-class MediaRouterJellybeanMr2 extends MediaRouterJellybeanMr1 {
-    public static Object getDefaultRoute(Object routerObj) {
-        return ((android.media.MediaRouter)routerObj).getDefaultRoute();
-    }
-
-    public static void addCallback(Object routerObj, int types, Object callbackObj, int flags) {
-        ((android.media.MediaRouter)routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback)callbackObj, flags);
-    }
-
-    public static final class RouteInfo {
-        public static CharSequence getDescription(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getDescription();
-        }
-
-        public static boolean isConnecting(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).isConnecting();
-        }
-    }
-
-    public static final class UserRouteInfo {
-        public static void setDescription(Object routeObj, CharSequence description) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setDescription(description);
-        }
-    }
-}
diff --git a/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java b/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
index ec9fe61..73f9666 100644
--- a/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
+++ b/v4/jellybean-mr2/android/support/v4/widget/TextViewCompatJbMr2.java
@@ -17,6 +17,7 @@
 package android.support.v4.widget;
 
 import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.widget.TextView;
@@ -36,7 +37,8 @@
     }
 
     public static void setCompoundDrawablesRelativeWithIntrinsicBounds(@NonNull TextView textView,
-            int start, int top, int end, int bottom) {
+            @DrawableRes int start, @DrawableRes int top, @DrawableRes int end,
+            @DrawableRes int bottom) {
         textView.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
     }
 
diff --git a/v4/jellybean/android/support/v4/media/routing/MediaRouterJellybean.java b/v4/jellybean/android/support/v4/media/routing/MediaRouterJellybean.java
deleted file mode 100644
index 3cf6727..0000000
--- a/v4/jellybean/android/support/v4/media/routing/MediaRouterJellybean.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2013 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.v4.media.routing;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-class MediaRouterJellybean {
-    private static final String TAG = "MediaRouterJellybean";
-
-    public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
-    public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
-    public static final int ROUTE_TYPE_USER = 0x00800000;
-
-    public static final int ALL_ROUTE_TYPES =
-            MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO
-            | MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO
-            | MediaRouterJellybean.ROUTE_TYPE_USER;
-
-    public static Object getMediaRouter(Context context) {
-        return context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static List getRoutes(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-        final int count = router.getRouteCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getRouteAt(i));
-        }
-        return out;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public static List getCategories(Object routerObj) {
-        final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-        final int count = router.getCategoryCount();
-        List out = new ArrayList(count);
-        for (int i = 0; i < count; i++) {
-            out.add(router.getCategoryAt(i));
-        }
-        return out;
-    }
-
-    public static Object getSelectedRoute(Object routerObj, int type) {
-        return ((android.media.MediaRouter)routerObj).getSelectedRoute(type);
-    }
-
-    public static void selectRoute(Object routerObj, int types, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).selectRoute(types,
-                (android.media.MediaRouter.RouteInfo)routeObj);
-    }
-
-    public static void addCallback(Object routerObj, int types, Object callbackObj) {
-        ((android.media.MediaRouter)routerObj).addCallback(types,
-                (android.media.MediaRouter.Callback)callbackObj);
-    }
-
-    public static void removeCallback(Object routerObj, Object callbackObj) {
-        ((android.media.MediaRouter)routerObj).removeCallback(
-                (android.media.MediaRouter.Callback)callbackObj);
-    }
-
-    public static Object createRouteCategory(Object routerObj,
-            String name, boolean isGroupable) {
-        return ((android.media.MediaRouter)routerObj).createRouteCategory(name, isGroupable);
-    }
-
-    public static Object createUserRoute(Object routerObj, Object categoryObj) {
-        return ((android.media.MediaRouter)routerObj).createUserRoute(
-                (android.media.MediaRouter.RouteCategory)categoryObj);
-    }
-
-    public static void addUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).addUserRoute(
-                (android.media.MediaRouter.UserRouteInfo)routeObj);
-    }
-
-    public static void removeUserRoute(Object routerObj, Object routeObj) {
-        ((android.media.MediaRouter)routerObj).removeUserRoute(
-                (android.media.MediaRouter.UserRouteInfo)routeObj);
-    }
-
-    public static Object createCallback(Callback callback) {
-        return new CallbackProxy<Callback>(callback);
-    }
-
-    public static Object createVolumeCallback(VolumeCallback callback) {
-        return new VolumeCallbackProxy<VolumeCallback>(callback);
-    }
-
-    public static final class RouteInfo {
-        public static CharSequence getName(Object routeObj, Context context) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context);
-        }
-
-        public static CharSequence getStatus(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getStatus();
-        }
-
-        public static int getSupportedTypes(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getSupportedTypes();
-        }
-
-        public static Object getCategory(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getCategory();
-        }
-
-        public static Drawable getIconDrawable(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getIconDrawable();
-        }
-
-        public static int getPlaybackType(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackType();
-        }
-
-        public static int getPlaybackStream(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackStream();
-        }
-
-        public static int getVolume(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolume();
-        }
-
-        public static int getVolumeMax(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeMax();
-        }
-
-        public static int getVolumeHandling(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeHandling();
-        }
-
-        public static Object getTag(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getTag();
-        }
-
-        public static void setTag(Object routeObj, Object tag) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).setTag(tag);
-        }
-
-        public static void requestSetVolume(Object routeObj, int volume) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).requestSetVolume(volume);
-        }
-
-        public static void requestUpdateVolume(Object routeObj, int direction) {
-            ((android.media.MediaRouter.RouteInfo)routeObj).requestUpdateVolume(direction);
-        }
-
-        public static Object getGroup(Object routeObj) {
-            return ((android.media.MediaRouter.RouteInfo)routeObj).getGroup();
-        }
-
-        public static boolean isGroup(Object routeObj) {
-            return routeObj instanceof android.media.MediaRouter.RouteGroup;
-        }
-    }
-
-    public static final class RouteGroup {
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public static List getGroupedRoutes(Object groupObj) {
-            final android.media.MediaRouter.RouteGroup group =
-                    (android.media.MediaRouter.RouteGroup)groupObj;
-            final int count = group.getRouteCount();
-            List out = new ArrayList(count);
-            for (int i = 0; i < count; i++) {
-                out.add(group.getRouteAt(i));
-            }
-            return out;
-        }
-    }
-
-    public static final class UserRouteInfo {
-        public static void setName(Object routeObj, CharSequence name) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setName(name);
-        }
-
-        public static void setStatus(Object routeObj, CharSequence status) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setStatus(status);
-        }
-
-        public static void setIconDrawable(Object routeObj, Drawable icon) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setIconDrawable(icon);
-        }
-
-        public static void setPlaybackType(Object routeObj, int type) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackType(type);
-        }
-
-        public static void setPlaybackStream(Object routeObj, int stream) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackStream(stream);
-        }
-
-        public static void setVolume(Object routeObj, int volume) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolume(volume);
-        }
-
-        public static void setVolumeMax(Object routeObj, int volumeMax) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeMax(volumeMax);
-        }
-
-        public static void setVolumeHandling(Object routeObj, int volumeHandling) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeHandling(volumeHandling);
-        }
-
-        public static void setVolumeCallback(Object routeObj, Object volumeCallbackObj) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeCallback(
-                    (android.media.MediaRouter.VolumeCallback)volumeCallbackObj);
-        }
-
-        public static void setRemoteControlClient(Object routeObj, Object rccObj) {
-            ((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient(
-                    (android.media.RemoteControlClient)rccObj);
-        }
-    }
-
-    public static final class RouteCategory {
-        public static CharSequence getName(Object categoryObj, Context context) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context);
-        }
-
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        public static List getRoutes(Object categoryObj) {
-            ArrayList out = new ArrayList();
-            ((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out);
-            return out;
-        }
-
-        public static int getSupportedTypes(Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes();
-        }
-
-        public static boolean isGroupable(Object categoryObj) {
-            return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable();
-        }
-    }
-
-    public static interface Callback {
-        public void onRouteSelected(int type, Object routeObj);
-        public void onRouteUnselected(int type, Object routeObj);
-        public void onRouteAdded(Object routeObj);
-        public void onRouteRemoved(Object routeObj);
-        public void onRouteChanged(Object routeObj);
-        public void onRouteGrouped(Object routeObj, Object groupObj, int index);
-        public void onRouteUngrouped(Object routeObj, Object groupObj);
-        public void onRouteVolumeChanged(Object routeObj);
-    }
-
-    public static interface VolumeCallback {
-        public void onVolumeSetRequest(Object routeObj, int volume);
-        public void onVolumeUpdateRequest(Object routeObj, int direction);
-    }
-
-    /**
-     * Workaround for limitations of selectRoute() on JB and JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class SelectRouteWorkaround {
-        private Method mSelectRouteIntMethod;
-
-        public SelectRouteWorkaround() {
-            if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
-                throw new UnsupportedOperationException();
-            }
-            try {
-                mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod(
-                        "selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class);
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public void selectRoute(Object routerObj, int types, Object routeObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-            android.media.MediaRouter.RouteInfo route =
-                    (android.media.MediaRouter.RouteInfo)routeObj;
-
-            int routeTypes = route.getSupportedTypes();
-            if ((routeTypes & ROUTE_TYPE_USER) == 0) {
-                // Handle non-user routes.
-                // On JB and JB MR1, the selectRoute() API only supports programmatically
-                // selecting user routes.  So instead we rely on the hidden selectRouteInt()
-                // method on these versions of the platform.
-                // This limitation was removed in JB MR2.
-                if (mSelectRouteIntMethod != null) {
-                    try {
-                        mSelectRouteIntMethod.invoke(router, types, route);
-                        return; // success!
-                    } catch (IllegalAccessException ex) {
-                        Log.w(TAG, "Cannot programmatically select non-user route.  "
-                                + "Media routing may not work.", ex);
-                    } catch (InvocationTargetException ex) {
-                        Log.w(TAG, "Cannot programmatically select non-user route.  "
-                                + "Media routing may not work.", ex);
-                    }
-                } else {
-                    Log.w(TAG, "Cannot programmatically select non-user route "
-                            + "because the platform is missing the selectRouteInt() "
-                            + "method.  Media routing may not work.");
-                }
-            }
-
-            // Default handling.
-            router.selectRoute(types, route);
-        }
-    }
-
-    /**
-     * Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1.
-     * Do not use on JB MR2 and above.
-     */
-    public static final class GetDefaultRouteWorkaround {
-        private Method mGetSystemAudioRouteMethod;
-
-        public GetDefaultRouteWorkaround() {
-            if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
-                throw new UnsupportedOperationException();
-            }
-            try {
-                mGetSystemAudioRouteMethod =
-                        android.media.MediaRouter.class.getMethod("getSystemAudioRoute");
-            } catch (NoSuchMethodException ex) {
-            }
-        }
-
-        public Object getDefaultRoute(Object routerObj) {
-            android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
-
-            if (mGetSystemAudioRouteMethod != null) {
-                try {
-                    return mGetSystemAudioRouteMethod.invoke(router);
-                } catch (IllegalAccessException ex) {
-                } catch (InvocationTargetException ex) {
-                }
-            }
-
-            // Could not find the method or it does not work.
-            // Return the first route and hope for the best.
-            return router.getRouteAt(0);
-        }
-    }
-
-    static class CallbackProxy<T extends Callback>
-            extends android.media.MediaRouter.Callback {
-        protected final T mCallback;
-
-        public CallbackProxy(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onRouteSelected(android.media.MediaRouter router,
-                int type, android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteSelected(type, route);
-        }
-
-        @Override
-        public void onRouteUnselected(android.media.MediaRouter router,
-                int type, android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteUnselected(type, route);
-        }
-
-        @Override
-        public void onRouteAdded(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteAdded(route);
-        }
-
-        @Override
-        public void onRouteRemoved(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteRemoved(route);
-        }
-
-        @Override
-        public void onRouteChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteChanged(route);
-        }
-
-        @Override
-        public void onRouteGrouped(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route,
-                android.media.MediaRouter.RouteGroup group, int index) {
-            mCallback.onRouteGrouped(route, group, index);
-        }
-
-        @Override
-        public void onRouteUngrouped(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route,
-                android.media.MediaRouter.RouteGroup group) {
-            mCallback.onRouteUngrouped(route, group);
-        }
-
-        @Override
-        public void onRouteVolumeChanged(android.media.MediaRouter router,
-                android.media.MediaRouter.RouteInfo route) {
-            mCallback.onRouteVolumeChanged(route);
-        }
-    }
-
-    static class VolumeCallbackProxy<T extends VolumeCallback>
-            extends android.media.MediaRouter.VolumeCallback {
-        protected final T mCallback;
-
-        public VolumeCallbackProxy(T callback) {
-            mCallback = callback;
-        }
-
-        @Override
-        public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route,
-                int volume) {
-            mCallback.onVolumeSetRequest(route, volume);
-        }
-
-        @Override
-        public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route,
-                int direction) {
-            mCallback.onVolumeUpdateRequest(route, direction);
-        }
-    }
-}
diff --git a/v4/tests/AndroidManifest.xml b/v4/tests/AndroidManifest.xml
index f99c5d1..85ffd7d 100644
--- a/v4/tests/AndroidManifest.xml
+++ b/v4/tests/AndroidManifest.xml
@@ -20,7 +20,8 @@
     <uses-sdk
             android:minSdkVersion="4"
             android:targetSdkVersion="23"
-            tools:overrideLibrary="android.support.test,android.support.test.espresso, android.support.test.espresso.idling"/>
+            tools:overrideLibrary="android.support.test, android.app, android.support.test.rule,
+                      android.support.test.espresso, android.support.test.espresso.idling"/>
 
     <uses-permission android:name="android.permission.VIBRATE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
@@ -29,7 +30,7 @@
 
     <application android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
-        <activity android:name="android.support.v4.widget.test.TextViewTestActivity"/>
+        <activity android:name="android.support.v4.widget.TextViewTestActivity"/>
 
         <activity android:name="android.support.v4.view.ViewPagerWithTitleStripActivity"/>
 
diff --git a/v4/tests/java/android/support/v4/BaseInstrumentationTestCase.java b/v4/tests/java/android/support/v4/BaseInstrumentationTestCase.java
new file mode 100644
index 0000000..5f9ce85
--- /dev/null
+++ b/v4/tests/java/android/support/v4/BaseInstrumentationTestCase.java
@@ -0,0 +1,33 @@
+/*
+ * 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.v4;
+
+import android.app.Activity;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseInstrumentationTestCase<A extends Activity> {
+    @Rule
+    public final ActivityTestRule<A> mActivityTestRule;
+
+    protected BaseInstrumentationTestCase(Class<A> activityClass) {
+        mActivityTestRule = new ActivityTestRule<A>(activityClass);
+    }
+}
diff --git a/v4/tests/java/android/support/v4/BaseTestActivity.java b/v4/tests/java/android/support/v4/BaseTestActivity.java
new file mode 100755
index 0000000..e0682ce
--- /dev/null
+++ b/v4/tests/java/android/support/v4/BaseTestActivity.java
@@ -0,0 +1,39 @@
+/*
+ * 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.v4;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public abstract class BaseTestActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        final int contentView = getContentViewLayoutResId();
+        if (contentView > 0) {
+            setContentView(contentView);
+        }
+        onContentViewSet();
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+    }
+
+    protected abstract int getContentViewLayoutResId();
+
+    protected void onContentViewSet() {}
+}
diff --git a/v4/tests/java/android/support/v4/testutils/LayoutDirectionActions.java b/v4/tests/java/android/support/v4/testutils/LayoutDirectionActions.java
new file mode 100755
index 0000000..25ae971
--- /dev/null
+++ b/v4/tests/java/android/support/v4/testutils/LayoutDirectionActions.java
@@ -0,0 +1,53 @@
+/*
+ * 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.v4.testutils;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.v4.view.ViewCompat;
+import android.view.View;
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+public class LayoutDirectionActions {
+    /**
+     * Sets layout direction on the view.
+     */
+    public static ViewAction setLayoutDirection(final int layoutDirection) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayed();
+            }
+
+            @Override
+            public String getDescription() {
+                return "set layout direction";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewCompat.setLayoutDirection(view, layoutDirection);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/v4/tests/java/android/support/v4/testutils/TextViewActions.java b/v4/tests/java/android/support/v4/testutils/TextViewActions.java
new file mode 100644
index 0000000..f67e8c0
--- /dev/null
+++ b/v4/tests/java/android/support/v4/testutils/TextViewActions.java
@@ -0,0 +1,230 @@
+/*
+ * 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.v4.testutils;
+
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+import android.support.annotation.StyleRes;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.v4.widget.TextViewCompat;
+import android.view.View;
+import android.widget.TextView;
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+
+public class TextViewActions {
+    /**
+     * Sets max lines count on <code>TextView</code>.
+     */
+    public static ViewAction setMaxLines(final int maxLines) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set max lines";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                textView.setMaxLines(maxLines);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets min lines count on <code>TextView</code>.
+     */
+    public static ViewAction setMinLines(final int minLines) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set min lines";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                textView.setMinLines(minLines);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets text content on <code>TextView</code>.
+     */
+    public static ViewAction setText(final @StringRes int stringResId) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set text";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                textView.setText(stringResId);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets text appearance on <code>TextView</code>.
+     */
+    public static ViewAction setTextAppearance(final @StyleRes int styleResId) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set text appearance";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setTextAppearance(textView, styleResId);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets compound drawables on <code>TextView</code>.
+     */
+    public static ViewAction setCompoundDrawablesRelative(final @Nullable Drawable start,
+            final @Nullable Drawable top, final @Nullable Drawable end,
+            final @Nullable Drawable bottom) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set compound drawables";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setCompoundDrawablesRelative(textView, start, top, end, bottom);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets compound drawables on <code>TextView</code>.
+     */
+    public static ViewAction setCompoundDrawablesRelativeWithIntrinsicBounds(
+            final @Nullable Drawable start, final @Nullable Drawable top,
+            final @Nullable Drawable end, final @Nullable Drawable bottom) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set compound drawables";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                        textView, start, top, end, bottom);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets compound drawables on <code>TextView</code>.
+     */
+    public static ViewAction setCompoundDrawablesRelativeWithIntrinsicBounds(
+            final @DrawableRes int start, final @DrawableRes int top, final @DrawableRes int end,
+            final @DrawableRes int bottom) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(TextView.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "TextView set compound drawables";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TextView textView = (TextView) view;
+                TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                        textView, start, top, end, bottom);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/v4/tests/java/android/support/v4/view/BaseViewPagerTest.java b/v4/tests/java/android/support/v4/view/BaseViewPagerTest.java
index 166fb1e..13d8372 100644
--- a/v4/tests/java/android/support/v4/view/BaseViewPagerTest.java
+++ b/v4/tests/java/android/support/v4/view/BaseViewPagerTest.java
@@ -17,47 +17,32 @@
 
 import android.app.Activity;
 import android.graphics.Color;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.support.v4.test.R;
+import android.support.v4.testutils.TestUtilsAssertions;
+import android.support.v4.testutils.TestUtilsMatchers;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.UiController;
-import android.support.v4.test.R;
-import android.support.v4.testutils.TestUtilsAssertions;
-import android.support.v4.testutils.TestUtilsMatchers;
-import android.support.v4.view.ViewPager;
-import android.support.v4.view.PagerTitleStrip;
-import android.support.v4.widget.TestActivity;
-
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
 
 import java.util.ArrayList;
 
-import org.hamcrest.Matcher;
-
 import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.action.ViewActions.swipeLeft;
 import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.assertion.PositionAssertions.*;
 import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.assertion.PositionAssertions.isBelow;
-import static android.support.test.espresso.assertion.PositionAssertions.isBottomAlignedWith;
-import static android.support.test.espresso.assertion.PositionAssertions.isLeftAlignedWith;
-import static android.support.test.espresso.assertion.PositionAssertions.isRightAlignedWith;
-import static android.support.test.espresso.assertion.PositionAssertions.isTopAlignedWith;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
+import static android.support.test.espresso.matcher.ViewMatchers.*;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Base class for testing <code>ViewPager</code>. Most of the testing logic should be in this
@@ -67,8 +52,7 @@
  * Testing logic that does depend on the specific pager title implementation is pushed into the
  * extending classes in <code>assertStripInteraction()</code> method.
  */
-public abstract class BaseViewPagerTest<T extends Activity>
-        extends ActivityInstrumentationTestCase2<T> {
+public abstract class BaseViewPagerTest<T extends Activity> extends BaseInstrumentationTestCase<T> {
     protected ViewPager mViewPager;
 
     protected static class BasePagerAdapter<Q> extends PagerAdapter {
@@ -181,14 +165,12 @@
     }
 
     public BaseViewPagerTest(Class<T> activityClass) {
-        super("android.support.v4.view", activityClass);
+        super(activityClass);
     }
 
-    @Override
+    @Before
     public void setUp() throws Exception {
-        super.setUp();
-
-        final T activity = getActivity();
+        final T activity = mActivityTestRule.getActivity();
         mViewPager = (ViewPager) activity.findViewById(R.id.pager);
 
         ColorPagerAdapter adapter = new ColorPagerAdapter();
@@ -199,13 +181,12 @@
                 ViewPagerActions.scrollToPage(0));
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
         onView(withId(R.id.pager)).perform(ViewPagerActions.setAdapter(null));
-
-        super.tearDown();
     }
 
+    @Test
     @SmallTest
     public void testPageSelections() {
         assertEquals("Initial state", 0, mViewPager.getCurrentItem());
@@ -232,6 +213,7 @@
 
     }
 
+    @Test
     @SmallTest
     public void testPageSwipes() {
         assertEquals("Initial state", 0, mViewPager.getCurrentItem());
@@ -257,6 +239,7 @@
         assertEquals("Swipe right beyond first page", 0, mViewPager.getCurrentItem());
     }
 
+    @Test
     @SmallTest
     public void testPageSwipesComposite() {
         assertEquals("Initial state", 0, mViewPager.getCurrentItem());
@@ -275,6 +258,7 @@
         assertEquals("Swipe right beyond first page and then left", 1, mViewPager.getCurrentItem());
     }
 
+    @Test
     @SmallTest
     public void testPageContent() {
         assertEquals("Initial state", 0, mViewPager.getCurrentItem());
@@ -315,6 +299,7 @@
                 TestUtilsMatchers.backgroundColor(Color.BLUE))));
     }
 
+    @Test
     @SmallTest
     public void testAdapterChange() {
         // Verify that we have the expected initial adapter
@@ -427,6 +412,7 @@
         }
     }
 
+    @Test
     @SmallTest
     public void testPagerStrip() {
         // Set an adapter with 5 pages
diff --git a/v4/tests/java/android/support/v4/view/GravityCompatTest.java b/v4/tests/java/android/support/v4/view/GravityCompatTest.java
index db62b0c..2e2f180 100644
--- a/v4/tests/java/android/support/v4/view/GravityCompatTest.java
+++ b/v4/tests/java/android/support/v4/view/GravityCompatTest.java
@@ -17,15 +17,10 @@
 
 import android.graphics.Rect;
 import android.os.Build;
-import android.view.Gravity;
-import android.view.View;
-
 import android.support.v4.testutils.TestUtils;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.view.ViewCompat;
-
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Gravity;
 
 public class GravityCompatTest extends AndroidTestCase {
     @SmallTest
diff --git a/v4/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java b/v4/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java
index db08a1a..057db99 100644
--- a/v4/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java
+++ b/v4/tests/java/android/support/v4/view/MarginLayoutParamsCompatTest.java
@@ -15,16 +15,10 @@
  */
 package android.support.v4.view;
 
-import android.view.View;
-
-import android.support.v4.view.MarginLayoutParamsCompat;
-import android.support.v4.view.ViewCompat;
-
 import android.os.Build;
-import android.view.ViewGroup;
-
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.ViewGroup;
 
 public class MarginLayoutParamsCompatTest extends AndroidTestCase {
     @SmallTest
diff --git a/v4/tests/java/android/support/v4/view/ViewCompatTest.java b/v4/tests/java/android/support/v4/view/ViewCompatTest.java
index 483d16b..29a0f5a 100644
--- a/v4/tests/java/android/support/v4/view/ViewCompatTest.java
+++ b/v4/tests/java/android/support/v4/view/ViewCompatTest.java
@@ -15,12 +15,9 @@
  */
 package android.support.v4.view;
 
-import android.view.View;
-
-import android.support.v4.view.ViewCompat;
-
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
 
 public class ViewCompatTest extends AndroidTestCase {
     @SmallTest
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerActions.java b/v4/tests/java/android/support/v4/view/ViewPagerActions.java
index b1e1a76..2b37e6f 100644
--- a/v4/tests/java/android/support/v4/view/ViewPagerActions.java
+++ b/v4/tests/java/android/support/v4/view/ViewPagerActions.java
@@ -22,20 +22,12 @@
 import android.support.test.espresso.action.GeneralClickAction;
 import android.support.test.espresso.action.Press;
 import android.support.test.espresso.action.Tap;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
 import android.view.View;
 import android.widget.TextView;
-
 import org.hamcrest.Matcher;
 
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.Matchers.allOf;
 
 public class ViewPagerActions {
     /**
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripActivity.java b/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripActivity.java
index edc4da9..2cb3048 100644
--- a/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripActivity.java
+++ b/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripActivity.java
@@ -21,8 +21,6 @@
 import android.support.v4.test.R;
 import android.view.WindowManager;
 
-import java.util.ArrayList;
-
 public class ViewPagerWithTabStripActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripTest.java b/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripTest.java
index 706d2e2..3e4a82f 100644
--- a/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripTest.java
+++ b/v4/tests/java/android/support/v4/view/ViewPagerWithTabStripTest.java
@@ -15,7 +15,6 @@
  */
 package android.support.v4.view;
 
-import android.support.v4.view.PagerTitleStrip;
 import android.support.v4.test.R;
 
 import static android.support.test.espresso.Espresso.onView;
@@ -23,8 +22,8 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
 import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Provides assertions that depend on the interactive nature of <code>PagerTabStrip</code>.
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripActivity.java b/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripActivity.java
index 28dcb07..49d6e76 100644
--- a/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripActivity.java
+++ b/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripActivity.java
@@ -21,8 +21,6 @@
 import android.support.v4.test.R;
 import android.view.WindowManager;
 
-import java.util.ArrayList;
-
 public class ViewPagerWithTitleStripActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripTest.java b/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripTest.java
index 528db86..8d831dc 100644
--- a/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripTest.java
+++ b/v4/tests/java/android/support/v4/view/ViewPagerWithTitleStripTest.java
@@ -15,7 +15,6 @@
  */
 package android.support.v4.view;
 
-import android.support.v4.view.PagerTitleStrip;
 import android.support.v4.test.R;
 
 import static android.support.test.espresso.Espresso.onView;
@@ -23,8 +22,8 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
-
 import static org.hamcrest.Matchers.allOf;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Provides assertions that depend on the non-interactive nature of <code>PagerTabStrip</code>.
diff --git a/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java b/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java
index d0f5b00..3ef236f 100644
--- a/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java
+++ b/v4/tests/java/android/support/v4/widget/TextViewCompatTest.java
@@ -17,35 +17,27 @@
 
 package android.support.v4.widget;
 
-import org.junit.After;
+import android.content.res.Resources;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.support.v4.test.R;
+import android.support.v4.testutils.TestUtils;
+import android.support.v4.view.ViewCompat;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.widget.TextView;
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-import android.app.Instrumentation;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Looper;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.support.annotation.ColorInt;
-import android.support.annotation.LayoutRes;
-import android.support.test.InstrumentationRegistry;
-import android.support.v4.test.R;
-import android.support.v4.view.ViewCompat;
-import android.support.v4.widget.TextViewCompat;
-import android.support.v4.testutils.TestUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.v4.testutils.LayoutDirectionActions.setLayoutDirection;
+import static android.support.v4.testutils.TextViewActions.*;
+import static org.junit.Assert.*;
 
-public class TextViewCompatTest extends ActivityInstrumentationTestCase2<TestActivity> {
+public class TextViewCompatTest extends BaseInstrumentationTestCase<TextViewTestActivity> {
     private static final String TAG = "TextViewCompatTest";
 
     private TextView mTextView;
@@ -72,127 +64,75 @@
     }
 
     public TextViewCompatTest() {
-        super("android.support.v4.widget", TestActivity.class);
+        super(TextViewTestActivity.class);
     }
 
-    @Override
-    public void tearDown() throws Exception {
-        if (mTextView != null) {
-            removeTextView();
-        }
-
-        getInstrumentation().waitForIdleSync();
-        super.tearDown();
+    @Before
+    public void setUp() {
+        mTextView = (TextView) mActivityTestRule.getActivity().findViewById(R.id.text_view);
     }
 
-    private boolean isMainThread() {
-        return Looper.myLooper() == Looper.getMainLooper();
-    }
-
-    private void removeTextView() {
-        if (mTextView == null) {
-            return;
-        }
-        if (!isMainThread()) {
-            getInstrumentation().waitForIdleSync();
-        }
-        try {
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    getActivity().mContainer.removeAllViews();
-                }
-            });
-        } catch (Throwable throwable) {
-            Log.e(TAG, "", throwable);
-        }
-        mTextView = null;
-    }
-
-    private void createAndAddTextView() {
-        final TestActivity activity = getActivity();
-        mTextView = new TextView(activity);
-        activity.mContainer.addView(mTextView);
-
-        // Explicitly measure and layout the text view. This way the core TextView updates its
-        // internal tracking of various visual facets so those can be tested in the relevant
-        // tests - such as, for example, where each drawable is positioned relative to the text.
-        final DisplayMetrics metrics = getActivity().getResources().getDisplayMetrics();
-        int textViewWidthPx = TestUtils.convertSizeDipsToPixels(metrics, 200);
-        int textViewHeightPx = TestUtils.convertSizeDipsToPixels(metrics, 60);
-        mTextView.measure(
-                View.MeasureSpec.makeMeasureSpec(textViewWidthPx, View.MeasureSpec.EXACTLY),
-                View.MeasureSpec.makeMeasureSpec(textViewHeightPx, View.MeasureSpec.EXACTLY));
-        mTextView.layout(0, 0, textViewWidthPx, textViewHeightPx);
-    }
-
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testMaxLines() throws Throwable {
-        createAndAddTextView();
         final int maxLinesCount = 4;
-        mTextView.setMaxLines(maxLinesCount);
+        onView(withId(R.id.text_view)).perform(setMaxLines(maxLinesCount));
 
         assertEquals("Empty view: Max lines must match", TextViewCompat.getMaxLines(mTextView),
                 maxLinesCount);
 
-        mTextView.setText(R.string.test_text_short);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_short));
         assertEquals("Short text: Max lines must match", TextViewCompat.getMaxLines(mTextView),
                 maxLinesCount);
 
-        mTextView.setText(R.string.test_text_medium);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_medium));
         assertEquals("Medium text: Max lines must match", TextViewCompat.getMaxLines(mTextView),
                 maxLinesCount);
 
-        mTextView.setText(R.string.test_text_long);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_long));
         assertEquals("Long text: Max lines must match", TextViewCompat.getMaxLines(mTextView),
                 maxLinesCount);
     }
 
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testMinLines() throws Throwable {
-        createAndAddTextView();
         final int minLinesCount = 3;
-        mTextView.setMinLines(minLinesCount);
+        onView(withId(R.id.text_view)).perform(setMinLines(minLinesCount));
 
         assertEquals("Empty view: Min lines must match", TextViewCompat.getMinLines(mTextView),
                 minLinesCount);
 
-        mTextView.setText(R.string.test_text_short);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_short));
         assertEquals("Short text: Min lines must match", TextViewCompat.getMinLines(mTextView),
                 minLinesCount);
 
-        mTextView.setText(R.string.test_text_medium);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_medium));
         assertEquals("Medium text: Min lines must match", TextViewCompat.getMinLines(mTextView),
                 minLinesCount);
 
-        mTextView.setText(R.string.test_text_long);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_long));
         assertEquals("Long text: Min lines must match", TextViewCompat.getMinLines(mTextView),
                 minLinesCount);
     }
 
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testStyle() throws Throwable {
-        createAndAddTextView();
+        onView(withId(R.id.text_view)).perform(setTextAppearance(R.style.TextMediumStyle));
 
-        TextViewCompat.setTextAppearance(mTextView, R.style.TextMediumStyle);
-
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivityTestRule.getActivity().getResources();
         assertTrue("Styled text view: style",
                 mTextView.getTypeface().isItalic() || (mTextView.getPaint().getTextSkewX() < 0));
         assertEquals("Styled text view: color", mTextView.getTextColors().getDefaultColor(),
                 res.getColor(R.color.text_color));
         assertEquals("Styled text view: size", mTextView.getTextSize(),
-                (float) res.getDimensionPixelSize(R.dimen.text_medium_size));
+                (float) res.getDimensionPixelSize(R.dimen.text_medium_size), 1.0f);
     }
 
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testCompoundDrawablesRelative() throws Throwable {
-        createAndAddTextView();
-
         final Drawable drawableStart = new ColorDrawable(0xFFFF0000);
         drawableStart.setBounds(0, 0, 20, 20);
         final Drawable drawableTop = new ColorDrawable(0xFF00FF00);
@@ -200,9 +140,9 @@
         final Drawable drawableEnd = new ColorDrawable(0xFF0000FF);
         drawableEnd.setBounds(0, 0, 25, 20);
 
-        mTextView.setText(R.string.test_text_medium);
-        TextViewCompat.setCompoundDrawablesRelative(mTextView, drawableStart, drawableTop,
-                drawableEnd, null);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_medium));
+        onView(withId(R.id.text_view)).perform(setCompoundDrawablesRelative(drawableStart,
+                drawableTop, drawableEnd, null));
 
         final Drawable[] drawablesAbsolute = mTextView.getCompoundDrawables();
 
@@ -227,12 +167,10 @@
         assertNull("Compound drawable: bottom", drawablesAbsolute[3]);
     }
 
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testCompoundDrawablesRelativeRtl() throws Throwable {
-        createAndAddTextView();
-
-        ViewCompat.setLayoutDirection(mTextView, ViewCompat.LAYOUT_DIRECTION_RTL);
+        onView(withId(R.id.text_view)).perform(setLayoutDirection(ViewCompat.LAYOUT_DIRECTION_RTL));
 
         final Drawable drawableStart = new ColorDrawable(0xFFFF0000);
         drawableStart.setBounds(0, 0, 20, 20);
@@ -241,9 +179,9 @@
         final Drawable drawableEnd = new ColorDrawable(0xFF0000FF);
         drawableEnd.setBounds(0, 0, 25, 20);
 
-        mTextView.setText(R.string.test_text_medium);
-        TextViewCompat.setCompoundDrawablesRelative(mTextView, drawableStart, drawableTop,
-                drawableEnd, null);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_medium));
+        onView(withId(R.id.text_view)).perform(setCompoundDrawablesRelative(drawableStart,
+                drawableTop, drawableEnd, null));
 
         // Check to see whether our text view is under RTL mode
         if (ViewCompat.getLayoutDirection(mTextView) != ViewCompat.LAYOUT_DIRECTION_RTL) {
@@ -276,18 +214,16 @@
         assertNull("Compound drawable: bottom", drawablesAbsolute[3]);
     }
 
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testCompoundDrawablesRelativeWithIntrinsicBounds() throws Throwable {
-        createAndAddTextView();
-
         final Drawable drawableStart = new TestDrawable(0xFFFF0000, 30, 20);
         final Drawable drawableEnd = new TestDrawable(0xFF0000FF, 25, 45);
         final Drawable drawableBottom = new TestDrawable(0xFF00FF00, 15, 35);
 
-        mTextView.setText(R.string.test_text_long);
-        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(mTextView, drawableStart,
-                null, drawableEnd, drawableBottom);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_long));
+        onView(withId(R.id.text_view)).perform(setCompoundDrawablesRelativeWithIntrinsicBounds(
+                drawableStart, null, drawableEnd, drawableBottom));
 
         final Drawable[] drawablesAbsolute = mTextView.getCompoundDrawables();
 
@@ -312,20 +248,18 @@
                 drawablesAbsolute[3].getBounds().height(), 35);
     }
 
-    @UiThreadTest
+    @Test
     @SmallTest
     public void testCompoundDrawablesRelativeWithIntrinsicBoundsRtl() throws Throwable {
-        createAndAddTextView();
-
-        ViewCompat.setLayoutDirection(mTextView, ViewCompat.LAYOUT_DIRECTION_RTL);
+        onView(withId(R.id.text_view)).perform(setLayoutDirection(ViewCompat.LAYOUT_DIRECTION_RTL));
 
         final Drawable drawableStart = new TestDrawable(0xFFFF0000, 30, 20);
         final Drawable drawableEnd = new TestDrawable(0xFF0000FF, 25, 45);
         final Drawable drawableBottom = new TestDrawable(0xFF00FF00, 15, 35);
 
-        mTextView.setText(R.string.test_text_long);
-        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(mTextView, drawableStart,
-                null, drawableEnd, drawableBottom);
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_long));
+        onView(withId(R.id.text_view)).perform(setCompoundDrawablesRelativeWithIntrinsicBounds(
+                drawableStart, null, drawableEnd, drawableBottom));
 
         // Check to see whether our text view is under RTL mode
         if (ViewCompat.getLayoutDirection(mTextView) != ViewCompat.LAYOUT_DIRECTION_RTL) {
@@ -358,18 +292,16 @@
                 drawablesAbsolute[3].getBounds().height(), 35);
     }
 
-    @UiThreadTest
+    @Test
     @MediumTest
     public void testCompoundDrawablesRelativeWithIntrinsicBoundsById() throws Throwable {
-        createAndAddTextView();
-
-        mTextView.setText(R.string.test_text_long);
-        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(mTextView,
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_long));
+        onView(withId(R.id.text_view)).perform(setCompoundDrawablesRelativeWithIntrinsicBounds(
                 R.drawable.test_drawable_red, 0,
-                R.drawable.test_drawable_green, R.drawable.test_drawable_blue);
+                R.drawable.test_drawable_green, R.drawable.test_drawable_blue));
 
         final Drawable[] drawablesAbsolute = mTextView.getCompoundDrawables();
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivityTestRule.getActivity().getResources();
 
         // The entire left drawable should be the specific red color
         TestUtils.assertAllPixelsOfColor("Compound drawable: left color",
@@ -404,17 +336,15 @@
                 res.getDimensionPixelSize(R.dimen.drawable_small_size));
     }
 
-    @UiThreadTest
+    @Test
     @MediumTest
     public void testCompoundDrawablesRelativeWithIntrinsicBoundsByIdRtl() throws Throwable {
-        createAndAddTextView();
+        onView(withId(R.id.text_view)).perform(setLayoutDirection(ViewCompat.LAYOUT_DIRECTION_RTL));
 
-        ViewCompat.setLayoutDirection(mTextView, ViewCompat.LAYOUT_DIRECTION_RTL);
-
-        mTextView.setText(R.string.test_text_long);
-        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(mTextView,
+        onView(withId(R.id.text_view)).perform(setText(R.string.test_text_long));
+        onView(withId(R.id.text_view)).perform(setCompoundDrawablesRelativeWithIntrinsicBounds(
                 R.drawable.test_drawable_red, 0,
-                R.drawable.test_drawable_green, R.drawable.test_drawable_blue);
+                R.drawable.test_drawable_green, R.drawable.test_drawable_blue));
 
         // Check to see whether our text view is under RTL mode
         if (ViewCompat.getLayoutDirection(mTextView) != ViewCompat.LAYOUT_DIRECTION_RTL) {
@@ -423,7 +353,7 @@
         }
 
         final Drawable[] drawablesAbsolute = mTextView.getCompoundDrawables();
-        final Resources res = getActivity().getResources();
+        final Resources res = mActivityTestRule.getActivity().getResources();
 
         // The entire left / end drawable should be the specific green color
         TestUtils.assertAllPixelsOfColor("Compound drawable: left color",
diff --git a/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java b/v4/tests/java/android/support/v4/widget/TextViewTestActivity.java
similarity index 68%
rename from v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java
rename to v4/tests/java/android/support/v4/widget/TextViewTestActivity.java
index 7366127..dcaab70 100644
--- a/v4/tests/java/android/support/v4/widget/test/TextViewTestActivity.java
+++ b/v4/tests/java/android/support/v4/widget/TextViewTestActivity.java
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package android.support.v4.widget.test;
+package android.support.v4.widget;
 
+import android.support.v4.BaseTestActivity;
+import android.support.v4.test.R;
 
-import android.app.Activity;
-
-public class TextViewTestActivity extends Activity {
-
+public class TextViewTestActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.text_view_activity;
+    }
 }
diff --git a/v4/tests/res/layout/text_view_activity.xml b/v4/tests/res/layout/text_view_activity.xml
new file mode 100644
index 0000000..ba5d688
--- /dev/null
+++ b/v4/tests/res/layout/text_view_activity.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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/text_view"
+        android:layout_width="200dip"
+        android:layout_height="60dip" />
+</FrameLayout>
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 8515409..734f46f 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.support.annotation.CallSuper;
 import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.annotation.StyleRes;
 import android.support.v4.app.ActivityCompat;
@@ -96,20 +97,20 @@
     }
 
     /**
-     * Set a {@link android.widget.Toolbar Toolbar} to act as the {@link android.support.v7.app.ActionBar} for this
-     * Activity window.
+     * Set a {@link android.widget.Toolbar Toolbar} to act as the
+     * {@link android.support.v7.app.ActionBar} for this Activity window.
      *
      * <p>When set to a non-null value the {@link #getActionBar()} method will return
-     * an {@link android.support.v7.app.ActionBar} object that can be used to control the given toolbar as if it were
-     * a traditional window decor action bar. The toolbar's menu will be populated with the
-     * Activity's options menu and the navigation button will be wired through the standard
-     * {@link android.R.id#home home} menu select action.</p>
+     * an {@link android.support.v7.app.ActionBar} object that can be used to control the given
+     * toolbar as if it were a traditional window decor action bar. The toolbar's menu will be
+     * populated with the Activity's options menu and the navigation button will be wired through
+     * the standard {@link android.R.id#home home} menu select action.</p>
      *
      * <p>In order to use a Toolbar within the Activity's window content the application
      * must not request the window feature
      * {@link android.view.Window#FEATURE_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
      *
-     * @param toolbar Toolbar to set as the Activity's action bar
+     * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it
      */
     public void setSupportActionBar(@Nullable Toolbar toolbar) {
         getDelegate().setSupportActionBar(toolbar);
@@ -221,7 +222,7 @@
      * @param mode The new action mode.
      */
     @CallSuper
-    public void onSupportActionModeStarted(ActionMode mode) {
+    public void onSupportActionModeStarted(@NonNull ActionMode mode) {
     }
 
     /**
@@ -231,7 +232,7 @@
      * @param mode The action mode that just finished.
      */
     @CallSuper
-    public void onSupportActionModeFinished(ActionMode mode) {
+    public void onSupportActionModeFinished(@NonNull ActionMode mode) {
     }
 
     /**
@@ -245,11 +246,18 @@
      */
     @Nullable
     @Override
-    public ActionMode onWindowStartingSupportActionMode(ActionMode.Callback callback) {
+    public ActionMode onWindowStartingSupportActionMode(@NonNull ActionMode.Callback callback) {
         return null;
     }
 
-    public ActionMode startSupportActionMode(ActionMode.Callback callback) {
+    /**
+     * Start an action mode.
+     *
+     * @param callback Callback that will manage lifecycle events for this context mode
+     * @return The ContextMode that was started, or null if it was canceled
+     */
+    @Nullable
+    public ActionMode startSupportActionMode(@NonNull ActionMode.Callback callback) {
         return getDelegate().startSupportActionMode(callback);
     }
 
@@ -304,7 +312,7 @@
      * @param builder An empty TaskStackBuilder - the application should add intents representing
      *                the desired task stack
      */
-    public void onCreateSupportNavigateUpTaskStack(TaskStackBuilder builder) {
+    public void onCreateSupportNavigateUpTaskStack(@NonNull TaskStackBuilder builder) {
         builder.addParentStack(this);
     }
 
@@ -323,7 +331,7 @@
      * @param builder A TaskStackBuilder that has been populated with Intents by
      *                onCreateNavigateUpTaskStack.
      */
-    public void onPrepareSupportNavigateUpTaskStack(TaskStackBuilder builder) {
+    public void onPrepareSupportNavigateUpTaskStack(@NonNull TaskStackBuilder builder) {
     }
 
     /**
@@ -403,7 +411,7 @@
      * @return true if navigating up should recreate a new task stack, false if the same task
      *         should be used for the destination
      */
-    public boolean supportShouldUpRecreateTask(Intent targetIntent) {
+    public boolean supportShouldUpRecreateTask(@NonNull Intent targetIntent) {
         return NavUtils.shouldUpRecreateTask(this, targetIntent);
     }
 
@@ -419,7 +427,7 @@
      *
      * @param upIntent An intent representing the target destination for up navigation
      */
-    public void supportNavigateUpTo(Intent upIntent) {
+    public void supportNavigateUpTo(@NonNull Intent upIntent) {
         NavUtils.navigateUpTo(this, upIntent);
     }
 
@@ -473,6 +481,7 @@
     /**
      * @return The {@link AppCompatDelegate} being used by this Activity.
      */
+    @NonNull
     public AppCompatDelegate getDelegate() {
         if (mDelegate == null) {
             mDelegate = AppCompatDelegate.create(this, this);
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
index becfe6c..2f887cc 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -26,6 +26,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.view.WindowCompat;
 import android.support.v7.appcompat.R;
@@ -187,6 +188,7 @@
      *
      * @return AppCompat's action bar, or null if it does not have one.
      */
+    @Nullable
     public abstract ActionBar getSupportActionBar();
 
     /**
@@ -202,9 +204,9 @@
      * must not request the window feature
      * {@link AppCompatDelegate#FEATURE_SUPPORT_ACTION_BAR FEATURE_SUPPORT_ACTION_BAR}.</p>
      *
-     * @param toolbar Toolbar to set as the Activity's action bar
+     * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it
      */
-    public abstract void setSupportActionBar(Toolbar toolbar);
+    public abstract void setSupportActionBar(@Nullable Toolbar toolbar);
 
     /**
      * Return the value of this call from your {@link Activity#getMenuInflater()}
@@ -271,7 +273,7 @@
     /**
      * Should be called from {@link Activity#onTitleChanged(CharSequence, int)}}
      */
-    public abstract void setTitle(CharSequence title);
+    public abstract void setTitle(@Nullable CharSequence title);
 
     /**
      * Should be called from {@link Activity#invalidateOptionsMenu()}} or
@@ -288,6 +290,7 @@
      * Returns an {@link ActionBarDrawerToggle.Delegate} which can be returned from your Activity
      * if it implements {@link ActionBarDrawerToggle.DelegateProvider}.
      */
+    @Nullable
     public abstract ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
 
     /**
@@ -317,7 +320,8 @@
      * @param callback Callback that will manage lifecycle events for this context mode
      * @return The ContextMode that was started, or null if it was canceled
      */
-    public abstract ActionMode startSupportActionMode(ActionMode.Callback callback);
+    @Nullable
+    public abstract ActionMode startSupportActionMode(@NonNull ActionMode.Callback callback);
 
     /**
      * Installs AppCompat's {@link android.view.LayoutInflater} Factory so that it can replace
@@ -348,7 +352,7 @@
      * {@link android.view.LayoutInflater LayoutInflater} factory, and have therefore not
      * installed the default factory via {@link #installViewFactory()}.
      */
-    public abstract View createView(View parent, String name, @NonNull Context context,
+    public abstract View createView(@Nullable View parent, String name, @NonNull Context context,
             @NonNull AttributeSet attrs);
 
     /**
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
index 05c8ec3..cc13909 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
@@ -206,11 +206,18 @@
             ab.onDestroy();
         }
 
-        ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity) mContext).getTitle(),
-                mAppCompatWindowCallback);
-        mActionBar = tbab;
-        mWindow.setCallback(tbab.getWrappedWindowCallback());
-        tbab.invalidateOptionsMenu();
+        if (toolbar != null) {
+            final ToolbarActionBar tbab = new ToolbarActionBar(toolbar,
+                    ((Activity) mContext).getTitle(), mAppCompatWindowCallback);
+            mActionBar = tbab;
+            mWindow.setCallback(tbab.getWrappedWindowCallback());
+        } else {
+            mActionBar = null;
+            // Re-set the original window callback since we may have already set a Toolbar wrapper
+            mWindow.setCallback(mAppCompatWindowCallback);
+        }
+
+        invalidateOptionsMenu();
     }
 
     @Override
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
index b517fc3..2ae4d64 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatBaseViewTest.java
@@ -24,17 +24,20 @@
 import android.support.annotation.NonNull;
 import android.support.v4.content.res.ResourcesCompat;
 import android.support.v4.graphics.ColorUtils;
+import android.support.v7.app.BaseInstrumentationTestCase;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.AppCompatTintableViewActions;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.testutils.TestUtils;
-import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
+import org.junit.Before;
+import org.junit.Test;
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.junit.Assert.assertNull;
 
 /**
  * Base class for testing custom view extensions in appcompat-v7 that implement the
@@ -43,19 +46,20 @@
  * base view class (such as <code>AppCompatTextView</code>'s all-caps support).
  */
 public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View>
-        extends ActivityInstrumentationTestCase2<A> {
+        extends BaseInstrumentationTestCase<A> {
     protected ViewGroup mContainer;
 
+    protected Resources mResources;
+
     public AppCompatBaseViewTest(Class clazz) {
         super(clazz);
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        final A activity = getActivity();
+    @Before
+    public void setUp() {
+        final A activity = mActivityTestRule.getActivity();
         mContainer = (ViewGroup) activity.findViewById(R.id.container);
+        mResources = activity.getResources();
     }
 
     private void verifyBackgroundIsColoredAs(String description, @NonNull View view,
@@ -70,10 +74,10 @@
      * This method tests that background tinting is not applied when the
      * tintable view has no background.
      */
+    @Test
     @SmallTest
     public void testBackgroundTintingWithNoBackground() {
         final @IdRes int viewId = R.id.view_tinted_no_background;
-        final Resources res = getActivity().getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
         // Note that all the asserts in this test check that the view background
@@ -93,7 +97,7 @@
         // Load a new color state list, set it on the view and check that the background
         // is still null.
         final ColorStateList sandColor = ResourcesCompat.getColorStateList(
-                res, R.color.color_state_list_sand, null);
+                mResources, R.color.color_state_list_sand, null);
         onView(withId(viewId)).perform(
                 AppCompatTintableViewActions.setBackgroundTintList(sandColor));
 
@@ -111,18 +115,24 @@
      * in enabled and disabled state across a number of <code>ColorStateList</code>s set as
      * background tint lists on the same background.
      */
+    @Test
     @SmallTest
     public void testBackgroundTintingAcrossStateChange() {
         final @IdRes int viewId = R.id.view_tinted_background;
-        final Resources res = getActivity().getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
-        @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
-        @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null);
-        @ColorInt int sandDefault = ResourcesCompat.getColor(res, R.color.sand_default, null);
-        @ColorInt int sandDisabled = ResourcesCompat.getColor(res, R.color.sand_disabled, null);
-        @ColorInt int oceanDefault = ResourcesCompat.getColor(res, R.color.ocean_default, null);
-        @ColorInt int oceanDisabled = ResourcesCompat.getColor(res, R.color.ocean_disabled, null);
+        final @ColorInt int lilacDefault = ResourcesCompat.getColor(
+                mResources, R.color.lilac_default, null);
+        final @ColorInt int lilacDisabled = ResourcesCompat.getColor(
+                mResources, R.color.lilac_disabled, null);
+        final @ColorInt int sandDefault = ResourcesCompat.getColor(
+                mResources, R.color.sand_default, null);
+        final @ColorInt int sandDisabled = ResourcesCompat.getColor(
+                mResources, R.color.sand_disabled, null);
+        final @ColorInt int oceanDefault = ResourcesCompat.getColor(
+                mResources, R.color.ocean_default, null);
+        final @ColorInt int oceanDisabled = ResourcesCompat.getColor(
+                mResources, R.color.ocean_disabled, null);
 
         // Test the default state for tinting set up in the layout XML file.
         verifyBackgroundIsColoredAs("Default lilac tinting in enabled state", view,
@@ -143,7 +153,7 @@
         // Load a new color state list, set it on the view and check that the background has
         // switched to the matching entry in newly set color state list.
         final ColorStateList sandColor = ResourcesCompat.getColorStateList(
-                res, R.color.color_state_list_sand, null);
+                mResources, R.color.color_state_list_sand, null);
         onView(withId(viewId)).perform(
                 AppCompatTintableViewActions.setBackgroundTintList(sandColor));
         verifyBackgroundIsColoredAs("New sand tinting in enabled state", view,
@@ -164,7 +174,7 @@
         // Load another color state list, set it on the view and check that the background has
         // switched to the matching entry in newly set color state list.
         final ColorStateList oceanColor = ResourcesCompat.getColorStateList(
-                res, R.color.color_state_list_ocean, null);
+                mResources, R.color.color_state_list_ocean, null);
         onView(withId(viewId)).perform(
                 AppCompatTintableViewActions.setBackgroundTintList(oceanColor));
         verifyBackgroundIsColoredAs("New ocean tinting in enabled state", view,
@@ -188,20 +198,20 @@
      * in enabled and disabled state across the same background respects the currently set
      * background tinting mode.
      */
+    @Test
     @SmallTest
     public void testBackgroundTintingAcrossModeChange() {
         final @IdRes int viewId = R.id.view_untinted_background;
-        final Resources res = getActivity().getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
-        @ColorInt int emeraldDefault = ResourcesCompat.getColor(
-                res, R.color.emerald_translucent_default, null);
-        @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
-                res, R.color.emerald_translucent_disabled, null);
+        final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
+                mResources, R.color.emerald_translucent_default, null);
+        final @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
+                mResources, R.color.emerald_translucent_disabled, null);
         // This is the fill color of R.drawable.test_background_green set on our view
         // that we'll be testing in this method
-        @ColorInt int backgroundColor = ResourcesCompat.getColor(
-                res, R.color.test_green, null);
+        final @ColorInt int backgroundColor = ResourcesCompat.getColor(
+                mResources, R.color.test_green, null);
 
         // Test the default state for tinting set up in the layout XML file.
         verifyBackgroundIsColoredAs("Default no tinting in enabled state", view,
@@ -221,7 +231,7 @@
         // Load a new color state list, set it on the view and check that the background has
         // switched to the matching entry in newly set color state list.
         final ColorStateList emeraldColor = ResourcesCompat.getColorStateList(
-                res, R.color.color_state_list_emerald_translucent, null);
+                mResources, R.color.color_state_list_emerald_translucent, null);
         onView(withId(viewId)).perform(
                 AppCompatTintableViewActions.setBackgroundTintList(emeraldColor));
         verifyBackgroundIsColoredAs("New emerald tinting in enabled state under src_in", view,
@@ -259,20 +269,22 @@
      * This method tests that opaque background tinting applied to tintable view
      * is applied correctly after changing the background itself of the view.
      */
+    @Test
     @SmallTest
     public void testBackgroundOpaqueTintingAcrossBackgroundChange() {
         final @IdRes int viewId = R.id.view_tinted_no_background;
-        final Resources res = getActivity().getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
-        @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null);
-        @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null);
+        final @ColorInt int lilacDefault = ResourcesCompat.getColor(
+                mResources, R.color.lilac_default, null);
+        final @ColorInt int lilacDisabled = ResourcesCompat.getColor(
+                mResources, R.color.lilac_disabled, null);
 
         assertNull("No background after XML loading", view.getBackground());
 
         // Set background on our view
         onView(withId(viewId)).perform(AppCompatTintableViewActions.setBackgroundDrawable(
-                ResourcesCompat.getDrawable(res, R.drawable.test_background_green, null)));
+                ResourcesCompat.getDrawable(mResources, R.drawable.test_background_green, null)));
 
         // Test the default state for tinting set up in the layout XML file.
         verifyBackgroundIsColoredAs("Default lilac tinting in enabled state on green background",
@@ -315,22 +327,22 @@
      * This method tests that translucent background tinting applied to tintable view
      * is applied correctly after changing the background itself of the view.
      */
+    @Test
     @SmallTest
     public void testBackgroundTranslucentTintingAcrossBackgroundChange() {
         final @IdRes int viewId = R.id.view_untinted_no_background;
-        final Resources res = getActivity().getResources();
         final T view = (T) mContainer.findViewById(viewId);
 
-        @ColorInt int emeraldDefault = ResourcesCompat.getColor(
-                res, R.color.emerald_translucent_default, null);
-        @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
-                res, R.color.emerald_translucent_disabled, null);
+        final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
+                mResources, R.color.emerald_translucent_default, null);
+        final @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
+                mResources, R.color.emerald_translucent_disabled, null);
         // This is the fill color of R.drawable.test_background_green set on our view
         // that we'll be testing in this method
-        @ColorInt int backgroundColorGreen = ResourcesCompat.getColor(
-                res, R.color.test_green, null);
-        @ColorInt int backgroundColorRed = ResourcesCompat.getColor(
-                res, R.color.test_red, null);
+        final @ColorInt int backgroundColorGreen = ResourcesCompat.getColor(
+                mResources, R.color.test_green, null);
+        final @ColorInt int backgroundColorRed = ResourcesCompat.getColor(
+                mResources, R.color.test_red, null);
 
         assertNull("No background after XML loading", view.getBackground());
 
@@ -342,13 +354,13 @@
                 AppCompatTintableViewActions.setBackgroundTintMode(PorterDuff.Mode.SRC_OVER));
         // Load and set a translucent color state list as the background tint list
         final ColorStateList emeraldColor = ResourcesCompat.getColorStateList(
-                res, R.color.color_state_list_emerald_translucent, null);
+                mResources, R.color.color_state_list_emerald_translucent, null);
         onView(withId(viewId)).perform(
                 AppCompatTintableViewActions.setBackgroundTintList(emeraldColor));
 
         // Set background on our view
         onView(withId(viewId)).perform(AppCompatTintableViewActions.setBackgroundDrawable(
-                ResourcesCompat.getDrawable(res, R.drawable.test_background_green, null)));
+                ResourcesCompat.getDrawable(mResources, R.drawable.test_background_green, null)));
 
         // From this point on in this method we're allowing a margin of error in checking the
         // color of the view background. This is due to both translucent colors being used
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java
index d2abe95..8d6233d 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatImageViewTest.java
@@ -15,14 +15,6 @@
  */
 package android.support.v7.widget;
 
-import android.content.res.Resources;
-import android.support.v7.appcompat.test.R;
-import android.support.v7.testutils.AppCompatTextViewActions;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-
 /**
  * In addition to all tinting-related tests done by the base class, this class provides
  * tests specific to <code>AppCompatImageView</code> class.
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
index 6448e7d..e2c6edf 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatTextViewTest.java
@@ -15,13 +15,14 @@
  */
 package android.support.v7.widget;
 
-import android.content.res.Resources;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.AppCompatTextViewActions;
 import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Test;
 
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.junit.Assert.assertEquals;
 
 /**
  * In addition to all tinting-related tests done by the base class, this class provides
@@ -33,11 +34,11 @@
         super(AppCompatTextViewActivity.class);
     }
 
+    @Test
     @SmallTest
     public void testAllCaps() throws Throwable {
-        final Resources res = getActivity().getResources();
-        final String text1 = res.getString(R.string.sample_text1);
-        final String text2 = res.getString(R.string.sample_text2);
+        final String text1 = mResources.getString(R.string.sample_text1);
+        final String text2 = mResources.getString(R.string.sample_text2);
 
         final AppCompatTextView textView1 =
                 (AppCompatTextView) mContainer.findViewById(R.id.text_view_caps1);
diff --git a/v7/mediarouter/api/current.txt b/v7/mediarouter/api/current.txt
index d082642..75b0fce 100644
--- a/v7/mediarouter/api/current.txt
+++ b/v7/mediarouter/api/current.txt
@@ -565,6 +565,7 @@
     method public void onRouteRemoved(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
     method public void onRouteSelected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
     method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
+    method public void onRouteUnselected(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo, int);
     method public void onRouteVolumeChanged(android.support.v7.media.MediaRouter, android.support.v7.media.MediaRouter.RouteInfo);
   }
 
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index 8926e53..20d784f 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -78,12 +78,14 @@
 
     /**
      * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * when the reason the route was unselected is unknown.
+     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the reason the route
+     * was unselected is unknown.
      */
     public static final int UNSELECT_REASON_UNKNOWN = 0;
     /**
      * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * when the user pressed the disconnect button to disconnect and keep playing.
+     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
+     * the disconnect button to disconnect and keep playing.
      * <p>
      *
      * @see {@link MediaRouteDescriptor#canDisconnectAndKeepPlaying()}.
@@ -91,12 +93,14 @@
     public static final int UNSELECT_REASON_DISCONNECTED = 1;
     /**
      * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * when the user pressed the stop casting button.
+     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user pressed
+     * the stop casting button.
      */
     public static final int UNSELECT_REASON_STOPPED = 2;
     /**
      * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)}
-     * when the user selected a different route.
+     * and {@link Callback#onRouteUnselected(MediaRouter, RouteInfo, int)} when the user selected
+     * a different route.
      */
     public static final int UNSELECT_REASON_ROUTE_CHANGED = 3;
 
@@ -1712,6 +1716,8 @@
 
         /**
          * Called when the supplied media route becomes unselected as the active route.
+         * For detailed reason, override {@link #onRouteUnselected(MediaRouter, RouteInfo, int)}
+         * instead.
          *
          * @param router The media router reporting the event.
          * @param route The route that has been unselected.
@@ -1720,6 +1726,26 @@
         }
 
         /**
+         * Called when the supplied media route becomes unselected as the active route.
+         * The default implementation calls {@link #onRouteUnselected}.
+         * <p>
+         * The reason provided will be one of the following:
+         * <ul>
+         * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li>
+         * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li>
+         * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li>
+         * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li>
+         * </ul>
+         *
+         * @param router The media router reporting the event.
+         * @param route The route that has been unselected.
+         * @param reason The reason for unselecting the route.
+         */
+        public void onRouteUnselected(MediaRouter router, RouteInfo route, int reason) {
+            onRouteUnselected(router, route);
+        }
+
+        /**
          * Called when a media route has been added.
          *
          * @param router The media router reporting the event.
@@ -2444,7 +2470,8 @@
                         Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: "
                                 + unselectReason);
                     }
-                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute);
+                    mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute,
+                            unselectReason);
                     if (mSelectedRouteController != null) {
                         mSelectedRouteController.onUnselect(unselectReason);
                         mSelectedRouteController.onRelease();
@@ -2733,10 +2760,17 @@
                 obtainMessage(msg, obj).sendToTarget();
             }
 
+            public void post(int msg, Object obj, int arg) {
+                Message message = obtainMessage(msg, obj);
+                message.arg1 = arg;
+                message.sendToTarget();
+            }
+
             @Override
             public void handleMessage(Message msg) {
                 final int what = msg.what;
                 final Object obj = msg.obj;
+                final int arg = msg.arg1;
 
                 // Synchronize state with the system media router.
                 syncWithSystemProvider(what, obj);
@@ -2756,7 +2790,7 @@
 
                     final int callbackCount = mTempCallbackRecords.size();
                     for (int i = 0; i < callbackCount; i++) {
-                        invokeCallback(mTempCallbackRecords.get(i), what, obj);
+                        invokeCallback(mTempCallbackRecords.get(i), what, obj, arg);
                     }
                 } finally {
                     mTempCallbackRecords.clear();
@@ -2780,7 +2814,7 @@
                 }
             }
 
-            private void invokeCallback(CallbackRecord record, int what, Object obj) {
+            private void invokeCallback(CallbackRecord record, int what, Object obj, int arg) {
                 final MediaRouter router = record.mRouter;
                 final MediaRouter.Callback callback = record.mCallback;
                 switch (what & MSG_TYPE_MASK) {
@@ -2809,7 +2843,7 @@
                                 callback.onRouteSelected(router, route);
                                 break;
                             case MSG_ROUTE_UNSELECTED:
-                                callback.onRouteUnselected(router, route);
+                                callback.onRouteUnselected(router, route, arg);
                                 break;
                         }
                         break;