Merge "Fix preference dialog double-creation" into mnc-ub-dev
diff --git a/build.gradle b/build.gradle
index 8e89705..45d9901 100644
--- a/build.gradle
+++ b/build.gradle
@@ -118,7 +118,6 @@
 createSourceProp.dependsOn unzipRepo
 prepareRepo.dependsOn createSourceProp
 
-
 import com.google.common.hash.HashCode
 import com.google.common.hash.HashFunction
 import com.google.common.hash.Hashing
@@ -169,6 +168,23 @@
             project.android.buildToolsVersion = rootProject.buildToolsVersion
         }
     }
+
+    // Copy instrumentation test APK into the dist dir
+    project.afterEvaluate {
+        def assembleTestTask = project.tasks.findByPath('assembleAndroidTest')
+        if (assembleTestTask != null) {
+            assembleTestTask.doLast {
+                // If the project actually has some instrumentation tests, copy its APK
+                if (!project.android.sourceSets.androidTest.java.sourceFiles.isEmpty()) {
+                    def pkgTask = project.tasks.findByPath('packageDebugAndroidTest')
+                    copy {
+                        from(pkgTask.outputFile)
+                        into(rootProject.ext.testApkDistOut)
+                    }
+                }
+            }
+        }
+    }
 }
 
 project.gradle.buildFinished { buildResult ->
diff --git a/customtabs/tests/NO_DOCS b/customtabs/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/customtabs/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java b/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java
index 9440282..1a72789 100644
--- a/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java
+++ b/customtabs/tests/src/android/support/customtabs/CustomTabsIntentTest.java
@@ -19,10 +19,12 @@
 import android.content.Intent;
 import android.graphics.Color;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
 /**
  * Tests for CustomTabsIntent.
  */
+@SmallTest
 public class CustomTabsIntentTest extends AndroidTestCase {
 
     public void testBareboneCustomTabIntent() {
diff --git a/design/Android.mk b/design/Android.mk
index 6f4ac3b..98a1f22 100644
--- a/design/Android.mk
+++ b/design/Android.mk
@@ -28,7 +28,8 @@
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
     --extra-packages android.support.v7.appcompat \
-    --extra-packages android.support.v7.recyclerview
+    --extra-packages android.support.v7.recyclerview \
+    --no-version-vectors
 LOCAL_JAR_EXCLUDE_FILES := none
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/design/build.gradle b/design/build.gradle
index 401ec8e..eca89a6 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -6,11 +6,20 @@
     compile project(':support-v4')
     compile project(':support-appcompat-v7')
     compile project(':support-recyclerview-v7')
+
+    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
+    testCompile 'junit:junit:4.12'
 }
 
 android {
     compileSdkVersion 'current'
 
+    defaultConfig {
+        minSdkVersion 7
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
     sourceSets {
         main.manifest.srcFile 'AndroidManifest.xml'
         main.java.srcDirs = ['base', 'eclair-mr1', 'honeycomb', 'honeycomb-mr1', 'ics', 'lollipop', 'src']
@@ -18,11 +27,10 @@
         main.assets.srcDir 'assets'
         main.resources.srcDir 'src'
 
-        // this moves src/instrumentTest to tests so all folders follow:
-        // tests/java, tests/res, tests/assets, ...
-        // This is a *reset* so it replaces the default paths
         androidTest.setRoot('tests')
         androidTest.java.srcDir 'tests/src'
+        androidTest.res.srcDir 'tests/res'
+        androidTest.manifest.srcFile 'tests/AndroidManifest.xml'
     }
 
     compileOptions {
@@ -38,6 +46,10 @@
     buildTypes.all {
         consumerProguardFiles 'proguard-rules.pro'
     }
+
+    aaptOptions {
+        additionalParameters "--no-version-vectors"
+    }
 }
 
 android.libraryVariants.all { variant ->
diff --git a/design/tests/AndroidManifest.xml b/design/tests/AndroidManifest.xml
new file mode 100755
index 0000000..c871796
--- /dev/null
+++ b/design/tests/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2015 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="android.support.design.test">
+
+    <uses-sdk
+            android:minSdkVersion="7"
+            tools:overrideLibrary="android.support.test, android.support.test.espresso,
+                    android.support.test.espresso.idling"/>
+
+    <application android:theme="@style/Theme.AppCompat">
+        <uses-library android:name="android.test.runner"/>
+
+        <activity
+                android:theme="@style/Theme.AppCompat.NoActionBar"
+                android:name="android.support.design.widget.TabLayoutWithViewPagerActivity"/>
+
+    </application>
+
+    <instrumentation
+            android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="android.support.design.test"/>
+
+</manifest>
diff --git a/design/tests/res/layout/design_tabs_viewpager.xml b/design/tests/res/layout/design_tabs_viewpager.xml
new file mode 100644
index 0000000..7e9728a
--- /dev/null
+++ b/design/tests/res/layout/design_tabs_viewpager.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_height="?attr/actionBarSize"
+            android:layout_width="match_parent"
+            android:background="?attr/colorPrimary"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            app:contentInsetStart="72dp"
+            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
+
+    <android.support.design.widget.TabLayout
+            android:id="@+id/tabs"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:background="?attr/colorPrimary"
+            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+            app:tabMode="scrollable"
+            app:tabContentStart="72dp"/>
+
+    <android.support.v4.view.ViewPager
+            android:id="@+id/tabs_viewpager"
+            android:layout_height="0px"
+            android:layout_width="match_parent"
+            android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/design/tests/res/values/ids.xml b/design/tests/res/values/ids.xml
new file mode 100644
index 0000000..e5fcf63
--- /dev/null
+++ b/design/tests/res/values/ids.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     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>
+    <item name="page_0" type="id"/>
+    <item name="page_1" type="id"/>
+    <item name="page_2" type="id"/>
+    <item name="page_3" type="id"/>
+    <item name="page_4" type="id"/>
+    <item name="page_5" type="id"/>
+    <item name="page_6" type="id"/>
+    <item name="page_7" type="id"/>
+    <item name="page_8" type="id"/>
+    <item name="page_9" type="id"/>
+</resources>
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/TabLayoutActions.java b/design/tests/src/android/support/design/testutils/TabLayoutActions.java
new file mode 100644
index 0000000..fb3809e
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/TabLayoutActions.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 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.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+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.PagerTitleStrip;
+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.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+
+public class TabLayoutActions {
+    /**
+     * Wires <code>TabLayout</code> to <code>ViewPager</code> content.
+     */
+    public static ViewAction setupWithViewPager(final @Nullable ViewPager viewPager) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Setup with ViewPager content";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TabLayout tabLayout = (TabLayout) view;
+                tabLayout.setupWithViewPager(viewPager);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Selects the specified tab in the <code>TabLayout</code>.
+     */
+    public static ViewAction selectTab(final int tabIndex) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "Setup with ViewPager content";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                TabLayout tabLayout = (TabLayout) view;
+                tabLayout.getTabAt(tabIndex).select();
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/design/tests/src/android/support/design/testutils/ViewPagerActions.java b/design/tests/src/android/support/design/testutils/ViewPagerActions.java
new file mode 100644
index 0000000..7ab1560
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/ViewPagerActions.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 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.annotation.Nullable;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+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.PagerTitleStrip;
+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.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+
+public class ViewPagerActions {
+    /**
+     * Moves <code>ViewPager</code> to the right by one page.
+     */
+    public static ViewAction scrollRight() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll one page to the right";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int current = viewPager.getCurrentItem();
+                viewPager.setCurrentItem(current + 1, false);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to the left by one page.
+     */
+    public static ViewAction scrollLeft() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll one page to the left";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int current = viewPager.getCurrentItem();
+                viewPager.setCurrentItem(current - 1, false);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to the last page.
+     */
+    public static ViewAction scrollToLast() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll to last page";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int size = viewPager.getAdapter().getCount();
+                if (size > 0) {
+                    viewPager.setCurrentItem(size - 1, false);
+                }
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to the first page.
+     */
+    public static ViewAction scrollToFirst() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager scroll to first page";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                int size = viewPager.getAdapter().getCount();
+                if (size > 0) {
+                    viewPager.setCurrentItem(0, false);
+                }
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to specific page.
+     */
+    public static ViewAction scrollToPage(final int page) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isDisplayingAtLeast(90);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager move one page to the right";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                viewPager.setCurrentItem(page, false);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Sets the specified adapter on <code>ViewPager</code>.
+     */
+    public static ViewAction setAdapter(final @Nullable PagerAdapter adapter) {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(ViewPager.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager set adapter";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                viewPager.setAdapter(adapter);
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+
+    /**
+     * Moves <code>ViewPager</code> to specific page.
+     */
+    public static ViewAction notifyAdapterContentChange() {
+        return new ViewAction() {
+            @Override
+            public Matcher<View> getConstraints() {
+                return isAssignableFrom(ViewPager.class);
+            }
+
+            @Override
+            public String getDescription() {
+                return "ViewPager notify on adapter content change";
+            }
+
+            @Override
+            public void perform(UiController uiController, View view) {
+                uiController.loopMainThreadUntilIdle();
+
+                ViewPager viewPager = (ViewPager) view;
+                viewPager.getAdapter().notifyDataSetChanged();
+
+                uiController.loopMainThreadUntilIdle();
+            }
+        };
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java b/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
new file mode 100644
index 0000000..bda213c
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/BaseInstrumentationTestCase.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 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.app.Activity;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.ActivityInstrumentationTestCase2;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public abstract class BaseInstrumentationTestCase<A extends Activity>
+        extends ActivityInstrumentationTestCase2<A> {
+
+    protected BaseInstrumentationTestCase(Class<A> activityClass) {
+        super(activityClass);
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+        getActivity();
+    }
+
+}
diff --git a/design/tests/src/android/support/design/widget/BaseTestActivity.java b/design/tests/src/android/support/design/widget/BaseTestActivity.java
new file mode 100755
index 0000000..164a191
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/BaseTestActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.WindowManager;
+
+abstract class BaseTestActivity extends AppCompatActivity {
+
+    private boolean mDestroyed;
+
+    @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() {}
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mDestroyed = true;
+    }
+
+    @Override
+    public boolean isDestroyed() {
+        return mDestroyed;
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
new file mode 100644
index 0000000..6fe99a5
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerActivity.java
@@ -0,0 +1,18 @@
+package android.support.design.widget;
+
+import android.support.design.test.R;
+import android.support.v7.widget.Toolbar;
+
+public class TabLayoutWithViewPagerActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.design_tabs_viewpager;
+    }
+
+    @Override
+    protected void onContentViewSet() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
new file mode 100755
index 0000000..4df12b7
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/TabLayoutWithViewPagerTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2015 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.graphics.Color;
+import android.support.design.testutils.TabLayoutActions;
+import android.support.design.testutils.ViewPagerActions;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.design.test.R;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import org.junit.Test;
+
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import java.util.ArrayList;
+
+import static android.support.test.espresso.Espresso.onView;
+
+public class TabLayoutWithViewPagerTest
+        extends BaseInstrumentationTestCase<TabLayoutWithViewPagerActivity> {
+    private TabLayout mTabLayout;
+
+    private ViewPager mViewPager;
+
+    private ColorPagerAdapter mDefaultPagerAdapter;
+
+    protected static class BasePagerAdapter<Q> extends PagerAdapter {
+        protected ArrayList<Pair<String, Q>> mEntries = new ArrayList<>();
+
+        public void add(String title, Q content) {
+            mEntries.add(new Pair(title, content));
+        }
+
+        @Override
+        public int getCount() {
+            return mEntries.size();
+        }
+
+        protected void configureInstantiatedItem(View view, int position) {
+            switch (position) {
+                case 0:
+                    view.setId(R.id.page_0);
+                    break;
+                case 1:
+                    view.setId(R.id.page_1);
+                    break;
+                case 2:
+                    view.setId(R.id.page_2);
+                    break;
+                case 3:
+                    view.setId(R.id.page_3);
+                    break;
+                case 4:
+                    view.setId(R.id.page_4);
+                    break;
+                case 5:
+                    view.setId(R.id.page_5);
+                    break;
+                case 6:
+                    view.setId(R.id.page_6);
+                    break;
+                case 7:
+                    view.setId(R.id.page_7);
+                    break;
+                case 8:
+                    view.setId(R.id.page_8);
+                    break;
+                case 9:
+                    view.setId(R.id.page_9);
+                    break;
+            }
+        }
+
+        @Override
+        public void destroyItem(ViewGroup container, int position, Object object) {
+            // The adapter is also responsible for removing the view.
+            container.removeView(((ViewHolder) object).view);
+        }
+
+        @Override
+        public int getItemPosition(Object object) {
+            return ((ViewHolder) object).position;
+        }
+
+        @Override
+        public boolean isViewFromObject(View view, Object object) {
+            return ((ViewHolder) object).view == view;
+        }
+
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return mEntries.get(position).first;
+        }
+
+        protected static class ViewHolder {
+            final View view;
+            final int position;
+
+            public ViewHolder(View view, int position) {
+                this.view = view;
+                this.position = position;
+            }
+        }
+    }
+
+    protected static class ColorPagerAdapter extends BasePagerAdapter<Integer> {
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            final View view = new View(container.getContext());
+            view.setBackgroundColor(mEntries.get(position).second);
+            configureInstantiatedItem(view, position);
+
+            // Unlike ListView adapters, the ViewPager adapter is responsible
+            // for adding the view to the container.
+            container.addView(view);
+
+            return new ViewHolder(view, position);
+        }
+    }
+
+    protected static class TextPagerAdapter extends BasePagerAdapter<String> {
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            final TextView view = new TextView(container.getContext());
+            view.setText(mEntries.get(position).second);
+            configureInstantiatedItem(view, position);
+
+            // Unlike ListView adapters, the ViewPager adapter is responsible
+            // for adding the view to the container.
+            container.addView(view);
+
+            return new ViewHolder(view, position);
+        }
+    }
+
+    public TabLayoutWithViewPagerTest() {
+        super(TabLayoutWithViewPagerActivity.class);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+
+        final TabLayoutWithViewPagerActivity activity = getActivity();
+        mTabLayout = (TabLayout) activity.findViewById(R.id.tabs);
+        mViewPager = (ViewPager) activity.findViewById(R.id.tabs_viewpager);
+
+        mDefaultPagerAdapter = new ColorPagerAdapter();
+        mDefaultPagerAdapter.add("Red", Color.RED);
+        mDefaultPagerAdapter.add("Green", Color.GREEN);
+        mDefaultPagerAdapter.add("Blue", Color.BLUE);
+
+        // Configure view pager
+        onView(withId(R.id.tabs_viewpager)).perform(
+                ViewPagerActions.setAdapter(mDefaultPagerAdapter),
+                ViewPagerActions.scrollToPage(0));
+
+        // And wire the tab layout to it
+        onView(withId(R.id.tabs)).perform(TabLayoutActions.setupWithViewPager(mViewPager));
+    }
+
+    /**
+     * Verifies that selecting pages in <code>ViewPager</code> also updates the tab selection
+     * in the wired <code>TabLayout</code>
+     */
+    private void verifyViewPagerSelection() {
+        int itemCount = mViewPager.getAdapter().getCount();
+
+        onView(withId(R.id.tabs_viewpager)).perform(ViewPagerActions.scrollToPage(0));
+        assertEquals("Selected page", 0, mViewPager.getCurrentItem());
+        assertEquals("Selected tab", 0, mTabLayout.getSelectedTabPosition());
+
+        // Scroll tabs to the right
+        for (int i = 0; i < (itemCount - 1); i++) {
+            // Scroll one tab to the right
+            onView(withId(R.id.tabs_viewpager)).perform(ViewPagerActions.scrollRight());
+            final int expectedCurrentTabIndex = i + 1;
+            assertEquals("Scroll right #" + i, expectedCurrentTabIndex,
+                    mViewPager.getCurrentItem());
+            assertEquals("Selected tab after scrolling right #" + i, expectedCurrentTabIndex,
+                    mTabLayout.getSelectedTabPosition());
+        }
+
+        // Scroll tabs to the left
+        for (int i = 0; i < (itemCount - 1); i++) {
+            // Scroll one tab to the left
+            onView(withId(R.id.tabs_viewpager)).perform(ViewPagerActions.scrollLeft());
+            final int expectedCurrentTabIndex = itemCount - i - 2;
+            assertEquals("Scroll left #" + i, expectedCurrentTabIndex, mViewPager.getCurrentItem());
+            assertEquals("Selected tab after scrolling left #" + i, expectedCurrentTabIndex,
+                    mTabLayout.getSelectedTabPosition());
+        }
+    }
+
+    /**
+     * Verifies that selecting pages in <code>ViewPager</code> also updates the tab selection
+     * in the wired <code>TabLayout</code>
+     */
+    private void verifyTabLayoutSelection() {
+        int itemCount = mTabLayout.getTabCount();
+
+        onView(withId(R.id.tabs_viewpager)).perform(ViewPagerActions.scrollToPage(0));
+        assertEquals("Selected tab", 0, mTabLayout.getSelectedTabPosition());
+        assertEquals("Selected page", 0, mViewPager.getCurrentItem());
+
+        // Select tabs "going" to the right. Note that the first loop iteration tests the
+        // scenario of "selecting" the first tab when it's already selected.
+        for (int i = 0; i < itemCount; i++) {
+            onView(withId(R.id.tabs)).perform(TabLayoutActions.selectTab(i));
+            assertEquals("Selected tab after selecting #" + i, i,
+                    mTabLayout.getSelectedTabPosition());
+            assertEquals("Select tab #" + i, i, mViewPager.getCurrentItem());
+        }
+
+        // Select tabs "going" to the left. Note that the first loop iteration tests the
+        // scenario of "selecting" the last tab when it's already selected.
+        for (int i = itemCount - 1; i >= 0; i--) {
+            onView(withId(R.id.tabs)).perform(TabLayoutActions.selectTab(i));
+            assertEquals("Scroll left #" + i, i, mViewPager.getCurrentItem());
+            assertEquals("Selected tab after scrolling left #" + i, i,
+                    mTabLayout.getSelectedTabPosition());
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBasics() {
+        final int itemCount = mViewPager.getAdapter().getCount();
+
+        assertEquals("Matching item count", itemCount, mTabLayout.getTabCount());
+
+        for (int i = 0; i < itemCount; i++) {
+            assertEquals("Tab #" +i, mViewPager.getAdapter().getPageTitle(i),
+                    mTabLayout.getTabAt(i).getText());
+        }
+
+        assertEquals("Selected tab", mViewPager.getCurrentItem(),
+                mTabLayout.getSelectedTabPosition());
+
+        verifyViewPagerSelection();
+    }
+
+    @Test
+    @SmallTest
+    public void testInteraction() {
+        assertEquals("Default selected page", 0, mViewPager.getCurrentItem());
+        assertEquals("Default selected tab", 0, mTabLayout.getSelectedTabPosition());
+
+        verifyTabLayoutSelection();
+    }
+
+    @Test
+    @SmallTest
+    public void testAdapterContentChange() {
+        // Verify that we have the expected initial adapter
+        PagerAdapter initialAdapter = mViewPager.getAdapter();
+        assertEquals("Initial adapter class", ColorPagerAdapter.class, initialAdapter.getClass());
+        assertEquals("Initial adapter page count", 3, initialAdapter.getCount());
+
+        // Add two more entries to our adapter
+        final int newItemCount = 5;
+        mDefaultPagerAdapter.add("Yellow", Color.YELLOW);
+        mDefaultPagerAdapter.add("Magenta", Color.MAGENTA);
+        onView(withId(R.id.tabs_viewpager)).perform(ViewPagerActions.notifyAdapterContentChange());
+
+        // We have more comprehensive test coverage for changing the ViewPager adapter in v4/tests.
+        // Here we are focused on testing the continuous integration of TabLayout with the new
+        // content of ViewPager
+
+        assertEquals("Matching item count", newItemCount, mTabLayout.getTabCount());
+
+        for (int i = 0; i < newItemCount; i++) {
+            assertEquals("Tab #" +i, mViewPager.getAdapter().getPageTitle(i),
+                    mTabLayout.getTabAt(i).getText());
+        }
+
+        verifyViewPagerSelection();
+        verifyTabLayoutSelection();
+    }
+
+    @Test
+    @SmallTest
+    public void testAdapterChange() {
+        // Verify that we have the expected initial adapter
+        PagerAdapter initialAdapter = mViewPager.getAdapter();
+        assertEquals("Initial adapter class", ColorPagerAdapter.class, initialAdapter.getClass());
+        assertEquals("Initial adapter page count", 3, initialAdapter.getCount());
+
+        // Create a new adapter
+        TextPagerAdapter newAdapter = new TextPagerAdapter();
+        final int newItemCount = 6;
+        for (int i = 0; i < newItemCount; i++) {
+            newAdapter.add("Title " + i, "Body " + i);
+        }
+        // And set it on the ViewPager
+        onView(withId(R.id.tabs_viewpager)).perform(ViewPagerActions.setAdapter(newAdapter),
+                ViewPagerActions.scrollToPage(0));
+
+        // As TabLayout doesn't track adapter changes, we need to re-wire the new adapter
+        onView(withId(R.id.tabs)).perform(TabLayoutActions.setupWithViewPager(mViewPager));
+
+        // We have more comprehensive test coverage for changing the ViewPager adapter in v4/tests.
+        // Here we are focused on testing the integration of TabLayout with the new
+        // content of ViewPager
+
+        assertEquals("Matching item count", newItemCount, mTabLayout.getTabCount());
+
+        for (int i = 0; i < newItemCount; i++) {
+            assertEquals("Tab #" +i, mViewPager.getAdapter().getPageTitle(i),
+                    mTabLayout.getTabAt(i).getText());
+        }
+
+        verifyViewPagerSelection();
+        verifyTabLayoutSelection();
+    }
+}
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index bc45e10..563ad30 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -30,9 +30,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, animated/src)
 
-LOCAL_JAVA_LIBRARIES := android-support-v4
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-vectordrawable
+LOCAL_JAVA_LIBRARIES := android-support-v4 android-support-vectordrawable
 
 LOCAL_AAPT_FLAGS := --no-version-vectors
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/graphics/drawable/animated/build.gradle b/graphics/drawable/animated/build.gradle
index c35a704..f56f612 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -75,17 +75,6 @@
     artifacts.add('archives', sourcesJarTask);
 }
 
-// TODO make this generic for all projects
-afterEvaluate {
-    def originalTask = tasks['packageDebugAndroidTest']
-    tasks['assembleDebugAndroidTest'].doLast {
-        copy {
-            from(originalTask.outputFile)
-            into(rootProject.ext.testApkDistOut)
-        }
-    }
-}
-
 uploadArchives {
     repositories {
         mavenDeployer {
@@ -118,4 +107,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/graphics/drawable/animated/tests/NO_DOCS b/graphics/drawable/animated/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/graphics/drawable/animated/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index 235e5b7..bf271b4 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -75,17 +75,6 @@
     artifacts.add('archives', sourcesJarTask);
 }
 
-// TODO make this generic for all projects
-afterEvaluate {
-    def originalTask = tasks['packageDebugAndroidTest']
-    tasks['assembleDebugAndroidTest'].doLast {
-        copy {
-            from(originalTask.outputFile)
-            into(rootProject.ext.testApkDistOut)
-        }
-    }
-}
-
 uploadArchives {
     repositories {
         mavenDeployer {
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index 9652e39..064d040 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -14,6 +14,9 @@
 
 package android.support.graphics.drawable;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.annotation.TargetApi;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -44,9 +47,6 @@
 import android.util.Log;
 import android.util.Xml;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Stack;
@@ -533,10 +533,7 @@
             if (type != XmlPullParser.START_TAG) {
                 throw new XmlPullParserException("No start tag found");
             }
-
-            final VectorDrawableCompat drawable = new VectorDrawableCompat();
-            drawable.inflate(res, parser, attrs, theme);
-            return drawable;
+            return createFromXmlInner(res, parser, attrs, theme);
         } catch (XmlPullParserException e) {
             Log.e(LOGTAG, "parser error", e);
         } catch (IOException e) {
@@ -545,6 +542,19 @@
         return null;
     }
 
+    /**
+     * Create a VectorDrawableCompat from inside an XML document using an optional
+     * {@link Theme}. Called on a parser positioned at a tag in an XML
+     * document, tries to create a Drawable from that tag. Returns {@code null}
+     * if the tag is not a valid drawable.
+     */
+    public static VectorDrawableCompat createFromXmlInner(Resources r, XmlPullParser parser,
+            AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
+        final VectorDrawableCompat drawable = new VectorDrawableCompat();
+        drawable.inflate(r, parser, attrs, theme);
+        return drawable;
+    }
+
     private static int applyAlpha(int color, float alpha) {
         int alphaBytes = Color.alpha(color);
         color &= 0x00FFFFFF;
diff --git a/graphics/drawable/static/tests/NO_DOCS b/graphics/drawable/static/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/graphics/drawable/static/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java b/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
index 2dab79a..4163eb2 100644
--- a/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
+++ b/graphics/drawable/static/tests/src/android/support/graphics/drawable/tests/VectorDrawableTest.java
@@ -26,6 +26,7 @@
 import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.graphics.drawable.test.R;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -33,6 +34,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 
+@MediumTest
 public class VectorDrawableTest extends AndroidTestCase {
     private static final String LOGTAG = "VectorDrawableTest";
 
diff --git a/v17/leanback/res/values-fr/strings.xml b/v17/leanback/res/values-fr/strings.xml
index 567a7d7..9d4d9f7 100644
--- a/v17/leanback/res/values-fr/strings.xml
+++ b/v17/leanback/res/values-fr/strings.xml
@@ -33,10 +33,10 @@
     <string name="lb_playback_controls_skip_next" msgid="2946499493161095772">"Ignorer l\'élément suivant"</string>
     <string name="lb_playback_controls_skip_previous" msgid="2326801832933178348">"Ignorer l\'élément précédent"</string>
     <string name="lb_playback_controls_more_actions" msgid="2330770008796987655">"Autres actions"</string>
-    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Désélectionner \"J\'aime\""</string>
-    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Sélectionner \"J\'aime\""</string>
-    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Désélectionner \"Je n\'aime pas\""</string>
-    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Sélectionner \"Je n\'aime pas\""</string>
+    <string name="lb_playback_controls_thumb_up" msgid="6530420347129222601">"Annuler +1"</string>
+    <string name="lb_playback_controls_thumb_up_outline" msgid="1577637924003500946">"Sélectionner +1"</string>
+    <string name="lb_playback_controls_thumb_down" msgid="4498041193172964797">"Annuler -1"</string>
+    <string name="lb_playback_controls_thumb_down_outline" msgid="2936020280629424365">"Sélectionner -1"</string>
     <string name="lb_playback_controls_repeat_none" msgid="87476947476529036">"Ne rien lire en boucle"</string>
     <string name="lb_playback_controls_repeat_all" msgid="6730354406289599000">"Tout lire en boucle"</string>
     <string name="lb_playback_controls_repeat_one" msgid="3285202316452203619">"Lire en boucle un élément"</string>
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
index dccde67..e0fad0a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionAdapter.java
@@ -381,14 +381,6 @@
 
         private boolean mKeyPressed = false;
 
-        private void playSound(View v, int soundEffect) {
-            if (v.isSoundEffectsEnabled()) {
-                Context ctx = v.getContext();
-                AudioManager manager = (AudioManager)ctx.getSystemService(Context.AUDIO_SERVICE);
-                manager.playSoundEffect(soundEffect);
-            }
-        }
-
         /**
          * Now only handles KEYCODE_ENTER and KEYCODE_NUMPAD_ENTER key event.
          */
@@ -424,7 +416,6 @@
                             }
                             if (!mKeyPressed) {
                                 mKeyPressed = true;
-                                playSound(v, AudioManager.FX_KEY_CLICK);
                                 mStylist.onAnimateItemPressed(avh, mKeyPressed);
                             }
                             break;
diff --git a/v17/tests/NO_DOCS b/v17/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v17/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v4/build.gradle b/v4/build.gradle
index dce3a15..5abdbb9 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -150,17 +150,6 @@
     artifacts.add('archives', sourcesJarTask);
 }
 
-// TODO make this generic for all projects
-afterEvaluate {
-    def originalTask = tasks['packageDebugAndroidTest']
-    tasks['assembleDebugAndroidTest'].doLast {
-        copy {
-            from(originalTask.outputFile)
-            into(rootProject.ext.testApkDistOut)
-        }
-    }
-}
-
 uploadArchives {
     repositories {
         mavenDeployer {
diff --git a/v4/java/android/support/v4/media/MediaBrowserCompat.java b/v4/java/android/support/v4/media/MediaBrowserCompat.java
index dcce7f5..f60d596 100644
--- a/v4/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/v4/java/android/support/v4/media/MediaBrowserCompat.java
@@ -200,6 +200,9 @@
         mImpl.getItem(mediaId, cb);
     }
 
+    /**
+     * A class with information on a single media item for use in browsing media.
+     */
     public static class MediaItem implements Parcelable {
         private final int mFlags;
         private final MediaDescriptionCompat mDescription;
diff --git a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
index 205e789..90a361e 100644
--- a/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/v4/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -794,7 +794,7 @@
         }
 
         /**
-         * Gets any extras about the brwoser service.
+         * Gets any extras about the browser service.
          */
         public Bundle getExtras() {
             return mExtras;
diff --git a/v4/tests/java/android/support/v4/app/FragmentTransitionTest.java b/v4/tests/java/android/support/v4/app/FragmentTransitionTest.java
index 2c58ad5..27f277b 100644
--- a/v4/tests/java/android/support/v4/app/FragmentTransitionTest.java
+++ b/v4/tests/java/android/support/v4/app/FragmentTransitionTest.java
@@ -22,8 +22,10 @@
 import android.support.v4.test.R;
 import android.support.v4.view.ViewCompat;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 
+@MediumTest
 public class FragmentTransitionTest extends
         ActivityInstrumentationTestCase2<FragmentTestActivity> {
     private TestFragment mStartFragment;
diff --git a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
index 6296903..b25ed4f 100644
--- a/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
+++ b/v4/tests/java/android/support/v4/graphics/ColorUtilsTest.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Color;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import java.lang.Integer;
 import java.util.ArrayList;
@@ -25,6 +26,7 @@
 /**
  * @hide
  */
+@SmallTest
 public class ColorUtilsTest extends AndroidTestCase {
 
     // 0.5% of the max value
diff --git a/v4/tests/java/android/support/v4/text/BidiFormatterTest.java b/v4/tests/java/android/support/v4/text/BidiFormatterTest.java
index ab32dc5..1d8ef7a 100644
--- a/v4/tests/java/android/support/v4/text/BidiFormatterTest.java
+++ b/v4/tests/java/android/support/v4/text/BidiFormatterTest.java
@@ -17,10 +17,12 @@
 package android.support.v4.text;
 
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import java.util.Locale;
 
 /** @hide */
+@SmallTest
 public class BidiFormatterTest extends AndroidTestCase {
 
     private static final BidiFormatter LTR_FMT = BidiFormatter.getInstance(false /* LTR context */);
diff --git a/v4/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java b/v4/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
index fc571a8..2b83fc2 100644
--- a/v4/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
+++ b/v4/tests/java/android/support/v4/widget/ScrollerCompatTestBase.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -29,6 +30,7 @@
 /**
  * @hide
  */
+@MediumTest
 abstract public class ScrollerCompatTestBase extends AndroidTestCase {
 
     private static final boolean DEBUG = false;
diff --git a/v7/appcompat/Android.mk b/v7/appcompat/Android.mk
index baa32eb..0eb6a7b 100644
--- a/v7/appcompat/Android.mk
+++ b/v7/appcompat/Android.mk
@@ -23,7 +23,10 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-vectordrawable
+LOCAL_JAVA_LIBRARIES := android-support-v4
+LOCAL_AAPT_FLAGS += --auto-add-overlay \
+        --no-version-vectors
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # API Check
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index ff8c7d8..1bfb318 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -500,6 +500,7 @@
     field public static int spinnerDropDownItemStyle;
     field public static int spinnerStyle;
     field public static int splitTrack;
+    field public static int srcCompat;
     field public static int state_above_anchor;
     field public static int submitBackground;
     field public static int subtitle;
@@ -719,13 +720,13 @@
     field public static int abc_action_bar_item_background_material;
     field public static int abc_btn_borderless_material;
     field public static int abc_btn_check_material;
-    field public static int abc_btn_check_to_on_mtrl_000;
-    field public static int abc_btn_check_to_on_mtrl_015;
+    field public static int abc_btn_checkbox_checked_mtrl;
+    field public static int abc_btn_checkbox_unchecked_mtrl;
     field public static int abc_btn_colored_material;
     field public static int abc_btn_default_mtrl_shape;
     field public static int abc_btn_radio_material;
-    field public static int abc_btn_radio_to_on_mtrl_000;
-    field public static int abc_btn_radio_to_on_mtrl_015;
+    field public static int abc_btn_radio_off_mtrl;
+    field public static int abc_btn_radio_on_mtrl;
     field public static int abc_btn_rating_star_off_mtrl_alpha;
     field public static int abc_btn_rating_star_on_mtrl_alpha;
     field public static int abc_btn_switch_to_on_mtrl_00001;
@@ -737,22 +738,22 @@
     field public static int abc_dialog_material_background_dark;
     field public static int abc_dialog_material_background_light;
     field public static int abc_edit_text_material;
-    field public static int abc_ic_ab_back_mtrl_am_alpha;
-    field public static int abc_ic_clear_mtrl_alpha;
+    field public static int abc_ic_ab_back_material;
+    field public static int abc_ic_clear_material;
     field public static int abc_ic_commit_search_api_mtrl_alpha;
-    field public static int abc_ic_go_search_api_mtrl_alpha;
-    field public static int abc_ic_menu_copy_mtrl_am_alpha;
-    field public static int abc_ic_menu_cut_mtrl_alpha;
-    field public static int abc_ic_menu_moreoverflow_mtrl_alpha;
-    field public static int abc_ic_menu_paste_mtrl_am_alpha;
-    field public static int abc_ic_menu_selectall_mtrl_alpha;
-    field public static int abc_ic_menu_share_mtrl_alpha;
+    field public static int abc_ic_go_search_api_material;
+    field public static int abc_ic_menu_copy_material;
+    field public static int abc_ic_menu_cut_material;
+    field public static int abc_ic_menu_overflow_material;
+    field public static int abc_ic_menu_paste_material;
+    field public static int abc_ic_menu_selectall_material;
+    field public static int abc_ic_menu_share_material;
     field public static int abc_ic_search_api_mtrl_alpha;
     field public static int abc_ic_star_black_16dp;
     field public static int abc_ic_star_black_36dp;
     field public static int abc_ic_star_half_black_16dp;
     field public static int abc_ic_star_half_black_36dp;
-    field public static int abc_ic_voice_search_api_mtrl_alpha;
+    field public static int abc_ic_voice_search_api_material;
     field public static int abc_item_background_holo_dark;
     field public static int abc_item_background_holo_light;
     field public static int abc_list_divider_mtrl_alpha;
@@ -1338,6 +1339,9 @@
     field public static int AlertDialog_listLayout;
     field public static int AlertDialog_multiChoiceItemLayout;
     field public static int AlertDialog_singleChoiceItemLayout;
+    field public static final int[] AppCompatImageView;
+    field public static int AppCompatImageView_android_src;
+    field public static int AppCompatImageView_srcCompat;
     field public static final int[] AppCompatTextView;
     field public static int AppCompatTextView_android_textAppearance;
     field public static int AppCompatTextView_textAllCaps;
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 19377ea..fe55fee 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -4,6 +4,7 @@
 
 dependencies {
     compile project(':support-v4')
+    compile project(':support-vector-drawable')
     androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
     androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
     testCompile 'junit:junit:4.12'
@@ -35,6 +36,10 @@
         targetCompatibility JavaVersion.VERSION_1_7
     }
 
+    aaptOptions {
+        additionalParameters "--no-version-vectors"
+    }
+
     lintOptions {
         // TODO: fix errors and reenable.
         abortOnError false
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index 8454ba8..fdb7ff5 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -147,6 +147,7 @@
      <public type="attr" name="ratingBarStyle"/>
      <public type="attr" name="ratingBarStyleIndicator"/>
      <public type="attr" name="ratingBarStyleSmall"/>
+     <public type="attr" name="srcCompat"/>
      <public type="attr" name="searchHintIcon"/>
      <public type="attr" name="searchIcon"/>
      <public type="attr" name="searchViewStyle"/>
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 9911008..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 69ff9dd..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 9218981..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index a588576..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index 46ccacc..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png
deleted file mode 100644
index ce64334..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png
deleted file mode 100644
index f39c153..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 706fc1f..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index e78bcaf..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
deleted file mode 100644
index 00e189b..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 8610c50..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index e631df7..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index cd1f57c..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png
deleted file mode 100644
index a6dbc2a..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index 57211b5..0000000
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index a262b0c..0000000
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index d8eaf07..0000000
--- a/v7/appcompat/res/drawable-ldrtl-hdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index b872414..0000000
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 254f806..0000000
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index efe4446..0000000
--- a/v7/appcompat/res/drawable-ldrtl-mdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index ffa1654..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 88e34c4..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 87bf8d3..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index 33b4f0f..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index fb54215..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 3cdb6cf..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index a91425a..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index fb91811..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 90fe333..0000000
--- a/v7/appcompat/res/drawable-ldrtl-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 7a9fcbc..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 3b052e5..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 96a8693..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index 6e18d40..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index 90fbc56..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png
deleted file mode 100644
index dde307e..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png
deleted file mode 100644
index 0084c12..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 559b835..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 44a3768..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
deleted file mode 100644
index f95f7f7..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 1492ab6..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 7c011af..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index 36f664c..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png
deleted file mode 100644
index a216da1..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 4902520..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 59a683a..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 03bf49c..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index 342323b..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index 154babd..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png
deleted file mode 100644
index 6d90d0c..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png
deleted file mode 100644
index 19e000a..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 6448549..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index cd38901..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
deleted file mode 100644
index e76c83e..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 9aabc43..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 6a7161f..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index 6be7e09..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png
deleted file mode 100644
index 209898d..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png
deleted file mode 100644
index 7329c15..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png
deleted file mode 100644
index accf80e..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 8c82ec3..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 8fc0a9b..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index 3038d70..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index f99802f..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png
deleted file mode 100644
index b85e87f..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png
deleted file mode 100644
index d041623..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_go_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 90d6ba3..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 63e541f..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
deleted file mode 100644
index c382aa6..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index f71485c..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 162ab98..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index d95a377..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
deleted file mode 100644
index dacf407..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png
deleted file mode 100644
index d44bbae..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 4e18de2..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png
deleted file mode 100644
index 5fa3266..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png b/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png
deleted file mode 100644
index c11cb2e..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_btn_radio_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
deleted file mode 100644
index 41d6d6b..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_ab_back_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png
deleted file mode 100644
index da2b577..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_clear_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
deleted file mode 100644
index 715db8a..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_copy_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
deleted file mode 100644
index 397fd91..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_cut_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
deleted file mode 100644
index 16e9e14..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_moreoverflow_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
deleted file mode 100644
index 1891b3d..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_paste_mtrl_am_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
deleted file mode 100644
index 591a1c9..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_selectall_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
deleted file mode 100644
index ba16aac..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_menu_share_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png b/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
deleted file mode 100644
index d191642..0000000
--- a/v7/appcompat/res/drawable-xxxhdpi/abc_ic_voice_search_api_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable/abc_btn_check_material.xml b/v7/appcompat/res/drawable/abc_btn_check_material.xml
index 4934a92..f8e90a9 100644
--- a/v7/appcompat/res/drawable/abc_btn_check_material.xml
+++ b/v7/appcompat/res/drawable/abc_btn_check_material.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2015 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.
@@ -15,6 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:drawable="@drawable/abc_btn_check_to_on_mtrl_015" />
-    <item android:drawable="@drawable/abc_btn_check_to_on_mtrl_000" />
+    <item android:state_checked="true" android:drawable="@drawable/abc_btn_checkbox_checked_mtrl" />
+    <item android:drawable="@drawable/abc_btn_checkbox_unchecked_mtrl" />
 </selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_btn_checkbox_checked_mtrl.xml b/v7/appcompat/res/drawable/abc_btn_checkbox_checked_mtrl.xml
new file mode 100644
index 0000000..638339a
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_btn_checkbox_checked_mtrl.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:name="abc_btn_checkbox_checked"
+        android:width="32dp"
+        android:viewportWidth="48"
+        android:height="32dp"
+        android:viewportHeight="48">
+    <group
+            android:name="icon_null"
+            android:translateX="24"
+            android:translateY="24"
+            android:scaleX="0.2"
+            android:scaleY="0.2">
+        <group
+                android:name="check"
+                android:scaleX="7.5"
+                android:scaleY="7.5">
+            <path
+                    android:name="check_path_merged"
+                    android:pathData="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -5.0,-5.00001525879 -5.0,-5.00001525879 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 3.58590698242,3.58601379395 3.58590698242,3.58601379395 c 0.0,0.0 7.58590698242,-7.58601379395 7.58590698242,-7.58601379395 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -9.0,9.00001525879 -9.0,9.00001525879 Z"
+                    android:fillColor="#FF000000"/>
+        </group>
+        <group
+                android:name="box_dilate"
+                android:scaleX="7.5"
+                android:scaleY="7.5">
+            <path
+                    android:name="box_inner_merged"
+                    android:pathData="M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+                    android:fillColor="#FF000000"
+                    android:fillAlpha="0"/>
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_btn_checkbox_unchecked_mtrl.xml b/v7/appcompat/res/drawable/abc_btn_checkbox_unchecked_mtrl.xml
new file mode 100644
index 0000000..0301f1f
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_btn_checkbox_unchecked_mtrl.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:name="abc_btn_checkbox_unchecked"
+        android:width="32dp"
+        android:viewportWidth="48"
+        android:height="32dp"
+        android:viewportHeight="48">
+    <group
+            android:name="icon_null"
+            android:translateX="24"
+            android:translateY="24"
+            android:scaleX="0.2"
+            android:scaleY="0.2">
+        <group
+                android:name="check"
+                android:scaleX="7.5"
+                android:scaleY="7.5">
+            <path
+                    android:name="box_outer_merged"
+                    android:pathData="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
+                    android:fillColor="#FF000000"
+                    android:fillAlpha="0"/>
+        </group>
+        <group
+                android:name="box_dilate"
+                android:scaleX="7.5"
+                android:scaleY="7.5">
+            <path
+                    android:name="box_inner_merged"
+                    android:pathData="M -7.0,-7.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+                    android:fillColor="#FF000000"/>
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_btn_radio_material.xml b/v7/appcompat/res/drawable/abc_btn_radio_material.xml
index 6e9f9cf..ac4eead 100644
--- a/v7/appcompat/res/drawable/abc_btn_radio_material.xml
+++ b/v7/appcompat/res/drawable/abc_btn_radio_material.xml
@@ -15,6 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:drawable="@drawable/abc_btn_radio_to_on_mtrl_015" />
-    <item android:drawable="@drawable/abc_btn_radio_to_on_mtrl_000" />
+    <item android:state_checked="true" android:drawable="@drawable/abc_btn_radio_on_mtrl" />
+    <item android:drawable="@drawable/abc_btn_radio_off_mtrl" />
 </selector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_btn_radio_off_mtrl.xml b/v7/appcompat/res/drawable/abc_btn_radio_off_mtrl.xml
new file mode 100644
index 0000000..9c4f3b1
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_btn_radio_off_mtrl.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:name="btn_radio_to_on_mtrl"
+        android:width="32dp"
+        android:viewportWidth="32"
+        android:height="32dp"
+        android:viewportHeight="32">
+    <group
+            android:name="btn_radio_to_on_mtrl_0"
+            android:translateX="16"
+            android:translateY="16" >
+        <group
+                android:name="ring_outer" >
+            <path
+                    android:name="ring_outer_path"
+                    android:strokeColor="#FF000000"
+                    android:strokeWidth="2"
+                    android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
+        </group>
+        <group
+                android:name="dot_group"
+                android:scaleX="0"
+                android:scaleY="0" >
+            <path
+                    android:name="dot_path"
+                    android:pathData="M 0.0,-5.0 c -2.7619934082,0.0 -5.0,2.2380065918 -5.0,5.0 c 0.0,2.7619934082 2.2380065918,5.0 5.0,5.0 c 2.7619934082,0.0 5.0,-2.2380065918 5.0,-5.0 c 0.0,-2.7619934082 -2.2380065918,-5.0 -5.0,-5.0 Z"
+                    android:fillColor="#FF000000" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_btn_radio_on_mtrl.xml b/v7/appcompat/res/drawable/abc_btn_radio_on_mtrl.xml
new file mode 100644
index 0000000..ab56e58
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_btn_radio_on_mtrl.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:name="btn_radio_to_off_mtrl"
+        android:width="32dp"
+        android:viewportWidth="32"
+        android:height="32dp"
+        android:viewportHeight="32">
+    <group
+            android:name="btn_radio_to_off_mtrl_0"
+            android:translateX="16"
+            android:translateY="16" >
+        <group
+                android:name="ring_outer" >
+            <path
+                    android:name="ring_outer_path"
+                    android:strokeColor="#FF000000"
+                    android:strokeWidth="2"
+                    android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
+        </group>
+        <group
+                android:name="dot_group" >
+            <path
+                    android:name="dot_path"
+                    android:pathData="M 0.0,-5.0 c -2.7619934082,0.0 -5.0,2.2380065918 -5.0,5.0 c 0.0,2.7619934082 2.2380065918,5.0 5.0,5.0 c 2.7619934082,0.0 5.0,-2.2380065918 5.0,-5.0 c 0.0,-2.7619934082 -2.2380065918,-5.0 -5.0,-5.0 Z"
+                    android:fillColor="#FF000000" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_ic_ab_back_material.xml b/v7/appcompat/res/drawable/abc_ic_ab_back_material.xml
new file mode 100644
index 0000000..5a89523
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_ab_back_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:autoMirrored="true"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M20,11L7.8,11l5.6,-5.6L12,4l-8,8l8,8l1.4,-1.4L7.8,13L20,13L20,11z"
+            android:fillColor="@android:color/white"/>
+</vector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_ic_clear_material.xml b/v7/appcompat/res/drawable/abc_ic_clear_material.xml
new file mode 100644
index 0000000..e6d106b
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_clear_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M19,6.41L17.59,5,12,10.59,6.41,5,5,6.41,10.59,12,5,17.59,6.41,19,12,13.41,17.59,19,19,17.59,13.41,12z"
+            android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_go_search_api_material.xml b/v7/appcompat/res/drawable/abc_ic_go_search_api_material.xml
new file mode 100644
index 0000000..0c88119
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_go_search_api_material.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:pathData="M10,6l-1.4,1.4 4.599999,4.6 -4.599999,4.6 1.4,1.4 6,-6z"
+        android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_menu_copy_material.xml b/v7/appcompat/res/drawable/abc_ic_menu_copy_material.xml
new file mode 100644
index 0000000..6af0e0c
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_menu_copy_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:autoMirrored="true"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M16,1L4,1C2.9,1 2,1.9 2,3l0,14l2,0L4,3l12,0L16,1zM19,5L8,5C6.9,5 6,5.9 6,7l0,14c0,1.1 0.9,2 2,2l11,0c1.1,0 2,-0.9 2,-2L21,7C21,5.9 20.1,5 19,5zM19,21L8,21L8,7l11,0L19,21z"
+            android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_menu_cut_material.xml b/v7/appcompat/res/drawable/abc_ic_menu_cut_material.xml
new file mode 100644
index 0000000..22cb81d
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_menu_cut_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:autoMirrored="true"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M10,6c0,-2.2 -1.8,-4 -4,-4S2,3.8 2,6c0,2.2 1.8,4 4,4c0.6,0 1.1,-0.1 1.6,-0.4L10,12l-2.4,2.4C7.1,14.1 6.6,14 6,14c-2.2,0 -4,1.8 -4,4c0,2.2 1.8,4 4,4s4,-1.8 4,-4c0,-0.6 -0.1,-1.1 -0.4,-1.6L12,14l7,7l4,0L9.6,7.6C9.9,7.1 10,6.6 10,6zM6,8C4.9,8 4,7.1 4,6s0.9,-2 2,-2c1.1,0 2,0.9 2,2S7.1,8 6,8zM6,20c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2c1.1,0 2,0.9 2,2S7.1,20 6,20zM12,11.5c0.3,0 0.5,0.2 0.5,0.5c0,0.3 -0.2,0.5 -0.5,0.5c-0.3,0 -0.5,-0.2 -0.5,-0.5C11.5,11.7 11.7,11.5 12,11.5zM23,3l-4,0l-6,6l2,2L23,3z"
+            android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_menu_overflow_material.xml b/v7/appcompat/res/drawable/abc_ic_menu_overflow_material.xml
new file mode 100644
index 0000000..1420edd
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_menu_overflow_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2S10.9,8 12,8zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2c1.1,0 2,-0.9 2,-2S13.1,10 12,10zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2c1.1,0 2,-0.9 2,-2S13.1,16 12,16z"
+            android:fillColor="@android:color/white"/>
+</vector>
\ No newline at end of file
diff --git a/v7/appcompat/res/drawable/abc_ic_menu_paste_material.xml b/v7/appcompat/res/drawable/abc_ic_menu_paste_material.xml
new file mode 100644
index 0000000..b642aae
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_menu_paste_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:autoMirrored="true"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M19,2l-4.2,0c-0.4,-1.2 -1.5,-2 -2.8,-2c-1.3,0 -2.4,0.8 -2.8,2L5,2C3.9,2 3,2.9 3,4l0,16c0,1.1 0.9,2 2,2l14,0c1.1,0 2,-0.9 2,-2L21,4C21,2.9 20.1,2 19,2zM12,2c0.6,0 1,0.4 1,1s-0.4,1 -1,1c-0.6,0 -1,-0.4 -1,-1S11.4,2 12,2zM19,20L5,20L5,4l2,0l0,3l10,0L17,4l2,0L19,20z"
+            android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_menu_selectall_material.xml b/v7/appcompat/res/drawable/abc_ic_menu_selectall_material.xml
new file mode 100644
index 0000000..cd0bbd5
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_menu_selectall_material.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:autoMirrored="true"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M3,5l2,0L5,3C3.9,3 3,3.9 3,5zM3,13l2,0l0,-2L3,11L3,13zM7,21l2,0l0,-2L7,19L7,21zM3,9l2,0L5,7L3,7L3,9zM13,3l-2,0l0,2l2,0L13,3zM19,3l0,2l2,0C21,3.9 20.1,3 19,3zM5,21l0,-2L3,19C3,20.1 3.9,21 5,21zM3,17l2,0l0,-2L3,15L3,17zM9,3L7,3l0,2l2,0L9,3zM11,21l2,0l0,-2l-2,0L11,21zM19,13l2,0l0,-2l-2,0L19,13zM19,21c1.1,0 2,-0.9 2,-2l-2,0L19,21zM19,9l2,0L21,7l-2,0L19,9zM19,17l2,0l0,-2l-2,0L19,17zM15,21l2,0l0,-2l-2,0L15,21zM15,5l2,0L17,3l-2,0L15,5zM7,17l10,0L17,7L7,7L7,17zM9,9l6,0l0,6L9,15L9,9z"
+            android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_menu_share_material.xml b/v7/appcompat/res/drawable/abc_ic_menu_share_material.xml
new file mode 100644
index 0000000..ce5ceff
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_menu_share_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+            android:pathData="M18,16.1c-0.8,0 -1.5,0.3 -2,0.8l-7.1,-4.2C9,12.5 9,12.2 9,12s0,-0.5 -0.1,-0.7L16,7.2C16.5,7.7 17.200001,8 18,8c1.7,0 3,-1.3 3,-3s-1.3,-3 -3,-3s-3,1.3 -3,3c0,0.2 0,0.5 0.1,0.7L8,9.8C7.5,9.3 6.8,9 6,9c-1.7,0 -2.9,1.2 -2.9,2.9s1.3,3 3,3c0.8,0 1.5,-0.3 2,-0.8l7.1,4.2c-0.1,0.3 -0.1,0.5 -0.1,0.7c0,1.6 1.3,2.9 2.9,2.9s2.9,-1.3 2.9,-2.9S19.6,16.1 18,16.1z"
+            android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/drawable/abc_ic_voice_search_api_material.xml b/v7/appcompat/res/drawable/abc_ic_voice_search_api_material.xml
new file mode 100644
index 0000000..143db55
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_ic_voice_search_api_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:pathData="M12,14c1.7,0 3,-1.3 3,-3l0,-6c0,-1.7 -1.3,-3 -3,-3c-1.7,0 -3,1.3 -3,3l0,6C9,12.7 10.3,14 12,14zM17.299999,11c0,3 -2.5,5.1 -5.3,5.1c-2.8,0 -5.3,-2.1 -5.3,-5.1L5,11c0,3.4 2.7,6.2 6,6.7L11,21l2,0l0,-3.3c3.3,-0.5 6,-3.3 6,-6.7L17.299999,11.000001z"
+        android:fillColor="@android:color/white"/>
+</vector>
diff --git a/v7/appcompat/res/layout/abc_action_mode_close_item_material.xml b/v7/appcompat/res/layout/abc_action_mode_close_item_material.xml
index 2944d98..b3babb2 100644
--- a/v7/appcompat/res/layout/abc_action_mode_close_item_material.xml
+++ b/v7/appcompat/res/layout/abc_action_mode_close_item_material.xml
@@ -16,11 +16,12 @@
 
 <ImageView
         xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
         android:id="@+id/action_mode_close_button"
         android:contentDescription="@string/abc_action_mode_done"
         android:focusable="true"
         android:clickable="true"
-        android:src="?attr/actionModeCloseDrawable"
+        app:srcCompat="?attr/actionModeCloseDrawable"
         style="?attr/actionModeCloseButtonStyle"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"/>
\ No newline at end of file
diff --git a/v7/appcompat/res/layout/abc_activity_chooser_view.xml b/v7/appcompat/res/layout/abc_activity_chooser_view.xml
index 2522f1a..0100c23 100644
--- a/v7/appcompat/res/layout/abc_activity_chooser_view.xml
+++ b/v7/appcompat/res/layout/abc_activity_chooser_view.xml
@@ -31,16 +31,16 @@
         android:layout_gravity="center"
         android:focusable="true"
         android:addStatesFromChildren="true"
-        android:background="?attr/actionBarItemBackground">
+        android:background="?attr/actionBarItemBackground"
+        android:paddingTop="2dip"
+        android:paddingBottom="2dip"
+        android:paddingLeft="12dip"
+        android:paddingRight="12dip">
 
         <ImageView android:id="@+id/image"
             android:layout_width="32dip"
             android:layout_height="32dip"
             android:layout_gravity="center"
-            android:layout_marginTop="2dip"
-            android:layout_marginBottom="2dip"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
             android:scaleType="fitCenter"
             android:adjustViewBounds="true" />
 
@@ -53,16 +53,16 @@
         android:layout_gravity="center"
         android:focusable="true"
         android:addStatesFromChildren="true"
-        android:background="?attr/actionBarItemBackground">
+        android:background="?attr/actionBarItemBackground"
+        android:paddingTop="2dip"
+        android:paddingBottom="2dip"
+        android:paddingLeft="12dip"
+        android:paddingRight="12dip">
 
         <ImageView android:id="@+id/image"
             android:layout_width="32dip"
             android:layout_height="32dip"
             android:layout_gravity="center"
-            android:layout_marginTop="2dip"
-            android:layout_marginBottom="2dip"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
             android:scaleType="fitCenter"
             android:adjustViewBounds="true" />
 
diff --git a/v7/appcompat/res/layout/notification_media_cancel_action.xml b/v7/appcompat/res/layout/notification_media_cancel_action.xml
index e31d891..39a316c 100644
--- a/v7/appcompat/res/layout/notification_media_cancel_action.xml
+++ b/v7/appcompat/res/layout/notification_media_cancel_action.xml
@@ -16,6 +16,7 @@
   -->
 
 <ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     style="?android:attr/borderlessButtonStyle"
     android:id="@+id/cancel_action"
     android:layout_width="48dp"
@@ -23,6 +24,6 @@
     android:layout_marginLeft="2dp"
     android:layout_marginRight="2dp"
     android:layout_weight="1"
-    android:src="@drawable/abc_ic_clear_mtrl_alpha"
+    app:srcCompat="@drawable/abc_ic_clear_material"
     android:gravity="center"
     android:visibility="gone"/>
\ No newline at end of file
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index 66b8efa..f3e496e 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -965,4 +965,10 @@
         <attr name="allowStacking" format="boolean" />
     </declare-styleable>
 
+    <declare-styleable name="AppCompatImageView">
+        <attr name="android:src"/>
+        <!-- TODO -->
+        <attr name="srcCompat" format="reference" />
+    </declare-styleable>
+
 </resources>
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index dad7570..9063b1e 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -75,7 +75,7 @@
     </style>
 
     <style name="Base.Widget.AppCompat.ActionButton.Overflow" parent="RtlUnderlay.Widget.AppCompat.ActionButton.Overflow">
-        <item name="android:src">@drawable/abc_ic_menu_moreoverflow_mtrl_alpha</item>
+        <item name="srcCompat">@drawable/abc_ic_menu_overflow_material</item>
         <item name="android:background">?attr/actionBarItemBackground</item>
         <item name="android:contentDescription">@string/abc_action_menu_overflow_description</item>
         <item name="android:minWidth">@dimen/abc_action_button_min_width_overflow_material</item>
@@ -327,11 +327,11 @@
         <item name="layout">@layout/abc_search_view</item>
         <item name="queryBackground">@drawable/abc_textfield_search_material</item>
         <item name="submitBackground">@drawable/abc_textfield_search_material</item>
-        <item name="closeIcon">@drawable/abc_ic_clear_mtrl_alpha</item>
+        <item name="closeIcon">@drawable/abc_ic_clear_material</item>
         <item name="searchIcon">@drawable/abc_ic_search_api_mtrl_alpha</item>
         <item name="searchHintIcon">@drawable/abc_ic_search_api_mtrl_alpha</item>
-        <item name="goIcon">@drawable/abc_ic_go_search_api_mtrl_alpha</item>
-        <item name="voiceIcon">@drawable/abc_ic_voice_search_api_mtrl_alpha</item>
+        <item name="goIcon">@drawable/abc_ic_go_search_api_material</item>
+        <item name="voiceIcon">@drawable/abc_ic_voice_search_api_material</item>
         <item name="commitIcon">@drawable/abc_ic_commit_search_api_mtrl_alpha</item>
         <item name="suggestionRowLayout">@layout/abc_search_dropdown_item_icons_2line</item>
     </style>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 38757df..4aaaed1 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -123,7 +123,7 @@
         <item name="selectableItemBackground">@drawable/abc_item_background_holo_dark</item>
         <item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
         <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
-        <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
+        <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_material</item>
 
         <item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
         <item name="dividerHorizontal">@drawable/abc_list_divider_mtrl_alpha</item>
@@ -152,14 +152,14 @@
         <item name="actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
         <item name="actionModeBackground">@drawable/abc_cab_background_top_material</item>
         <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
-        <item name="actionModeCloseDrawable">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
+        <item name="actionModeCloseDrawable">@drawable/abc_ic_ab_back_material</item>
         <item name="actionModeCloseButtonStyle">@style/Widget.AppCompat.ActionButton.CloseMode</item>
 
-        <item name="actionModeCutDrawable">@drawable/abc_ic_menu_cut_mtrl_alpha</item>
-        <item name="actionModeCopyDrawable">@drawable/abc_ic_menu_copy_mtrl_am_alpha</item>
-        <item name="actionModePasteDrawable">@drawable/abc_ic_menu_paste_mtrl_am_alpha</item>
-        <item name="actionModeSelectAllDrawable">@drawable/abc_ic_menu_selectall_mtrl_alpha</item>
-        <item name="actionModeShareDrawable">@drawable/abc_ic_menu_share_mtrl_alpha</item>
+        <item name="actionModeCutDrawable">@drawable/abc_ic_menu_cut_material</item>
+        <item name="actionModeCopyDrawable">@drawable/abc_ic_menu_copy_material</item>
+        <item name="actionModePasteDrawable">@drawable/abc_ic_menu_paste_material</item>
+        <item name="actionModeSelectAllDrawable">@drawable/abc_ic_menu_selectall_material</item>
+        <item name="actionModeShareDrawable">@drawable/abc_ic_menu_share_material</item>
 
         <!-- Panel attributes -->
         <item name="panelMenuListWidth">@dimen/abc_panel_menu_list_width</item>
@@ -280,7 +280,7 @@
         <item name="selectableItemBackground">@drawable/abc_item_background_holo_light</item>
         <item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
         <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
-        <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
+        <item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_material</item>
 
         <item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
         <item name="dividerHorizontal">@drawable/abc_list_divider_mtrl_alpha</item>
@@ -306,14 +306,14 @@
         <item name="actionModeStyle">@style/Widget.AppCompat.ActionMode</item>
         <item name="actionModeBackground">@drawable/abc_cab_background_top_material</item>
         <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
-        <item name="actionModeCloseDrawable">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
+        <item name="actionModeCloseDrawable">@drawable/abc_ic_ab_back_material</item>
         <item name="actionModeCloseButtonStyle">@style/Widget.AppCompat.ActionButton.CloseMode</item>
 
-        <item name="actionModeCutDrawable">@drawable/abc_ic_menu_cut_mtrl_alpha</item>
-        <item name="actionModeCopyDrawable">@drawable/abc_ic_menu_copy_mtrl_am_alpha</item>
-        <item name="actionModePasteDrawable">@drawable/abc_ic_menu_paste_mtrl_am_alpha</item>
-        <item name="actionModeSelectAllDrawable">@drawable/abc_ic_menu_selectall_mtrl_alpha</item>
-        <item name="actionModeShareDrawable">@drawable/abc_ic_menu_share_mtrl_alpha</item>
+        <item name="actionModeCutDrawable">@drawable/abc_ic_menu_cut_material</item>
+        <item name="actionModeCopyDrawable">@drawable/abc_ic_menu_copy_material</item>
+        <item name="actionModePasteDrawable">@drawable/abc_ic_menu_paste_material</item>
+        <item name="actionModeSelectAllDrawable">@drawable/abc_ic_menu_selectall_material</item>
+        <item name="actionModeShareDrawable">@drawable/abc_ic_menu_share_material</item>
 
         <!-- Dropdown Spinner Attributes -->
         <item name="actionDropDownStyle">@style/Widget.AppCompat.Light.Spinner.DropDown.ActionBar</item>
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
index 15dacb0..1b06b82 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatButton.java
@@ -59,7 +59,7 @@
     }
 
     public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mDrawableManager = AppCompatDrawableManager.get();
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this, mDrawableManager);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
index 4a6ecbe..65cab42 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckBox.java
@@ -55,7 +55,7 @@
     }
 
     public AppCompatCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
         mDrawableManager = AppCompatDrawableManager.get();
         mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this, mDrawableManager);
         mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
index 29c877c..58098c08 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatCheckedTextView.java
@@ -45,7 +45,7 @@
     }
 
     public AppCompatCheckedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mTextHelper = AppCompatTextHelper.create(this);
         mTextHelper.loadFromAttributes(attrs, defStyleAttr);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index 422ec1f..322bf81 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -16,8 +16,12 @@
 
 package android.support.v7.widget;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -31,15 +35,20 @@
 import android.support.annotation.DrawableRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.util.ArrayMap;
 import android.support.v4.util.LruCache;
 import android.support.v7.appcompat.R;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedValue;
+import android.util.Xml;
 
-import java.util.ArrayList;
+import java.lang.reflect.Type;
 import java.util.WeakHashMap;
 
 import static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
@@ -52,27 +61,34 @@
 public final class AppCompatDrawableManager {
 
     public interface InflateDelegate {
-        /**
-         * Allows custom inflation of a drawable resource.
-         *
-         * @param context Context to inflate/create with
-         * @param resId Resource ID of the drawable
-         * @return the created drawable, or {@code null} to leave inflation to
-         * AppCompatDrawableManager.
-         */
-        @Nullable
-        Drawable onInflateDrawable(@NonNull Context context, @DrawableRes int resId);
+        Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
+                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme);
     }
 
+    private static final InflateDelegate VDC_DELEGATE = new InflateDelegate() {
+        @Override
+        public Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
+                @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
+            try {
+                return VectorDrawableCompat.createFromXmlInner(r, parser, attrs, theme);
+            } catch (Exception e) {
+                Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e);
+                return null;
+            }
+        }
+    };
+
     private static final String TAG = "TintManager";
     private static final boolean DEBUG = false;
     private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
+    private static final String SKIP_DRAWABLE_TAG = "appcompat_skip_skip";
 
     private static AppCompatDrawableManager INSTANCE;
 
     public static AppCompatDrawableManager get() {
         if (INSTANCE == null) {
             INSTANCE = new AppCompatDrawableManager();
+            INSTANCE.addDelegate("vector", VDC_DELEGATE);
         }
         return INSTANCE;
     }
@@ -94,18 +110,8 @@
      * {@link DrawableCompat}'s tinting functionality.
      */
     private static final int[] TINT_COLOR_CONTROL_NORMAL = {
-            R.drawable.abc_ic_ab_back_mtrl_am_alpha,
-            R.drawable.abc_ic_go_search_api_mtrl_alpha,
             R.drawable.abc_ic_search_api_mtrl_alpha,
             R.drawable.abc_ic_commit_search_api_mtrl_alpha,
-            R.drawable.abc_ic_clear_mtrl_alpha,
-            R.drawable.abc_ic_menu_share_mtrl_alpha,
-            R.drawable.abc_ic_menu_copy_mtrl_am_alpha,
-            R.drawable.abc_ic_menu_cut_mtrl_alpha,
-            R.drawable.abc_ic_menu_selectall_mtrl_alpha,
-            R.drawable.abc_ic_menu_paste_mtrl_am_alpha,
-            R.drawable.abc_ic_menu_moreoverflow_mtrl_alpha,
-            R.drawable.abc_ic_voice_search_api_mtrl_alpha
     };
 
     /**
@@ -134,16 +140,9 @@
      * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated}
      */
     private static final int[] TINT_COLOR_CONTROL_STATE_LIST = {
-            R.drawable.abc_edit_text_material,
             R.drawable.abc_tab_indicator_material,
             R.drawable.abc_textfield_search_material,
-            R.drawable.abc_spinner_mtrl_am_alpha,
-            R.drawable.abc_spinner_textfield_background_material,
-            R.drawable.abc_ratingbar_full_material,
-            R.drawable.abc_switch_track_mtrl_alpha,
-            R.drawable.abc_switch_thumb_material,
-            R.drawable.abc_btn_default_mtrl_shape,
-            R.drawable.abc_btn_borderless_material
+            R.drawable.abc_ratingbar_full_material
     };
 
     /**
@@ -157,7 +156,10 @@
     };
 
     private WeakHashMap<Context, SparseArray<ColorStateList>> mTintLists;
-    private ArrayList<InflateDelegate> mDelegates;
+    private ArrayMap<String, InflateDelegate> mDelegates;
+    private SparseArray<String> mKnownDrawableIdTags;
+
+    private TypedValue mTypedValue;
 
     public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
         return getDrawable(context, resId, false);
@@ -165,71 +167,145 @@
 
     public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId,
             boolean failIfNotKnown) {
-        // Let the InflateDelegates have a go first
-        if (mDelegates != null) {
-            for (int i = 0, count = mDelegates.size(); i < count; i++) {
-                final InflateDelegate delegate = mDelegates.get(i);
-                final Drawable result = delegate.onInflateDrawable(context, resId);
-                if (result != null) {
-                    return result;
-                }
-            }
+        Drawable drawable = loadDrawableFromDelegates(context, resId);
+        if (drawable == null) {
+            drawable = ContextCompat.getDrawable(context, resId);
         }
-
-        // The delegates failed so we'll carry on
-        Drawable drawable = ContextCompat.getDrawable(context, resId);
-
         if (drawable != null) {
-            final ColorStateList tintList = getTintList(context, resId);
-            if (tintList != null) {
-                // First mutate the Drawable, then wrap it and set the tint list
-                if (shouldMutateDrawable(drawable)) {
-                    drawable = drawable.mutate();
-                }
-                drawable = DrawableCompat.wrap(drawable);
-                DrawableCompat.setTintList(drawable, tintList);
+            return tintDrawable(context, resId, failIfNotKnown, drawable);
+        }
+        return null;
+    }
 
-                // If there is a blending mode specified for the drawable, use it
-                final PorterDuff.Mode tintMode = getTintMode(resId);
-                if (tintMode != null) {
-                    DrawableCompat.setTintMode(drawable, tintMode);
-                }
-            } else if (resId == R.drawable.abc_cab_background_top_material) {
-                return new LayerDrawable(new Drawable[]{
-                        getDrawable(context, R.drawable.abc_cab_background_internal_bg),
-                        getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha)
-                });
-            } else if (resId == R.drawable.abc_seekbar_track_material) {
-                LayerDrawable ld = (LayerDrawable) drawable;
-                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
-                        getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
-                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
-                        getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
-                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
-                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
-            } else if (resId == R.drawable.abc_ratingbar_indicator_material
-                    || resId == R.drawable.abc_ratingbar_small_material) {
-                LayerDrawable ld = (LayerDrawable) drawable;
-                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
-                        getDisabledThemeAttrColor(context, R.attr.colorControlNormal),
-                        DEFAULT_MODE);
-                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
-                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
-                setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
-                        getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
-            } else {
-                final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable);
-                if (!tinted && failIfNotKnown) {
-                    // If we didn't tint using a ColorFilter, and we're set to fail if we don't
-                    // know the id, return null
-                    drawable = null;
-                }
+    private Drawable tintDrawable(@NonNull Context context, @DrawableRes int resId,
+            boolean failIfNotKnown, @NonNull Drawable drawable) {
+        final ColorStateList tintList = getTintList(context, resId);
+        if (tintList != null) {
+            // First mutate the Drawable, then wrap it and set the tint list
+            if (shouldMutateDrawable(drawable)) {
+                drawable = drawable.mutate();
+            }
+            drawable = DrawableCompat.wrap(drawable);
+            DrawableCompat.setTintList(drawable, tintList);
+
+            // If there is a blending mode specified for the drawable, use it
+            final PorterDuff.Mode tintMode = getTintMode(resId);
+            if (tintMode != null) {
+                DrawableCompat.setTintMode(drawable, tintMode);
+            }
+        } else if (resId == R.drawable.abc_cab_background_top_material) {
+            return new LayerDrawable(new Drawable[]{
+                    getDrawable(context, R.drawable.abc_cab_background_internal_bg),
+                    getDrawable(context, R.drawable.abc_cab_background_top_mtrl_alpha)
+            });
+        } else if (resId == R.drawable.abc_seekbar_track_material) {
+            LayerDrawable ld = (LayerDrawable) drawable;
+            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
+                    getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
+            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
+                    getThemeAttrColor(context, R.attr.colorControlNormal), DEFAULT_MODE);
+            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
+                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
+        } else if (resId == R.drawable.abc_ratingbar_indicator_material
+                || resId == R.drawable.abc_ratingbar_small_material) {
+            LayerDrawable ld = (LayerDrawable) drawable;
+            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.background),
+                    getDisabledThemeAttrColor(context, R.attr.colorControlNormal),
+                    DEFAULT_MODE);
+            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.secondaryProgress),
+                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
+            setPorterDuffColorFilter(ld.findDrawableByLayerId(android.R.id.progress),
+                    getThemeAttrColor(context, R.attr.colorControlActivated), DEFAULT_MODE);
+        } else {
+            final boolean tinted = tintDrawableUsingColorFilter(context, resId, drawable);
+            if (!tinted && failIfNotKnown) {
+                // If we didn't tint using a ColorFilter, and we're set to fail if we don't
+                // know the id, return null
+                drawable = null;
             }
         }
         return drawable;
     }
 
-    public final boolean tintDrawableUsingColorFilter(@NonNull Context context,
+    private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) {
+        if (mDelegates != null && !mDelegates.isEmpty()) {
+            String cachedTagName = null;
+
+            if (mKnownDrawableIdTags != null) {
+                cachedTagName = mKnownDrawableIdTags.get(resId);
+                if (SKIP_DRAWABLE_TAG.equals(cachedTagName)
+                        || (cachedTagName != null && mDelegates.get(cachedTagName) == null)) {
+                    // If we don't have a delegate for the drawable tag, or we've been set to
+                    // skip it, fail fast and return null
+                    if (DEBUG) {
+                        Log.d(TAG, "loadDrawableFromDelegates. Skipping drawable "
+                                + context.getResources().getResourceName(resId));
+                    }
+                    return null;
+                }
+            } else {
+                // Create an id cache as we'll need one later
+                mKnownDrawableIdTags = new SparseArray<>();
+            }
+
+            if (mTypedValue == null) {
+                mTypedValue = new TypedValue();
+            }
+
+            final TypedValue tv = mTypedValue;
+            final Resources res = context.getResources();
+            res.getValue(resId, tv, true);
+
+            if (tv.string != null && tv.string.toString().endsWith(".xml")) {
+                // If the resource is an XML file, let's try and parse it
+                try {
+                    final XmlPullParser parser = res.getXml(resId);
+                    final AttributeSet attrs = Xml.asAttributeSet(parser);
+                    int type;
+                    while ((type = parser.next()) != XmlPullParser.START_TAG &&
+                            type != XmlPullParser.END_DOCUMENT) {
+                        // Empty loop
+                    }
+                    if (type != XmlPullParser.START_TAG) {
+                        throw new XmlPullParserException("No start tag found");
+                    }
+
+                    final String tagName = parser.getName();
+                    if (cachedTagName == null) {
+                        // If we don't already have this cached, add it to the cache
+                        mKnownDrawableIdTags.append(resId, tagName);
+                    }
+
+                    // Now try and find a delegate for the tag name and inflate if found
+                    final InflateDelegate delegate = mDelegates.get(tagName);
+                    if (delegate != null) {
+                        return delegate.createFromXmlInner(res, parser, attrs, context.getTheme());
+                    }
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception while inflating drawable", e);
+                }
+            }
+        }
+
+        // If we reach here then the delegate inflation of the resource failed. Mark it as
+        // bad so we skip the id next time
+        mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG);
+        return null;
+    }
+
+    public final Drawable onDrawableLoadedFromResources(@NonNull Context context,
+            @NonNull TintResources resources, @DrawableRes final int resId) {
+        Drawable drawable = loadDrawableFromDelegates(context, resId);
+        if (drawable == null) {
+            drawable = resources.superGetDrawable(resId);
+        }
+        if (drawable != null) {
+            return tintDrawable(context, resId, false, drawable);
+        }
+        return null;
+    }
+
+    private static boolean tintDrawableUsingColorFilter(@NonNull Context context,
             @DrawableRes final int resId, @NonNull Drawable drawable) {
         PorterDuff.Mode tintMode = DEFAULT_MODE;
         boolean colorAttrSet = false;
@@ -273,18 +349,16 @@
         return false;
     }
 
-    public void addDelegate(@NonNull InflateDelegate delegate) {
+    public final void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
         if (mDelegates == null) {
-            mDelegates = new ArrayList<>();
+            mDelegates = new ArrayMap<>();
         }
-        if (!mDelegates.contains(delegate)) {
-            mDelegates.add(delegate);
-        }
+        mDelegates.put(tagName, delegate);
     }
 
-    public void removeDelegate(@NonNull InflateDelegate delegate) {
-        if (mDelegates != null) {
-            mDelegates.remove(delegate);
+    public final void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
+        if (mDelegates != null && mDelegates.get(tagName) == delegate) {
+            mDelegates.remove(tagName);
         }
     }
 
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java
index 18c07c9..379ae0c 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatImageButton.java
@@ -54,7 +54,7 @@
     }
 
     public AppCompatImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         final AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
 
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java
index 89cc04d..6bfce53 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java
@@ -18,13 +18,12 @@
 
 import android.graphics.drawable.Drawable;
 import android.support.v4.content.ContextCompat;
+import android.support.v7.appcompat.R;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
 class AppCompatImageHelper {
 
-    private static final int[] VIEW_ATTRS = {android.R.attr.src};
-
     private final ImageView mView;
     private final AppCompatDrawableManager mDrawableManager;
 
@@ -35,12 +34,20 @@
 
     void loadFromAttributes(AttributeSet attrs, int defStyleAttr) {
         TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
-                VIEW_ATTRS, defStyleAttr, 0);
+                R.styleable.AppCompatImageView, defStyleAttr, 0);
         try {
-            final Drawable d = a.getDrawableIfKnown(0);
+            Drawable d = a.getDrawableIfKnown(R.styleable.AppCompatImageView_android_src);
             if (d != null) {
                 mView.setImageDrawable(d);
             }
+
+            final int id = a.getResourceId(R.styleable.AppCompatImageView_srcCompat, -1);
+            if (id != -1) {
+                d = mDrawableManager.getDrawable(mView.getContext(), id);
+                if (d != null) {
+                    mView.setImageDrawable(d);
+                }
+            }
         } finally {
             a.recycle();
         }
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java
index 0d85121..90bc173 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatImageView.java
@@ -54,7 +54,7 @@
     }
 
     public AppCompatImageView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         final AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
 
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java b/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
index 3814a7e..cc1adc6 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatRadioButton.java
@@ -55,7 +55,7 @@
     }
 
     public AppCompatRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
         mDrawableManager = AppCompatDrawableManager.get();
         mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this, mDrawableManager);
         mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
index b21f6bc..fdb4960 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatTextView.java
@@ -57,7 +57,7 @@
     }
 
     public AppCompatTextView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
+        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
 
         mDrawableManager = AppCompatDrawableManager.get();
         mBackgroundTintHelper = new AppCompatBackgroundHelper(this, mDrawableManager);
diff --git a/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java b/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java
deleted file mode 100644
index 828a792..0000000
--- a/v7/appcompat/src/android/support/v7/widget/ResourcesWrapper.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2015 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.v7.widget;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.res.AssetFileDescriptor;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Movie;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * This extends Resources but delegates the calls to another Resources object. This enables
- * any customization done by some subclass of Resources to be also picked up.
- */
-class ResourcesWrapper extends Resources {
-
-    private final Resources mResources;
-
-    public ResourcesWrapper(Resources resources) {
-        super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
-        mResources = resources;
-    }
-
-    @Override
-    public CharSequence getText(int id) throws NotFoundException {
-        return mResources.getText(id);
-    }
-
-    @Override
-    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
-        return mResources.getQuantityText(id, quantity);
-    }
-
-    @Override
-    public String getString(int id) throws NotFoundException {
-        return mResources.getString(id);
-    }
-
-    @Override
-    public String getString(int id, Object... formatArgs) throws NotFoundException {
-        return mResources.getString(id, formatArgs);
-    }
-
-    @Override
-    public String getQuantityString(int id, int quantity, Object... formatArgs)
-            throws NotFoundException {
-        return mResources.getQuantityString(id, quantity, formatArgs);
-    }
-
-    @Override
-    public String getQuantityString(int id, int quantity) throws NotFoundException {
-        return mResources.getQuantityString(id, quantity);
-    }
-
-    @Override
-    public CharSequence getText(int id, CharSequence def) {
-        return mResources.getText(id, def);
-    }
-
-    @Override
-    public CharSequence[] getTextArray(int id) throws NotFoundException {
-        return mResources.getTextArray(id);
-    }
-
-    @Override
-    public String[] getStringArray(int id) throws NotFoundException {
-        return mResources.getStringArray(id);
-    }
-
-    @Override
-    public int[] getIntArray(int id) throws NotFoundException {
-        return mResources.getIntArray(id);
-    }
-
-    @Override
-    public TypedArray obtainTypedArray(int id) throws NotFoundException {
-        return mResources.obtainTypedArray(id);
-    }
-
-    @Override
-    public float getDimension(int id) throws NotFoundException {
-        return mResources.getDimension(id);
-    }
-
-    @Override
-    public int getDimensionPixelOffset(int id) throws NotFoundException {
-        return mResources.getDimensionPixelOffset(id);
-    }
-
-    @Override
-    public int getDimensionPixelSize(int id) throws NotFoundException {
-        return mResources.getDimensionPixelSize(id);
-    }
-
-    @Override
-    public float getFraction(int id, int base, int pbase) {
-        return mResources.getFraction(id, base, pbase);
-    }
-
-    @Override
-    public Drawable getDrawable(int id) throws NotFoundException {
-        return mResources.getDrawable(id);
-    }
-
-    @Override
-    public Drawable getDrawable(int id, Theme theme) throws NotFoundException {
-        return mResources.getDrawable(id, theme);
-    }
-
-    @Override
-    public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
-        return mResources.getDrawableForDensity(id, density);
-    }
-
-    @Override
-    public Drawable getDrawableForDensity(int id, int density, Theme theme) {
-        return mResources.getDrawableForDensity(id, density, theme);
-    }
-
-    @Override
-    public Movie getMovie(int id) throws NotFoundException {
-        return mResources.getMovie(id);
-    }
-
-    @Override
-    public int getColor(int id) throws NotFoundException {
-        return mResources.getColor(id);
-    }
-
-    @Override
-    public ColorStateList getColorStateList(int id) throws NotFoundException {
-        return mResources.getColorStateList(id);
-    }
-
-    @Override
-    public boolean getBoolean(int id) throws NotFoundException {
-        return mResources.getBoolean(id);
-    }
-
-    @Override
-    public int getInteger(int id) throws NotFoundException {
-        return mResources.getInteger(id);
-    }
-
-    @Override
-    public XmlResourceParser getLayout(int id) throws NotFoundException {
-        return mResources.getLayout(id);
-    }
-
-    @Override
-    public XmlResourceParser getAnimation(int id) throws NotFoundException {
-        return mResources.getAnimation(id);
-    }
-
-    @Override
-    public XmlResourceParser getXml(int id) throws NotFoundException {
-        return mResources.getXml(id);
-    }
-
-    @Override
-    public InputStream openRawResource(int id) throws NotFoundException {
-        return mResources.openRawResource(id);
-    }
-
-    @Override
-    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
-        return mResources.openRawResource(id, value);
-    }
-
-    @Override
-    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
-        return mResources.openRawResourceFd(id);
-    }
-
-    @Override
-    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
-            throws NotFoundException {
-        mResources.getValue(id, outValue, resolveRefs);
-    }
-
-    @Override
-    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
-            throws NotFoundException {
-        mResources.getValueForDensity(id, density, outValue, resolveRefs);
-    }
-
-    @Override
-    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
-            throws NotFoundException {
-        mResources.getValue(name, outValue, resolveRefs);
-    }
-
-    @Override
-    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
-        return mResources.obtainAttributes(set, attrs);
-    }
-
-    @Override
-    public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
-        super.updateConfiguration(config, metrics);
-        if (mResources != null) { // called from super's constructor. So, need to check.
-            mResources.updateConfiguration(config, metrics);
-        }
-    }
-
-    @Override
-    public DisplayMetrics getDisplayMetrics() {
-        return mResources.getDisplayMetrics();
-    }
-
-    @Override
-    public Configuration getConfiguration() {
-        return mResources.getConfiguration();
-    }
-
-    @Override
-    public int getIdentifier(String name, String defType, String defPackage) {
-        return mResources.getIdentifier(name, defType, defPackage);
-    }
-
-    @Override
-    public String getResourceName(int resid) throws NotFoundException {
-        return mResources.getResourceName(resid);
-    }
-
-    @Override
-    public String getResourcePackageName(int resid) throws NotFoundException {
-        return mResources.getResourcePackageName(resid);
-    }
-
-    @Override
-    public String getResourceTypeName(int resid) throws NotFoundException {
-        return mResources.getResourceTypeName(resid);
-    }
-
-    @Override
-    public String getResourceEntryName(int resid) throws NotFoundException {
-        return mResources.getResourceEntryName(resid);
-    }
-
-    @Override
-    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
-            throws XmlPullParserException, IOException {
-        mResources.parseBundleExtras(parser, outBundle);
-    }
-
-    @Override
-    public void parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle)
-            throws XmlPullParserException {
-        mResources.parseBundleExtra(tagName, attrs, outBundle);
-    }
-}
-
diff --git a/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java b/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
index ef12e06..2503fe4 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
@@ -20,6 +20,7 @@
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
 
 /**
  * A {@link android.content.ContextWrapper} which returns a tint-aware
@@ -35,40 +36,32 @@
     }
 
     private Resources mResources;
+    private final Resources.Theme mTheme;
 
-    private TintContextWrapper(Context base) {
+    private TintContextWrapper(@NonNull final Context base) {
         super(base);
+
+        // We need to create a copy of the Theme so that the Theme references our Resources
+        // instance
+        mTheme = getResources().newTheme();
+        mTheme.setTo(base.getTheme());
+    }
+
+    @Override
+    public Resources.Theme getTheme() {
+        return mTheme;
+    }
+
+    @Override
+    public void setTheme(int resid) {
+        mTheme.applyStyle(resid, true);
     }
 
     @Override
     public Resources getResources() {
         if (mResources == null) {
-            mResources = new TintResources(super.getResources());
+            mResources = new TintResources(this, super.getResources());
         }
         return mResources;
     }
-
-    /**
-     * This class allows us to intercept calls so that we can tint resources (if applicable).
-     */
-    class TintResources extends ResourcesWrapper {
-        public TintResources(Resources resources) {
-            super(resources);
-        }
-
-        /**
-         * We intercept this call so that we tint the result (if applicable). This is needed for
-         * things like {@link android.graphics.drawable.DrawableContainer}s which can retrieve
-         * their children via this method.
-         */
-        @Override
-        public Drawable getDrawable(int id) throws NotFoundException {
-            Drawable d = super.getDrawable(id);
-            if (d != null) {
-                AppCompatDrawableManager.get().tintDrawableUsingColorFilter(
-                        TintContextWrapper.this, id, d);
-            }
-            return d;
-        }
-    }
 }
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/widget/TintResources.java b/v7/appcompat/src/android/support/v7/widget/TintResources.java
new file mode 100644
index 0000000..bd63634
--- /dev/null
+++ b/v7/appcompat/src/android/support/v7/widget/TintResources.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.v7.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+
+/**
+ * This class allows us to intercept calls so that we can tint resources (if applicable).
+ */
+class TintResources extends Resources {
+    private final Context mContext;
+
+    TintResources(@NonNull final Context context, @NonNull final Resources res) {
+        super(res.getAssets(), res.getDisplayMetrics(), res.getConfiguration());
+        mContext = context;
+    }
+
+    /**
+     * We intercept this call so that we tint the result (if applicable). This is needed for
+     * things like {@link android.graphics.drawable.DrawableContainer}s which can retrieve
+     * their children via this method.
+     */
+    @Override
+    public Drawable getDrawable(int id) throws NotFoundException {
+        return AppCompatDrawableManager.get().onDrawableLoadedFromResources(mContext, this, id);
+    }
+
+    final Drawable superGetDrawable(int id) {
+        return super.getDrawable(id);
+    }
+}
\ No newline at end of file
diff --git a/v7/appcompat/src/android/support/v7/widget/ToolbarWidgetWrapper.java b/v7/appcompat/src/android/support/v7/widget/ToolbarWidgetWrapper.java
index 0576c15..cb88cfd 100644
--- a/v7/appcompat/src/android/support/v7/widget/ToolbarWidgetWrapper.java
+++ b/v7/appcompat/src/android/support/v7/widget/ToolbarWidgetWrapper.java
@@ -89,7 +89,7 @@
 
     public ToolbarWidgetWrapper(Toolbar toolbar, boolean style) {
         this(toolbar, style, R.string.abc_action_bar_up_description,
-                R.drawable.abc_ic_ab_back_mtrl_am_alpha);
+                R.drawable.abc_ic_ab_back_material);
     }
 
     public ToolbarWidgetWrapper(Toolbar toolbar, boolean style,
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
new file mode 100644
index 0000000..d91ce9f
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AlertDialogCursorTest.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2015 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.v7.app;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.test.espresso.DataInteraction;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.TestUtilsMatchers;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckedTextView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import org.hamcrest.Matcher;
+
+import java.io.File;
+
+import static android.support.test.espresso.Espresso.onData;
+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.RootMatchers.isDialog;
+import static android.support.test.espresso.matcher.ViewMatchers.*;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.core.AllOf.allOf;
+import static org.hamcrest.core.Is.is;
+
+public class AlertDialogCursorTest
+        extends ActivityInstrumentationTestCase2<AlertDialogTestActivity> {
+
+    private Button mButton;
+
+    private int mClickedItemIndex = -1;
+
+    private static final String TEXT_COLUMN_NAME = "text";
+    private static final String CHECKED_COLUMN_NAME = "checked";
+
+    private String[] mTextContent;
+    private boolean[] mCheckedContent;
+
+    private String[] mProjectionWithChecked;
+    private String[] mProjectionWithoutChecked;
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private Cursor mCursor;
+
+    private AlertDialog mAlertDialog;
+
+    public AlertDialogCursorTest() {
+        super(AlertDialogTestActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Ideally these constant arrays would be defined as final static fields on the
+        // class level, but for some reason those get reset to null on v9- devices after
+        // the first test method has been executed.
+        mTextContent = new String[] { "Adele", "Beyonce", "Ciara", "Dido" };
+        mCheckedContent = new boolean[] { false, false, true, false };
+
+        mProjectionWithChecked = new String[] {
+                "_id",                       // 0
+                TEXT_COLUMN_NAME,            // 1
+                CHECKED_COLUMN_NAME          // 2
+        };
+        mProjectionWithoutChecked = new String[] {
+                "_id",                       // 0
+                TEXT_COLUMN_NAME             // 1
+        };
+
+        final AlertDialogTestActivity activity = getActivity();
+        mButton = (Button) activity.findViewById(R.id.test_button);
+
+        Context context = getInstrumentation().getTargetContext();
+        File dbDir = context.getDir("tests", Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, "database_alert_dialog_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+        // Create and populate a test table
+        mDatabase.execSQL(
+                "CREATE TABLE test (_id INTEGER PRIMARY KEY, " + TEXT_COLUMN_NAME +
+                        " TEXT, " + CHECKED_COLUMN_NAME + " INTEGER);");
+        for (int i = 0; i < mTextContent.length; i++) {
+            mDatabase.execSQL("INSERT INTO test (" + TEXT_COLUMN_NAME + ", " +
+                    CHECKED_COLUMN_NAME + ") VALUES ('" + mTextContent[i] + "', " +
+                    (mCheckedContent[i] ? "1" : "0") + ");");
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mCursor != null) {
+            try {
+                // Close the cursor on the UI thread as the list view in the alert dialog
+                // will get notified of any change to the underlying cursor.
+                runTestOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        mCursor.close();
+                        mCursor = null;
+                    }
+                });
+            } catch (Throwable t) {
+                throw new RuntimeException(t);
+            }
+        }
+        if (mDatabase != null) {
+            mDatabase.close();
+        }
+        if (mDatabaseFile != null) {
+            mDatabaseFile.delete();
+        }
+        super.tearDown();
+    }
+
+    private void wireBuilder(final AlertDialog.Builder builder) {
+        mButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAlertDialog = builder.show();
+            }
+        });
+    }
+
+    private void verifySimpleItemsContent(String[] expectedContent) {
+        final int expectedCount = expectedContent.length;
+
+        onView(withId(R.id.test_button)).perform(click());
+
+        final ListView listView = mAlertDialog.getListView();
+        assertNotNull("List view is shown", listView);
+
+        final ListAdapter listAdapter = listView.getAdapter();
+        assertEquals("List has " + expectedCount + " entries",
+                expectedCount, listAdapter.getCount());
+
+        // Test that all items are showing
+        onView(withText("Dialog title")).inRoot(isDialog()).check(matches(isDisplayed()));
+        for (int i = 0; i < expectedCount; i++) {
+            DataInteraction rowInteraction = onData(allOf(
+                    is(instanceOf(SQLiteCursor.class)),
+                    TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[i])));
+            rowInteraction.inRoot(isDialog()).check(matches(isDisplayed()));
+        }
+
+        // Test that a click on an item invokes the registered listener
+        assertEquals("Before list item click", -1, mClickedItemIndex);
+        int indexToClick = expectedCount - 2;
+        DataInteraction interactionForClick = onData(allOf(
+                is(instanceOf(SQLiteCursor.class)),
+                TestUtilsMatchers.withCursorItemContent(
+                        TEXT_COLUMN_NAME, expectedContent[indexToClick])));
+        interactionForClick.inRoot(isDialog()).perform(click());
+        assertEquals("List item clicked", indexToClick, mClickedItemIndex);
+    }
+
+    @SmallTest
+    public void testSimpleItemsFromCursor() {
+        mCursor = mDatabase.query("test", mProjectionWithoutChecked,
+                null, null, null, null, null);
+        assertNotNull(mCursor);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.alert_dialog_title)
+                .setCursor(mCursor,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mClickedItemIndex = which;
+                            }
+                        }, "text");
+        wireBuilder(builder);
+
+        verifySimpleItemsContent(mTextContent);
+    }
+
+    /**
+     * Helper method to verify the state of the multi-choice items list. It gets the String
+     * array of content and verifies that:
+     *
+     * 1. The items in the array are rendered as CheckedTextViews inside a ListView
+     * 2. Each item in the array is displayed
+     * 3. Checked state of each row in the ListView corresponds to the matching entry in the
+     *    passed boolean array
+     */
+    private void verifyMultiChoiceItemsState(String[] expectedContent,
+            boolean[] checkedTracker) {
+        final int expectedCount = expectedContent.length;
+
+        final ListView listView = mAlertDialog.getListView();
+        assertNotNull("List view is shown", listView);
+
+        final ListAdapter listAdapter = listView.getAdapter();
+        assertEquals("List has " + expectedCount + " entries",
+                expectedCount, listAdapter.getCount());
+
+        for (int i = 0; i < expectedCount; i++) {
+            Matcher checkedStateMatcher = checkedTracker[i] ? TestUtilsMatchers.isCheckedTextView() :
+                    TestUtilsMatchers.isNonCheckedTextView();
+            // Check that the corresponding row is rendered as CheckedTextView with expected
+            // checked state.
+            DataInteraction rowInteraction = onData(allOf(
+                    is(instanceOf(SQLiteCursor.class)),
+                    TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[i])));
+            rowInteraction.inRoot(isDialog()).
+                    check(matches(allOf(
+                            isDisplayed(),
+                            isAssignableFrom(CheckedTextView.class),
+                            isDescendantOfA(isAssignableFrom(ListView.class)),
+                            checkedStateMatcher)));
+        }
+    }
+
+    private void verifyMultiChoiceItemsContent(String[] expectedContent,
+            final boolean[] checkedTracker) {
+        final int expectedCount = expectedContent.length;
+
+        onView(withId(R.id.test_button)).perform(click());
+
+        final ListView listView = mAlertDialog.getListView();
+        assertNotNull("List view is shown", listView);
+
+        final ListAdapter listAdapter = listView.getAdapter();
+        assertEquals("List has " + expectedCount + " entries",
+                expectedCount, listAdapter.getCount());
+
+        // Test that all items are showing
+        onView(withText("Dialog title")).inRoot(isDialog()).check(matches(isDisplayed()));
+        verifyMultiChoiceItemsState(expectedContent, checkedTracker);
+
+        // We're going to click item #1 and test that the click listener has been invoked to
+        // update the original state array
+        boolean[] expectedAfterClick1 = checkedTracker.clone();
+        expectedAfterClick1[1] = !expectedAfterClick1[1];
+        DataInteraction interactionForClick = onData(allOf(
+                is(instanceOf(SQLiteCursor.class)),
+                TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[1])));
+        interactionForClick.inRoot(isDialog()).perform(click());
+        verifyMultiChoiceItemsState(expectedContent, expectedAfterClick1);
+
+        // Now click item #1 again and test that the click listener has been invoked to update the
+        // original state array again
+        expectedAfterClick1[1] = !expectedAfterClick1[1];
+        interactionForClick.inRoot(isDialog()).perform(click());
+        verifyMultiChoiceItemsState(expectedContent, expectedAfterClick1);
+
+        // Now we're going to click the last item and test that the click listener has been invoked
+        // to update the original state array
+        boolean[] expectedAfterClickLast = checkedTracker.clone();
+        expectedAfterClickLast[expectedCount - 1] = !expectedAfterClickLast[expectedCount - 1];
+        interactionForClick = onData(allOf(
+                is(instanceOf(SQLiteCursor.class)),
+                TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME,
+                        expectedContent[expectedCount - 1])));
+        interactionForClick.inRoot(isDialog()).perform(click());
+        verifyMultiChoiceItemsState(expectedContent, expectedAfterClickLast);
+    }
+
+    @SmallTest
+    public void testMultiChoiceItemsFromCursor() {
+        mCursor = mDatabase.query("test", mProjectionWithChecked,
+                null, null, null, null, null);
+        assertNotNull(mCursor);
+
+        final boolean[] checkedTracker = mCheckedContent.clone();
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.alert_dialog_title)
+                .setMultiChoiceItems(mCursor, CHECKED_COLUMN_NAME, TEXT_COLUMN_NAME,
+                        new DialogInterface.OnMultiChoiceClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which,
+                                    boolean isChecked) {
+                                // Update the underlying database with the new checked
+                                // state for the specific row
+                                mCursor.moveToPosition(which);
+                                ContentValues valuesToUpdate = new ContentValues();
+                                valuesToUpdate.put(CHECKED_COLUMN_NAME, isChecked ? 1 : 0);
+                                mDatabase.update("test", valuesToUpdate,
+                                        TEXT_COLUMN_NAME + " = ?",
+                                        new String[] { mCursor.getString(1) } );
+                                mCursor.requery();
+                                checkedTracker[which] = isChecked;
+                            }
+                        });
+        wireBuilder(builder);
+
+        // Pass the same boolean[] array as used for initialization since our click listener
+        // will be updating its content.
+        verifyMultiChoiceItemsContent(mTextContent, checkedTracker);
+    }
+
+    /**
+     * Helper method to verify the state of the single-choice items list. It gets the String
+     * array of content and verifies that:
+     *
+     * 1. The items in the array are rendered as CheckedTextViews inside a ListView
+     * 2. Each item in the array is displayed
+     * 3. Only one row in the ListView is checked, and that corresponds to the passed
+     *    integer index.
+     */
+    private void verifySingleChoiceItemsState(String[] expectedContent,
+            int currentlyExpectedSelectionIndex) {
+        final int expectedCount = expectedContent.length;
+
+        final ListView listView = mAlertDialog.getListView();
+        assertNotNull("List view is shown", listView);
+
+        final ListAdapter listAdapter = listView.getAdapter();
+        assertEquals("List has " + expectedCount + " entries",
+                expectedCount, listAdapter.getCount());
+
+        for (int i = 0; i < expectedCount; i++) {
+            Matcher checkedStateMatcher = (i == currentlyExpectedSelectionIndex) ?
+                    TestUtilsMatchers.isCheckedTextView() :
+                    TestUtilsMatchers.isNonCheckedTextView();
+            // Check that the corresponding row is rendered as CheckedTextView with expected
+            // checked state.
+            DataInteraction rowInteraction = onData(allOf(
+                    is(instanceOf(SQLiteCursor.class)),
+                    TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[i])));
+            rowInteraction.inRoot(isDialog()).
+                    check(matches(allOf(
+                            isDisplayed(),
+                            isAssignableFrom(CheckedTextView.class),
+                            isDescendantOfA(isAssignableFrom(ListView.class)),
+                            checkedStateMatcher)));
+        }
+    }
+
+    private void verifySingleChoiceItemsContent(String[] expectedContent,
+            int initialSelectionIndex) {
+        final int expectedCount = expectedContent.length;
+        int currentlyExpectedSelectionIndex = initialSelectionIndex;
+
+        onView(withId(R.id.test_button)).perform(click());
+
+        // Test that all items are showing
+        onView(withText("Dialog title")).inRoot(isDialog()).check(matches(isDisplayed()));
+        verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
+
+        // We're going to click the first unselected item and test that the click listener has
+        // been invoked.
+        currentlyExpectedSelectionIndex = (currentlyExpectedSelectionIndex == 0) ? 1 : 0;
+        DataInteraction interactionForClick = onData(allOf(
+                is(instanceOf(SQLiteCursor.class)),
+                TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME,
+                        expectedContent[currentlyExpectedSelectionIndex])));
+        interactionForClick.inRoot(isDialog()).perform(click());
+        assertEquals("Selected first single-choice item",
+                currentlyExpectedSelectionIndex, mClickedItemIndex);
+        verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
+
+        // Now click the same item again and test that the selection has not changed
+        interactionForClick.inRoot(isDialog()).perform(click());
+        assertEquals("Selected first single-choice item again",
+                currentlyExpectedSelectionIndex, mClickedItemIndex);
+        verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
+
+        // Now we're going to click the last item and test that the click listener has been invoked
+        // to update the original state array
+        currentlyExpectedSelectionIndex = expectedCount - 1;
+        interactionForClick = onData(allOf(
+                is(instanceOf(SQLiteCursor.class)),
+                TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME,
+                        expectedContent[currentlyExpectedSelectionIndex])));
+        interactionForClick.inRoot(isDialog()).perform(click());
+        assertEquals("Selected last single-choice item",
+                currentlyExpectedSelectionIndex, mClickedItemIndex);
+        verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
+    }
+
+    @SmallTest
+    public void testSingleChoiceItemsFromCursor() {
+        mCursor = mDatabase.query("test", mProjectionWithoutChecked,
+                null, null, null, null, null);
+        assertNotNull(mCursor);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.alert_dialog_title)
+                .setSingleChoiceItems(mCursor, 2, TEXT_COLUMN_NAME,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mClickedItemIndex = which;
+                            }
+                        });
+        wireBuilder(builder);
+
+        verifySingleChoiceItemsContent(mTextContent, 2);
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
index 28ed880..854adb5 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
@@ -17,6 +17,8 @@
 package android.support.v7.app;
 
 import android.support.v7.testutils.BaseTestActivity;
+import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.Test;
 
 public abstract class BaseBasicsTestCase<A extends BaseTestActivity>
@@ -27,16 +29,19 @@
     }
 
     @Test
+    @SmallTest
     public void testActionBarExists() {
         assertNotNull("ActionBar is not null", getActivity().getSupportActionBar());
     }
 
     @Test
+    @SmallTest
      public void testDefaultActionBarTitle() {
         assertEquals(getActivity().getTitle(), getActivity().getSupportActionBar().getTitle());
     }
 
     @Test
+    @SmallTest
     public void testSetActionBarTitle() throws Throwable {
         final String newTitle = "hello";
         runTestOnUiThread(new Runnable() {
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
index 91b791f..d0beb59 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyEventsTestCase.java
@@ -21,6 +21,8 @@
 
 import android.support.v7.appcompat.test.R;
 import android.support.v7.view.ActionMode;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -35,6 +37,7 @@
     }
 
     @Test
+    @SmallTest
     public void testBackDismissesActionMode() {
         final AtomicBoolean destroyed = new AtomicBoolean();
 
@@ -75,6 +78,7 @@
     }
 
     @Test
+    @SmallTest
     public void testBackCollapsesSearchView() throws InterruptedException {
         // First expand the SearchView
         getActivity().runOnUiThread(new Runnable() {
@@ -102,6 +106,7 @@
     }
 
     @Test
+    @SmallTest
     public void testMenuPressInvokesPanelCallbacks() throws InterruptedException {
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
         getInstrumentation().waitForIdleSync();
@@ -113,6 +118,7 @@
     }
 
     @Test
+    @SmallTest
     public void testBackPressWithMenuInvokesOnPanelClosed() throws InterruptedException {
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
         getInstrumentation().waitForIdleSync();
@@ -123,6 +129,7 @@
     }
 
     @Test
+    @MediumTest
     public void testBackPressWithEmptyMenuDestroysActivity() throws InterruptedException {
         repopulateWithEmptyMenu();
 
@@ -134,6 +141,7 @@
     }
 
     @Test
+    @SmallTest
     public void testDelKeyEventReachesActivity() {
         // First send the event
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_DEL);
@@ -149,6 +157,7 @@
     }
 
     @Test
+    @SmallTest
     public void testMenuKeyEventReachesActivity() throws InterruptedException {
         getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
         getInstrumentation().waitForIdleSync();
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
index 73d1e61..b118eec 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseKeyboardShortcutsTestCase.java
@@ -21,6 +21,7 @@
 
 import android.os.SystemClock;
 import android.support.v7.appcompat.test.R;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MenuItem;
@@ -33,6 +34,7 @@
     }
 
     @Test
+    @SmallTest
     public void testAlphabeticCtrlShortcut() {
         testKeyboardShortcut(KeyEvent.KEYCODE_A,
                 KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON,
diff --git a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
index e1f9aa2..785dcc1 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
@@ -28,6 +28,7 @@
 import android.support.v7.widget.AppCompatRadioButton;
 import android.support.v7.widget.AppCompatRatingBar;
 import android.support.v7.widget.AppCompatSpinner;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -38,6 +39,7 @@
     }
 
     @Test
+    @SmallTest
     public void testAndroidThemeInflation() throws Throwable {
         if (Build.VERSION.SDK_INT < 10) {
             // Ignore this test if running on Gingerbread or below
@@ -54,6 +56,7 @@
     }
 
     @Test
+    @SmallTest
     public void testAppThemeInflation() throws Throwable {
         if (Build.VERSION.SDK_INT < 10) {
             // Ignore this test if running on Gingerbread or below
@@ -70,42 +73,50 @@
     }
 
     @Test
+    @SmallTest
     public void testSpinnerInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_spinner, AppCompatSpinner.class);
     }
 
     @Test
+    @SmallTest
     public void testEditTextInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_edittext, AppCompatEditText.class);
     }
 
     @Test
+    @SmallTest
     public void testButtonInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_button, AppCompatButton.class);
     }
 
     @Test
+    @SmallTest
     public void testRadioButtonInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_radiobutton, AppCompatRadioButton.class);
     }
 
     @Test
+    @SmallTest
     public void testCheckBoxInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_checkbox, AppCompatCheckBox.class);
     }
 
     @Test
+    @SmallTest
     public void testActvInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_actv, AppCompatAutoCompleteTextView.class);
     }
 
     @Test
+    @SmallTest
     public void testMactvInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_mactv,
                 AppCompatMultiAutoCompleteTextView.class);
     }
 
     @Test
+    @SmallTest
     public void testRatingBarInflation() throws Throwable {
         testAppCompatWidgetInflation(R.layout.layout_ratingbar, AppCompatRatingBar.class);
     }
diff --git a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
index 319583e..2b75600 100644
--- a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
+++ b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
@@ -16,9 +16,11 @@
 
 package android.support.v7.testutils;
 
+import android.database.sqlite.SQLiteCursor;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.test.espresso.matcher.BoundedMatcher;
+import android.text.TextUtils;
 import android.view.View;
 import android.widget.CheckedTextView;
 import android.widget.ImageView;
@@ -117,4 +119,24 @@
             }
         };
     }
+
+    /**
+     * Returns a matcher that matches data entry in <code>SQLiteCursor</code> that has
+     * the specified text in the specified column.
+     */
+    public static Matcher<Object> withCursorItemContent(final String columnName,
+            final String expectedText) {
+        return new BoundedMatcher<Object, SQLiteCursor>(SQLiteCursor.class) {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("doesn't match " + expectedText);
+            }
+
+            @Override
+            protected boolean matchesSafely(SQLiteCursor cursor) {
+                return TextUtils.equals(expectedText,
+                        cursor.getString(cursor.getColumnIndex(columnName)));
+            }
+        };
+    }
 }
diff --git a/v7/gridlayout/tests/AndroidManifest.xml b/v7/gridlayout/tests/AndroidManifest.xml
index c55a49a..bc50c89 100644
--- a/v7/gridlayout/tests/AndroidManifest.xml
+++ b/v7/gridlayout/tests/AndroidManifest.xml
@@ -24,7 +24,5 @@
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.support.v7.gridlayout.test"
-                     android:label="GridLayout Tests" />
-
+                     android:targetPackage="android.support.v7.gridlayout.test"/>
 </manifest>
diff --git a/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java b/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java
index 1a88be5..aa81870 100644
--- a/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java
+++ b/v7/gridlayout/tests/src/android/support/v7/widget/test/GridLayoutTest.java
@@ -20,7 +20,8 @@
 import android.os.Debug;
 import android.support.v7.widget.GridLayout;
 import android.test.ActivityInstrumentationTestCase2;
-import android.support.v7.gridlayout.R;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.support.v7.gridlayout.test.R;
 import android.test.UiThreadTest;
 import android.view.Gravity;
 import android.view.View;
@@ -30,6 +31,7 @@
 /**
  * @hide
  */
+@SmallTest
 public class GridLayoutTest extends ActivityInstrumentationTestCase2 {
 
     public GridLayoutTest() {
diff --git a/v7/palette/src/androidTest/NO_DOCS b/v7/palette/src/androidTest/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/palette/src/androidTest/NO_DOCS
@@ -0,0 +1,17 @@
+# Copyright (C) 2015 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/recyclerview/api/current.txt b/v7/recyclerview/api/current.txt
index 6bc9576..d74c0e5 100644
--- a/v7/recyclerview/api/current.txt
+++ b/v7/recyclerview/api/current.txt
@@ -265,6 +265,8 @@
     method public int computeVerticalScrollRange();
     method public boolean drawChild(android.graphics.Canvas, android.view.View, long);
     method public android.view.View findChildViewUnder(float, float);
+    method public android.view.View findContainingItemView(android.view.View);
+    method public android.support.v7.widget.RecyclerView.ViewHolder findContainingViewHolder(android.view.View);
     method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForAdapterPosition(int);
     method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForItemId(long);
     method public android.support.v7.widget.RecyclerView.ViewHolder findViewHolderForLayoutPosition(int);
@@ -469,6 +471,7 @@
     method public void detachView(android.view.View);
     method public void detachViewAt(int);
     method public void endAnimation(android.view.View);
+    method public android.view.View findContainingItemView(android.view.View);
     method public android.view.View findViewByPosition(int);
     method public abstract android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
     method public android.support.v7.widget.RecyclerView.LayoutParams generateLayoutParams(android.view.ViewGroup.LayoutParams);
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index c9f1a22..c556fc5 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -5,9 +5,16 @@
 dependencies {
     compile project(':support-v4')
     compile project(':support-annotations')
-    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'
+    androidTestCompile "org.mockito:mockito-core:1.9.5"
+    androidTestCompile "com.google.dexmaker:dexmaker:1.2"
+    androidTestCompile "com.google.dexmaker:dexmaker-mockito:1.2"
 }
 
 android {
@@ -43,6 +50,10 @@
     testOptions {
         unitTests.returnDefaultValues = true
     }
+
+    buildTypes.all {
+        consumerProguardFiles 'proguard-rules.pro'
+    }
 }
 
 android.libraryVariants.all { variant ->
@@ -78,17 +89,6 @@
     artifacts.add('archives', sourcesJarTask);
 }
 
-// TODO make this generic for all projects
-afterEvaluate {
-    def originalTask = tasks['packageDebugAndroidTest']
-    tasks['assembleDebugAndroidTest'].doLast {
-        copy {
-            from(originalTask.outputFile)
-            into(rootProject.ext.testApkDistOut)
-        }
-    }
-}
-
 uploadArchives {
     repositories {
         mavenDeployer {
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java
index b3ab064..e803d2a 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListAdapterCallbackWrapperTest.java
@@ -22,6 +22,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import android.test.suitebuilder.annotation.SmallTest;
+
 import static android.support.v7.util.SortedList.BatchedCallback.TYPE_NONE;
 import static android.support.v7.util.SortedList.BatchedCallback.TYPE_ADD;
 import static android.support.v7.util.SortedList.BatchedCallback.TYPE_REMOVE;
@@ -29,6 +31,7 @@
 import static android.support.v7.util.SortedList.BatchedCallback.TYPE_MOVE;
 
 @RunWith(JUnit4.class)
+@SmallTest
 public class SortedListAdapterCallbackWrapperTest extends TestCase {
 
     private int lastReceivedType = TYPE_NONE;
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java
index 03c1151..9bcae5c 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/util/SortedListTest.java
@@ -23,6 +23,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import android.test.suitebuilder.annotation.SmallTest;
+
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -31,6 +33,7 @@
 import java.util.Random;
 
 @RunWith(JUnit4.class)
+@SmallTest
 public class SortedListTest extends TestCase {
 
     SortedList<Item> mList;
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
index ba6ec71..09f7336 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/AdapterHelperTest.java
@@ -25,6 +25,7 @@
 import org.junit.runners.JUnit4;
 
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.view.View;
 import android.widget.TextView;
@@ -39,6 +40,7 @@
 import static android.support.v7.widget.RecyclerView.*;
 
 @RunWith(JUnit4.class)
+@SmallTest
 public class AdapterHelperTest extends AndroidTestCase {
 
     private static final boolean DEBUG = false;
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java
index 06bfce6..884cdd5 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/OpReorderTest.java
@@ -28,6 +28,7 @@
 import java.util.Set;
 
 import android.support.v7.widget.AdapterHelper.UpdateOp;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
 import static android.support.v7.widget.AdapterHelper.UpdateOp.ADD;
@@ -37,6 +38,7 @@
 import static org.junit.Assert.*;
 
 @RunWith(JUnit4.class)
+@SmallTest
 public class OpReorderTest {
 
     private static final String TAG = "OpReorderTest";
diff --git a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java
index 559bc6b..f460f79 100644
--- a/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java
+++ b/v7/recyclerview/jvm-tests/src/android/support/v7/widget/ViewInfoStoreTest.java
@@ -26,6 +26,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.util.Pair;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 
 import java.util.ArrayList;
@@ -42,6 +43,7 @@
 
 @SuppressWarnings("ConstantConditions")
 @RunWith(JUnit4.class)
+@SmallTest
 public class ViewInfoStoreTest extends TestCase {
     ViewInfoStore mStore;
     LoggingProcessCallback mCallback;
diff --git a/v7/recyclerview/proguard-rules.pro b/v7/recyclerview/proguard-rules.pro
new file mode 100644
index 0000000..113d94d
--- /dev/null
+++ b/v7/recyclerview/proguard-rules.pro
@@ -0,0 +1,19 @@
+# Copyright (C) 2015 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.
+
+# When layoutManager xml attribute is used, RecyclerView inflates
+#LayoutManagers' constructors using reflection.
+-keep public class * extends android.support.v7.widget.RecyclerView$LayoutManager {
+    public <init>(...);
+}
\ No newline at end of file
diff --git a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
index 7dcaea0..e182c93 100644
--- a/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/GridLayoutManager.java
@@ -842,6 +842,78 @@
     }
 
     @Override
+    public View onFocusSearchFailed(View focused, int focusDirection,
+            RecyclerView.Recycler recycler, RecyclerView.State state) {
+        View prevFocusedChild = findContainingItemView(focused);
+        if (prevFocusedChild == null) {
+            return null;
+        }
+        LayoutParams lp = (LayoutParams) prevFocusedChild.getLayoutParams();
+        final int prevSpanStart = lp.mSpanIndex;
+        final int prevSpanEnd = lp.mSpanIndex + lp.mSpanSize;
+        View view = super.onFocusSearchFailed(focused, focusDirection, recycler, state);
+        if (view == null) {
+            return null;
+        }
+        // LinearLayoutManager finds the last child. What we want is the child which has the same
+        // spanIndex.
+        final int layoutDir = convertFocusDirectionToLayoutDirection(focusDirection);
+        final boolean ascend = (layoutDir == LayoutState.LAYOUT_END) != mShouldReverseLayout;
+        final int start, inc, limit;
+        if (ascend) {
+            start = getChildCount() - 1;
+            inc = -1;
+            limit = -1;
+        } else {
+            start = 0;
+            inc = 1;
+            limit = getChildCount();
+        }
+        final boolean preferLastSpan = mOrientation == VERTICAL && isLayoutRTL();
+        View weakCandidate = null; // somewhat matches but not strong
+        int weakCandidateSpanIndex = -1;
+        int weakCandidateOverlap = 0; // how many spans overlap
+
+        for (int i = start; i != limit; i += inc) {
+            View candidate = getChildAt(i);
+            if (candidate == prevFocusedChild) {
+                break;
+            }
+            if (!candidate.isFocusable()) {
+                continue;
+            }
+            final LayoutParams candidateLp = (LayoutParams) candidate.getLayoutParams();
+            final int candidateStart = candidateLp.mSpanIndex;
+            final int candidateEnd = candidateLp.mSpanIndex + candidateLp.mSpanSize;
+            if (candidateStart == prevSpanStart && candidateEnd == prevSpanEnd) {
+                return candidate; // perfect match
+            }
+            boolean assignAsWeek = false;
+            if (weakCandidate == null) {
+                assignAsWeek = true;
+            } else {
+                int maxStart = Math.max(candidateStart, prevSpanStart);
+                int minEnd = Math.min(candidateEnd, prevSpanEnd);
+                int overlap = minEnd - maxStart;
+                if (overlap > weakCandidateOverlap) {
+                    assignAsWeek = true;
+                } else if (overlap == weakCandidateOverlap &&
+                        preferLastSpan == (candidateStart > weakCandidateSpanIndex)) {
+                    assignAsWeek = true;
+                }
+            }
+
+            if (assignAsWeek) {
+                weakCandidate = candidate;
+                weakCandidateSpanIndex = candidateLp.mSpanIndex;
+                weakCandidateOverlap = Math.min(candidateEnd, prevSpanEnd) -
+                        Math.max(candidateStart, prevSpanStart);
+            }
+        }
+        return weakCandidate;
+    }
+
+    @Override
     public boolean supportsPredictiveItemAnimations() {
         return mPendingSavedState == null && !mPendingSpanCountChange;
     }
diff --git a/v7/recyclerview/src/android/support/v7/widget/LayoutState.java b/v7/recyclerview/src/android/support/v7/widget/LayoutState.java
index f58a4a7..2402313 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LayoutState.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LayoutState.java
@@ -70,6 +70,11 @@
     int mEndLine = 0;
 
     /**
+     * If true, layout should stop if a focusable view is added
+     */
+    boolean mStopInFocusable;
+
+    /**
      * @return true if there are more items in the data adapter
      */
     boolean hasMore(RecyclerView.State state) {
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index b896902..bef7d6a 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -58,7 +58,7 @@
      * than this factor times the total space of the list. If layout is vertical, total space is the
      * height minus padding, if layout is horizontal, total space is the width minus padding.
      */
-    private static final float MAX_SCROLL_FACTOR = 0.33f;
+    private static final float MAX_SCROLL_FACTOR = 1 / 3f;
 
 
     /**
@@ -1449,7 +1449,7 @@
      * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
      * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
      */
-    private int convertFocusDirectionToLayoutDirection(int focusDirection) {
+    int convertFocusDirectionToLayoutDirection(int focusDirection) {
         switch (focusDirection) {
             case View.FOCUS_BACKWARD:
                 return LayoutState.LAYOUT_START;
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 5808604..edf9a45 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -3472,6 +3472,44 @@
         return getChildViewHolderInt(child);
     }
 
+    /**
+     * Traverses the ascendants of the given view and returns the item view that contains it and
+     * also a direct child of the RecyclerView. This returned view can be used to get the
+     * ViewHolder by calling {@link #getChildViewHolder(View)}.
+     *
+     * @param view The view that is a descendant of the RecyclerView.
+     *
+     * @return The direct child of the RecyclerView which contains the given view or null if the
+     * provided view is not a descendant of this RecyclerView.
+     *
+     * @see #getChildViewHolder(View)
+     * @see #findContainingViewHolder(View)
+     */
+    @Nullable
+    public View findContainingItemView(View view) {
+        ViewParent parent = view.getParent();
+        while (parent != null && parent != this && parent instanceof View) {
+            view = (View) parent;
+            parent = view.getParent();
+        }
+        return parent == this ? view : null;
+    }
+
+    /**
+     * Returns the ViewHolder that contains the given view.
+     *
+     * @param view The view that is a descendant of the RecyclerView.
+     *
+     * @return The ViewHolder that contains the given view or null if the provided view is not a
+     * descendant of this RecyclerView.
+     */
+    @Nullable
+    public ViewHolder findContainingViewHolder(View view) {
+        View itemView = findContainingItemView(view);
+        return itemView == null ? null : getChildViewHolder(itemView);
+    }
+
+
     static ViewHolder getChildViewHolderInt(View child) {
         if (child == null) {
             return null;
@@ -6409,6 +6447,36 @@
         }
 
         /**
+         * Traverses the ascendants of the given view and returns the item view that contains it
+         * and also a direct child of the LayoutManager.
+         * <p>
+         * Note that this method may return null if the view is a child of the RecyclerView but
+         * not a child of the LayoutManager (e.g. running a disappear animation).
+         *
+         * @param view The view that is a descendant of the LayoutManager.
+         *
+         * @return The direct child of the LayoutManager which contains the given view or null if
+         * the provided view is not a descendant of this LayoutManager.
+         *
+         * @see RecyclerView#getChildViewHolder(View)
+         * @see RecyclerView#findContainingViewHolder(View)
+         */
+        @Nullable
+        public View findContainingItemView(View view) {
+            if (mRecyclerView == null) {
+                return null;
+            }
+            View found = mRecyclerView.findContainingItemView(view);
+            if (found == null) {
+                return null;
+            }
+            if (mChildHelper.isHidden(found)) {
+                return null;
+            }
+            return found;
+        }
+
+        /**
          * Finds the view which represents the given adapter position.
          * <p>
          * This method traverses each child since it has no information about child order.
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index 071bb4e..3147d24 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.support.annotation.Nullable;
 import android.support.v4.view.ViewCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -29,6 +30,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 
 import java.util.ArrayList;
@@ -65,6 +67,7 @@
      */
     public static final int GAP_HANDLING_NONE = 0;
 
+    @SuppressWarnings("unused")
     @Deprecated
     public static final int GAP_HANDLING_LAZY = 1;
 
@@ -90,6 +93,12 @@
     public static final int GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS = 2;
 
     private static final int INVALID_OFFSET = Integer.MIN_VALUE;
+    /**
+     * While trying to find next view to focus, LayoutManager will not try to scroll more
+     * than this factor times the total space of the list. If layout is vertical, total space is the
+     * height minus padding, if layout is horizontal, total space is the width minus padding.
+     */
+    private static final float MAX_SCROLL_FACTOR = 1 / 3f;
 
     /**
      * Number of spans
@@ -205,6 +214,7 @@
      * Constructor used when layout manager is set in XML by RecyclerView attribute
      * "layoutManager". Defaults to single column and vertical.
      */
+    @SuppressWarnings("unused")
     public StaggeredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
@@ -1298,6 +1308,7 @@
             mLayoutState.mEndLine = mPrimaryOrientation.getEnd() + endExtra;
             mLayoutState.mStartLine = -startExtra;
         }
+        mLayoutState.mStopInFocusable = false;
     }
 
     private void setLayoutStateDirection(int direction) {
@@ -1493,7 +1504,6 @@
                         mLaidOutInvalidFullSpan = true;
                     }
                 }
-
             }
             attachViewToSpans(view, lp, layoutState);
             final int otherStart = lp.mFullSpan ? mSecondaryOrientation.getStartAfterPadding()
@@ -1512,6 +1522,13 @@
                 updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
             }
             recycle(recycler, mLayoutState);
+            if (mLayoutState.mStopInFocusable && view.isFocusable()) {
+                if (lp.mFullSpan) {
+                    mRemainingSpans.clear();
+                } else {
+                    mRemainingSpans.set(currentSpan.mIndex, false);
+                }
+            }
             added = true;
         }
         if (!added) {
@@ -1563,6 +1580,9 @@
     }
 
     private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState) {
+        if (layoutState.mStopInFocusable) {
+            return;
+        }
         if (layoutState.mAvailable == 0) {
             // easy, recycle line is still valid
             if (layoutState.mLayoutDirection == LAYOUT_START) {
@@ -2014,6 +2034,104 @@
         return mOrientation;
     }
 
+    @Nullable
+    @Override
+    public View onFocusSearchFailed(View focused, int direction, RecyclerView.Recycler recycler,
+            RecyclerView.State state) {
+        if (getChildCount() == 0) {
+            return null;
+        }
+
+        View directChild = findContainingItemView(focused);
+        if (directChild == null) {
+            return null;
+        }
+
+        ensureOrientationHelper();
+        resolveShouldLayoutReverse();
+        final int layoutDir = convertFocusDirectionToLayoutDirection(direction);
+        if (layoutDir == LayoutState.INVALID_LAYOUT) {
+            return null;
+        }
+        LayoutParams prevFocusLayoutParams = (LayoutParams) focused.getLayoutParams();
+        boolean prevFocusFullSpan = prevFocusLayoutParams.mFullSpan;
+        final Span prevFocusSpan = prevFocusLayoutParams.mSpan;
+        final int referenceChildPosition;
+        if (layoutDir == LAYOUT_END) { // layout towards end
+            referenceChildPosition = getLastChildPosition();
+        } else {
+            referenceChildPosition = getFirstChildPosition();
+        }
+        updateLayoutState(referenceChildPosition, state);
+        setLayoutStateDirection(layoutDir);
+
+        mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
+        mLayoutState.mAvailable = (int) (MAX_SCROLL_FACTOR * mPrimaryOrientation.getTotalSpace());
+        mLayoutState.mStopInFocusable = true;
+        fill(recycler, mLayoutState, state);
+        mLastLayoutFromEnd = mShouldReverseLayout;
+        if (!prevFocusFullSpan) {
+            View view = prevFocusSpan.getFocusableViewAfter(referenceChildPosition, layoutDir);
+            if (view != null && view != focused) {
+                return view;
+            }
+        }
+        // either could not find from the desired span or prev view is full span.
+        // traverse all spans
+        if (preferLastSpan(layoutDir)) {
+            for (int i = mSpanCount - 1; i >= 0; i --) {
+                View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
+                if (view != null && view != focused) {
+                    return view;
+                }
+            }
+        } else {
+            for (int i = 0; i < mSpanCount; i ++) {
+                View view = mSpans[i].getFocusableViewAfter(referenceChildPosition, layoutDir);
+                if (view != null && view != focused) {
+                    return view;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Converts a focusDirection to orientation.
+     *
+     * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+     *                       {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},
+     *                       {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}
+     *                       or 0 for not applicable
+     * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction
+     * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.
+     */
+    private int convertFocusDirectionToLayoutDirection(int focusDirection) {
+        switch (focusDirection) {
+            case View.FOCUS_BACKWARD:
+                return LayoutState.LAYOUT_START;
+            case View.FOCUS_FORWARD:
+                return LayoutState.LAYOUT_END;
+            case View.FOCUS_UP:
+                return mOrientation == VERTICAL ? LayoutState.LAYOUT_START
+                        : LayoutState.INVALID_LAYOUT;
+            case View.FOCUS_DOWN:
+                return mOrientation == VERTICAL ? LayoutState.LAYOUT_END
+                        : LayoutState.INVALID_LAYOUT;
+            case View.FOCUS_LEFT:
+                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_START
+                        : LayoutState.INVALID_LAYOUT;
+            case View.FOCUS_RIGHT:
+                return mOrientation == HORIZONTAL ? LayoutState.LAYOUT_END
+                        : LayoutState.INVALID_LAYOUT;
+            default:
+                if (DEBUG) {
+                    Log.d(TAG, "Unknown focus request:" + focusDirection);
+                }
+                return LayoutState.INVALID_LAYOUT;
+        }
+
+    }
 
     /**
      * LayoutParams used by StaggeredGridLayoutManager.
@@ -2094,7 +2212,7 @@
     class Span {
 
         static final int INVALID_LINE = Integer.MIN_VALUE;
-        private ArrayList<View> mViews = new ArrayList<View>();
+        private ArrayList<View> mViews = new ArrayList<>();
         int mCachedStart = INVALID_LINE;
         int mCachedEnd = INVALID_LINE;
         int mDeletedSize = 0;
@@ -2278,45 +2396,6 @@
             }
         }
 
-        // normalized offset is how much this span can scroll
-        int getNormalizedOffset(int dt, int targetStart, int targetEnd) {
-            if (mViews.size() == 0) {
-                return 0;
-            }
-            if (dt < 0) {
-                final int endSpace = getEndLine() - targetEnd;
-                if (endSpace <= 0) {
-                    return 0;
-                }
-                return -dt > endSpace ? -endSpace : dt;
-            } else {
-                final int startSpace = targetStart - getStartLine();
-                if (startSpace <= 0) {
-                    return 0;
-                }
-                return startSpace < dt ? startSpace : dt;
-            }
-        }
-
-        /**
-         * Returns if there is no child between start-end lines
-         *
-         * @param start The start line
-         * @param end   The end line
-         * @return true if a new child can be added between start and end
-         */
-        boolean isEmpty(int start, int end) {
-            final int count = mViews.size();
-            for (int i = 0; i < count; i++) {
-                final View view = mViews.get(i);
-                if (mPrimaryOrientation.getDecoratedStart(view) < end &&
-                        mPrimaryOrientation.getDecoratedEnd(view) > start) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
         public int findFirstVisibleItemPosition() {
             return mReverseLayout
                     ? findOneVisibleChild(mViews.size() - 1, -1, false)
@@ -2361,6 +2440,36 @@
             }
             return NO_POSITION;
         }
+
+        /**
+         * Depending on the layout direction, returns the View that is after the given position.
+         */
+        public View getFocusableViewAfter(int referenceChildPosition, int layoutDir) {
+            View candidate = null;
+            if (layoutDir == LAYOUT_START) {
+                final int limit = mViews.size();
+                for (int i = 0; i < limit; i++) {
+                    final View view = mViews.get(i);
+                    if (view.isFocusable() &&
+                            (getPosition(view) > referenceChildPosition == mReverseLayout) ) {
+                        candidate = view;
+                    } else {
+                        break;
+                    }
+                }
+            } else {
+                for (int i = mViews.size() - 1; i >= 0; i--) {
+                    final View view = mViews.get(i);
+                    if (view.isFocusable() &&
+                            (getPosition(view) > referenceChildPosition == !mReverseLayout)) {
+                        candidate = view;
+                    } else {
+                        break;
+                    }
+                }
+            }
+            return candidate;
+        }
     }
 
     /**
@@ -2537,7 +2646,7 @@
 
         public void addFullSpanItem(FullSpanItem fullSpanItem) {
             if (mFullSpanItems == null) {
-                mFullSpanItems = new ArrayList<FullSpanItem>();
+                mFullSpanItems = new ArrayList<>();
             }
             final int size = mFullSpanItems.size();
             for (int i = 0; i < size; i++) {
@@ -2629,10 +2738,6 @@
                 return mGapPerSpan == null ? 0 : mGapPerSpan[spanIndex];
             }
 
-            public void invalidateSpanGaps() {
-                mGapPerSpan = null;
-            }
-
             @Override
             public int describeContents() {
                 return 0;
@@ -2712,6 +2817,7 @@
             mReverseLayout = in.readInt() == 1;
             mAnchorLayoutFromEnd = in.readInt() == 1;
             mLastLayoutRTL = in.readInt() == 1;
+            //noinspection unchecked
             mFullSpanItems = in.readArrayList(
                     LazySpanLookup.FullSpanItem.class.getClassLoader());
         }
diff --git a/v7/recyclerview/tests/AndroidManifest.xml b/v7/recyclerview/tests/AndroidManifest.xml
index 5047517..69ff69c 100644
--- a/v7/recyclerview/tests/AndroidManifest.xml
+++ b/v7/recyclerview/tests/AndroidManifest.xml
@@ -17,16 +17,17 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           package="android.support.v7.recyclerview.test">
-    <uses-sdk android:minSdkVersion="7" tools:overrideLibrary="android.support.test, 
-            android.support.test.espresso, android.support.test.espresso.idling"/>
+    <uses-sdk android:minSdkVersion="7" tools:overrideLibrary="android.support.test,
+                      android.app, android.support.test.rule, android.support.test.espresso,
+                      android.support.test.espresso.idling"/>
 
     <application>
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
         <activity android:name="android.support.v7.widget.test.RecyclerViewTestActivity"/>
         <activity android:name="android.support.v7.widget.TestActivity"/>
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
                      android:targetPackage="android.support.v7.recyclerview.test"
-                     />
+    />
 </manifest>
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
index f985898..8679043 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/AsyncListUtilTest.java
@@ -17,11 +17,13 @@
 package android.support.v7.util;
 
 import android.support.annotation.UiThread;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.SparseBooleanArray;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+@MediumTest
 public class AsyncListUtilTest extends BaseThreadedTest {
 
     private static final int TILE_SIZE = 10;
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java b/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java
index 28c14d0..044f9d4 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/MessageQueueTest.java
@@ -17,6 +17,7 @@
 package android.support.v7.util;
 
 import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -28,6 +29,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class MessageQueueTest {
     MessageThreadUtil.MessageQueue mQueue;
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java b/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java
index 472374d..f5f32b8 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/ThreadUtilTest.java
@@ -21,12 +21,14 @@
 
 import android.os.Looper;
 import android.support.annotation.UiThread;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
+@MediumTest
 public class ThreadUtilTest extends BaseThreadedTest {
     Map<String, LockedObject> results = new HashMap<>();
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java b/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java
index 42ddc22..ada1694 100644
--- a/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/util/TileListTest.java
@@ -18,6 +18,7 @@
 
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.util.TileList;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import static org.hamcrest.MatcherAssert.*;
 import static org.hamcrest.CoreMatchers.*;
@@ -26,6 +27,7 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class TileListTest {
     int mTileSize = 3;
     TileList<Integer> mTileList;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
index b8f6788..cc23c68 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/AsyncListUtilLayoutTest.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.support.v7.util.AsyncListUtil;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -26,6 +27,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+@MediumTest
 public class AsyncListUtilLayoutTest extends BaseRecyclerViewInstrumentationTest {
 
     private static final boolean DEBUG = false;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
new file mode 100644
index 0000000..201532a
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseLinearLayoutManagerTest.java
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2015 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.v7.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+public class BaseLinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
+
+    protected static final boolean DEBUG = false;
+    protected static final String TAG = "LinearLayoutManagerTest";
+
+    protected static List<Config> createBaseVariations() {
+        List<Config> variations = new ArrayList<>();
+        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
+            for (boolean reverseLayout : new boolean[]{false, true}) {
+                for (boolean stackFromBottom : new boolean[]{false, true}) {
+                    variations.add(new Config(orientation, reverseLayout, stackFromBottom));
+                }
+            }
+        }
+        return variations;
+    }
+
+    WrappedLinearLayoutManager mLayoutManager;
+    TestAdapter mTestAdapter;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    protected static List<Config> addConfigVariation(List<Config> base, String fieldName,
+            Object... variations)
+            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
+        List<Config> newConfigs = new ArrayList<Config>();
+        Field field = Config.class.getDeclaredField(fieldName);
+        for (Config config : base) {
+            for (Object variation : variations) {
+                Config newConfig = (Config) config.clone();
+                field.set(newConfig, variation);
+                newConfigs.add(newConfig);
+            }
+        }
+        return newConfigs;
+    }
+
+    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
+        mRecyclerView = inflateWrappedRV();
+
+        mRecyclerView.setHasFixedSize(true);
+        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
+                : config.mTestAdapter;
+        mRecyclerView.setAdapter(mTestAdapter);
+        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
+                config.mReverseLayout);
+        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
+        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+        if (waitForFirstLayout) {
+            waitForFirstLayout();
+        }
+    }
+
+    public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset)
+            throws Throwable {
+        setupByConfig(new Config(VERTICAL, false, false), true);
+
+        mLayoutManager.mOnLayoutListener = new OnLayoutListener() {
+            @Override
+            void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
+                if (state.isPreLayout()) {
+                    assertEquals("pending scroll position should still be pending",
+                            scrollPosition, mLayoutManager.mPendingScrollPosition);
+                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
+                        assertEquals("pending scroll position offset should still be pending",
+                                scrollOffset, mLayoutManager.mPendingScrollPositionOffset);
+                    }
+                } else {
+                    RecyclerView.ViewHolder vh =
+                            mRecyclerView.findViewHolderForLayoutPosition(scrollPosition);
+                    assertNotNull("scroll to position should work", vh);
+                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
+                        assertEquals("scroll offset should be applied properly",
+                                mLayoutManager.getPaddingTop() + scrollOffset +
+                                        ((RecyclerView.LayoutParams) vh.itemView
+                                                .getLayoutParams()).topMargin,
+                                mLayoutManager.getDecoratedTop(vh.itemView));
+                    }
+                }
+            }
+        };
+        mLayoutManager.expectLayouts(2);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    mTestAdapter.addAndNotify(0, 1);
+                    if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) {
+                        mLayoutManager.scrollToPosition(scrollPosition);
+                    } else {
+                        mLayoutManager.scrollToPositionWithOffset(scrollPosition,
+                                scrollOffset);
+                    }
+
+                } catch (Throwable throwable) {
+                    throwable.printStackTrace();
+                }
+
+            }
+        });
+        mLayoutManager.waitForLayout(2);
+        checkForMainThreadException();
+    }
+
+    protected void waitForFirstLayout() throws Throwable {
+        mLayoutManager.expectLayouts(1);
+        setRecyclerView(mRecyclerView);
+        mLayoutManager.waitForLayout(2);
+    }
+
+    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mLayoutManager.scrollToPositionWithOffset(position, offset);
+            }
+        });
+    }
+
+    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
+            Map<Item, Rect> after, boolean strictItemEquality) {
+        Throwable throwable = null;
+        try {
+            assertRectSetsEqual("NOT " + message, before, after, strictItemEquality);
+        } catch (Throwable t) {
+            throwable = t;
+        }
+        assertNotNull(message + "\ntwo layout should be different", throwable);
+    }
+
+    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
+        assertRectSetsEqual(message, before, after, true);
+    }
+
+    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after,
+            boolean strictItemEquality) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("checking rectangle equality.\n");
+        sb.append("before:\n");
+        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
+            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
+        }
+        sb.append("after:\n");
+        for (Map.Entry<Item, Rect> entry : after.entrySet()) {
+            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
+        }
+        message = message + "\n" + sb.toString();
+        assertEquals(message + ":\nitem counts should be equal", before.size()
+                , after.size());
+        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
+            final Item beforeItem = entry.getKey();
+            Rect afterRect = null;
+            if (strictItemEquality) {
+                afterRect = after.get(beforeItem);
+                assertNotNull(message + ":\nSame item should be visible after simple re-layout",
+                        afterRect);
+            } else {
+                for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) {
+                    final Item afterItem = afterEntry.getKey();
+                    if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) {
+                        afterRect = afterEntry.getValue();
+                        break;
+                    }
+                }
+                assertNotNull(message + ":\nItem with same adapter index should be visible " +
+                                "after simple re-layout",
+                        afterRect);
+            }
+            assertEquals(message + ":\nItem should be laid out at the same coordinates",
+                    entry.getValue(), afterRect);
+        }
+    }
+
+    static class VisibleChildren {
+
+        int firstVisiblePosition = RecyclerView.NO_POSITION;
+
+        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
+
+        int lastVisiblePosition = RecyclerView.NO_POSITION;
+
+        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
+
+        @Override
+        public String toString() {
+            return "VisibleChildren{" +
+                    "firstVisiblePosition=" + firstVisiblePosition +
+                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
+                    ", lastVisiblePosition=" + lastVisiblePosition +
+                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
+                    '}';
+        }
+    }
+
+    static class OnLayoutListener {
+
+        void before(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        }
+
+        void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
+        }
+    }
+
+    static class Config implements Cloneable {
+
+        static final int DEFAULT_ITEM_COUNT = 100;
+
+        boolean mStackFromEnd;
+
+        int mOrientation = VERTICAL;
+
+        boolean mReverseLayout = false;
+
+        boolean mRecycleChildrenOnDetach = false;
+
+        int mItemCount = DEFAULT_ITEM_COUNT;
+
+        TestAdapter mTestAdapter;
+
+        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
+            mOrientation = orientation;
+            mReverseLayout = reverseLayout;
+            mStackFromEnd = stackFromEnd;
+        }
+
+        public Config() {
+
+        }
+
+        Config adapter(TestAdapter adapter) {
+            mTestAdapter = adapter;
+            return this;
+        }
+
+        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
+            mRecycleChildrenOnDetach = recycleChildrenOnDetach;
+            return this;
+        }
+
+        Config orientation(int orientation) {
+            mOrientation = orientation;
+            return this;
+        }
+
+        Config stackFromBottom(boolean stackFromBottom) {
+            mStackFromEnd = stackFromBottom;
+            return this;
+        }
+
+        Config reverseLayout(boolean reverseLayout) {
+            mReverseLayout = reverseLayout;
+            return this;
+        }
+
+        public Config itemCount(int itemCount) {
+            mItemCount = itemCount;
+            return this;
+        }
+
+        // required by convention
+        @Override
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+
+        @Override
+        public String toString() {
+            return "Config{" +
+                    "mStackFromEnd=" + mStackFromEnd +
+                    ", mOrientation=" + mOrientation +
+                    ", mReverseLayout=" + mReverseLayout +
+                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
+                    ", mItemCount=" + mItemCount +
+                    '}';
+        }
+    }
+
+    class WrappedLinearLayoutManager extends LinearLayoutManager {
+
+        CountDownLatch layoutLatch;
+
+        OrientationHelper mSecondaryOrientation;
+
+        OnLayoutListener mOnLayoutListener;
+
+        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
+            super(context, orientation, reverseLayout);
+        }
+
+        public void expectLayouts(int count) {
+            layoutLatch = new CountDownLatch(count);
+        }
+
+        public void waitForLayout(long timeout) throws InterruptedException {
+            waitForLayout(timeout, TimeUnit.SECONDS);
+        }
+
+        @Override
+        public void setOrientation(int orientation) {
+            super.setOrientation(orientation);
+            mSecondaryOrientation = null;
+        }
+
+        @Override
+        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {
+            if (DEBUG) {
+                Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child));
+            }
+            super.removeAndRecycleView(child, recycler);
+        }
+
+        @Override
+        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
+            if (DEBUG) {
+                Log.d(TAG,
+                        "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index)));
+            }
+            super.removeAndRecycleViewAt(index, recycler);
+        }
+
+        @Override
+        void ensureLayoutState() {
+            super.ensureLayoutState();
+            if (mSecondaryOrientation == null) {
+                mSecondaryOrientation = OrientationHelper.createOrientationHelper(this,
+                        1 - getOrientation());
+            }
+        }
+
+        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
+            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);
+            assertEquals("all expected layouts should be executed at the expected time",
+                    0, layoutLatch.getCount());
+            getInstrumentation().waitForIdleSync();
+        }
+
+        @Override
+        LayoutState createLayoutState() {
+            return new LayoutState() {
+                @Override
+                View next(RecyclerView.Recycler recycler) {
+                    final boolean hadMore = hasMore(mRecyclerView.mState);
+                    final int position = mCurrentPosition;
+                    View next = super.next(recycler);
+                    assertEquals("if has more, should return a view", hadMore, next != null);
+                    assertEquals("position of the returned view must match current position",
+                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
+                    return next;
+                }
+            };
+        }
+
+        public String getBoundsLog() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
+                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
+            sb.append("\nchildren bounds\n");
+            final int childCount = getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
+                        .append("[").append("start:").append(
+                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
+                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
+            }
+            return sb.toString();
+        }
+
+        public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException {
+            RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
+            if (itemAnimator == null) {
+                return;
+            }
+            final CountDownLatch latch = new CountDownLatch(1);
+            final boolean running = itemAnimator.isRunning(
+                    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
+                        @Override
+                        public void onAnimationsFinished() {
+                            latch.countDown();
+                        }
+                    }
+            );
+            if (running) {
+                latch.await(timeoutInSeconds, TimeUnit.SECONDS);
+            }
+        }
+
+        public VisibleChildren traverseAndFindVisibleChildren() {
+            int childCount = getChildCount();
+            final VisibleChildren visibleChildren = new VisibleChildren();
+            final int start = mOrientationHelper.getStartAfterPadding();
+            final int end = mOrientationHelper.getEndAfterPadding();
+            for (int i = 0; i < childCount; i++) {
+                View child = getChildAt(i);
+                final int childStart = mOrientationHelper.getDecoratedStart(child);
+                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
+                final boolean fullyVisible = childStart >= start && childEnd <= end;
+                final boolean hidden = childEnd <= start || childStart >= end;
+                if (hidden) {
+                    continue;
+                }
+                final int position = getPosition(child);
+                if (fullyVisible) {
+                    if (position < visibleChildren.firstFullyVisiblePosition ||
+                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
+                        visibleChildren.firstFullyVisiblePosition = position;
+                    }
+
+                    if (position > visibleChildren.lastFullyVisiblePosition) {
+                        visibleChildren.lastFullyVisiblePosition = position;
+                    }
+                }
+
+                if (position < visibleChildren.firstVisiblePosition ||
+                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
+                    visibleChildren.firstVisiblePosition = position;
+                }
+
+                if (position > visibleChildren.lastVisiblePosition) {
+                    visibleChildren.lastVisiblePosition = position;
+                }
+
+            }
+            return visibleChildren;
+        }
+
+        Rect getViewBounds(View view) {
+            if (getOrientation() == HORIZONTAL) {
+                return new Rect(
+                        mOrientationHelper.getDecoratedStart(view),
+                        mSecondaryOrientation.getDecoratedStart(view),
+                        mOrientationHelper.getDecoratedEnd(view),
+                        mSecondaryOrientation.getDecoratedEnd(view));
+            } else {
+                return new Rect(
+                        mSecondaryOrientation.getDecoratedStart(view),
+                        mOrientationHelper.getDecoratedStart(view),
+                        mSecondaryOrientation.getDecoratedEnd(view),
+                        mOrientationHelper.getDecoratedEnd(view));
+            }
+
+        }
+
+        Map<Item, Rect> collectChildCoordinates() throws Throwable {
+            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    final int childCount = getChildCount();
+                    Rect layoutBounds = new Rect(0, 0,
+                            mLayoutManager.getWidth(), mLayoutManager.getHeight());
+                    for (int i = 0; i < childCount; i++) {
+                        View child = getChildAt(i);
+                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
+                                .getLayoutParams();
+                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
+                        Rect childBounds = getViewBounds(child);
+                        if (new Rect(childBounds).intersect(layoutBounds)) {
+                            items.put(vh.mBoundItem, childBounds);
+                        }
+                    }
+                }
+            });
+            return items;
+        }
+
+        @Override
+        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
+            try {
+                if (mOnLayoutListener != null) {
+                    mOnLayoutListener.before(recycler, state);
+                }
+                super.onLayoutChildren(recycler, state);
+                if (mOnLayoutListener != null) {
+                    mOnLayoutListener.after(recycler, state);
+                }
+            } catch (Throwable t) {
+                postExceptionToInstrumentation(t);
+            }
+            layoutLatch.countDown();
+        }
+
+
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index f45e485..3ea025a 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -105,6 +105,19 @@
         });
     }
 
+    public void focusSearch(final View focused, final int direction)
+            throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                View view = focused.focusSearch(direction);
+                if (view != null && view != focused) {
+                    view.requestFocus();
+                }
+            }
+        });
+    }
+
     protected WrappedRecyclerView inflateWrappedRV() {
         return (WrappedRecyclerView)
                 LayoutInflater.from(getActivity()).inflate(R.layout.wrapped_test_rv,
@@ -223,12 +236,16 @@
 
     public boolean requestFocus(final View view) {
         final boolean[] result = new boolean[1];
-        getActivity().runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                result[0] = view.requestFocus();
-            }
-        });
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    result[0] = view.requestFocus();
+                }
+            });
+        } catch (Throwable throwable) {
+            fail(throwable.getMessage());
+        }
         return result[0];
     }
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java
index bbe2cf8..23e7527 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BucketTest.java
@@ -17,12 +17,14 @@
 package android.support.v7.widget;
 
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
+@SmallTest
 public class BucketTest extends AndroidTestCase {
 
     ChildHelper.Bucket mBucket;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java
index 8163310..590fa98 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/ChildHelperTest.java
@@ -24,6 +24,7 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.AndroidTestCase;
 import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -31,6 +32,7 @@
 import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
+@SmallTest
 public class ChildHelperTest extends AndroidTestCase {
     LoggingCallback  mLoggingCallback;
     ChildHelper mChildHelper;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
index 008354c..8e5ecfd 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/DefaultItemAnimatorTest.java
@@ -18,6 +18,7 @@
 
 import android.os.Looper;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -34,6 +35,7 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+@MediumTest
 public class DefaultItemAnimatorTest extends ActivityInstrumentationTestCase2<TestActivity> {
 
     private static final String TAG = "DefaultItemAnimatorTest";
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index 6192c66..d5781fa 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -17,12 +17,18 @@
 package android.support.v7.widget;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.StateListDrawable;
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.util.SparseIntArray;
+import android.util.StateSet;
+import android.view.FocusFinder;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -41,6 +47,7 @@
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
 import static java.util.concurrent.TimeUnit.SECONDS;
 
+@MediumTest
 public class GridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
 
     static final String TAG = "GridLayoutManagerTest";
@@ -86,6 +93,65 @@
         mGlm.waitForLayout(2);
     }
 
+    public void testFocusSearchFailureUp() throws Throwable {
+        focusSearchFailure(false);
+    }
+
+    public void testFocusSearchFailureDown() throws Throwable {
+        focusSearchFailure(true);
+    }
+
+    public void focusSearchFailure(boolean scrollDown) throws Throwable {
+        final RecyclerView recyclerView = setupBasic(new Config(3, 31).reverseLayout(!scrollDown)
+                , new GridTestAdapter(31, 1) {
+            RecyclerView mAttachedRv;
+            @Override
+            public TestViewHolder onCreateViewHolder(ViewGroup parent,
+                    int viewType) {
+                TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
+                testViewHolder.itemView.setFocusable(true);
+                testViewHolder.itemView.setFocusableInTouchMode(true);
+                // Good to have colors for debugging
+                StateListDrawable stl = new StateListDrawable();
+                stl.addState(new int[]{android.R.attr.state_focused}, new ColorDrawable(Color.RED));
+                stl.addState(StateSet.WILD_CARD, new ColorDrawable(Color.BLUE));
+                testViewHolder.itemView.setBackground(stl);
+                return testViewHolder;
+            }
+
+            @Override
+            public void onAttachedToRecyclerView(RecyclerView recyclerView) {
+                mAttachedRv = recyclerView;
+            }
+
+            @Override
+            public void onBindViewHolder(TestViewHolder holder,
+                    int position) {
+                super.onBindViewHolder(holder, position);
+                holder.itemView.setMinimumHeight(mAttachedRv.getHeight() / 3);
+            }
+        });
+        waitForFirstLayout(recyclerView);
+
+        View viewToFocus = recyclerView.findViewHolderForAdapterPosition(1).itemView;
+        assertTrue(requestFocus(viewToFocus));
+        getInstrumentation().waitForIdleSync();
+        assertSame(viewToFocus, recyclerView.getFocusedChild());
+        int pos = 1;
+        View focusedView = viewToFocus;
+        while (pos < 31) {
+            focusSearch(focusedView, scrollDown ? View.FOCUS_DOWN : View.FOCUS_UP);
+            getInstrumentation().waitForIdleSync();
+            while (recyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
+                Thread.sleep(100);
+            }
+            focusedView = recyclerView.getFocusedChild();
+            assertEquals(Math.min(pos + 3, mAdapter.getItemCount() - 1),
+                    recyclerView.getChildViewHolder(focusedView).getAdapterPosition());
+            pos += 3;
+        }
+    }
+
     @UiThreadTest
     public void testScrollWithoutLayout() throws Throwable {
         final RecyclerView recyclerView = setupBasic(new Config(3, 100));
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java
index 116a80d..57bd26c 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/ItemAnimatorV2ApiTest.java
@@ -17,6 +17,7 @@
 
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -31,6 +32,7 @@
 /**
  * Includes tests for the new RecyclerView animations API (v2).
  */
+@MediumTest
 public class ItemAnimatorV2ApiTest extends BaseRecyclerViewAnimationsTest {
     @Override
     protected RecyclerView.ItemAnimator createItemAnimator() {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
new file mode 100644
index 0000000..27f420a
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerBaseConfigSetTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2015 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.v7.widget;
+
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+import android.view.View;
+
+import java.util.List;
+import java.util.Map;
+
+import static android.support.v7.widget.LayoutState.LAYOUT_END;
+import static android.support.v7.widget.LayoutState.LAYOUT_START;
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+
+/**
+ * Tests that rely on the basic configuration and does not do any additions / removals
+ */
+@RunWith(Parameterized.class)
+public class LinearLayoutManagerBaseConfigSetTest extends BaseLinearLayoutManagerTest {
+
+    private final Config mConfig;
+
+    public LinearLayoutManagerBaseConfigSetTest(Config config) {
+        mConfig = config;
+    }
+
+
+    @Parameterized.Parameters(name = "{0}")
+    public static List<Config> configs() throws CloneNotSupportedException {
+        return createBaseVariations();
+    }
+
+    @Test
+    @MediumTest
+    public void scrollToPositionWithOffsetTest() throws Throwable {
+        Config config = ((Config) mConfig.clone()).itemCount(300);
+        OrientationHelper orientationHelper = OrientationHelper
+                .createOrientationHelper(mLayoutManager, config.mOrientation);
+        Rect layoutBounds = getDecoratedRecyclerViewBounds();
+        // try scrolling towards head, should not affect anything
+        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
+        if (config.mStackFromEnd) {
+            scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1,
+                    mLayoutManager.mOrientationHelper.getEnd() - 500);
+        } else {
+            scrollToPositionWithOffset(0, 20);
+        }
+        assertRectSetsEqual(config + " trying to over scroll with offset should be no-op",
+                before, mLayoutManager.collectChildCoordinates());
+        // try offsetting some visible children
+        int testCount = 10;
+        while (testCount-- > 0) {
+            // get middle child
+            final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2);
+            final int position = mRecyclerView.getChildLayoutPosition(child);
+            final int startOffset = config.mReverseLayout ?
+                    orientationHelper.getEndAfterPadding() - orientationHelper
+                            .getDecoratedEnd(child)
+                    : orientationHelper.getDecoratedStart(child) - orientationHelper
+                            .getStartAfterPadding();
+            final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2
+                    : startOffset / 2;
+            mLayoutManager.expectLayouts(1);
+            scrollToPositionWithOffset(position, scrollOffset);
+            mLayoutManager.waitForLayout(2);
+            final int finalOffset = config.mReverseLayout ?
+                    orientationHelper.getEndAfterPadding() - orientationHelper
+                            .getDecoratedEnd(child)
+                    : orientationHelper.getDecoratedStart(child) - orientationHelper
+                            .getStartAfterPadding();
+            assertEquals(config + " scroll with offset on a visible child should work fine " +
+                            " offset:" + finalOffset + " , existing offset:" + startOffset + ", "
+                            + "child " + position,
+                    scrollOffset, finalOffset);
+        }
+
+        // try scrolling to invisible children
+        testCount = 10;
+        // we test above and below, one by one
+        int offsetMultiplier = -1;
+        while (testCount-- > 0) {
+            final TargetTuple target = findInvisibleTarget(config);
+            final String logPrefix = config + " " + target;
+            mLayoutManager.expectLayouts(1);
+            final int offset = offsetMultiplier
+                    * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3;
+            scrollToPositionWithOffset(target.mPosition, offset);
+            mLayoutManager.waitForLayout(2);
+            final View child = mLayoutManager.findViewByPosition(target.mPosition);
+            assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset
+                    + " should layout it", child);
+            final Rect bounds = mLayoutManager.getViewBounds(child);
+            if (DEBUG) {
+                Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in "
+                        + layoutBounds + " with offset " + offset);
+            }
+
+            if (config.mReverseLayout) {
+                assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse "
+                                + "layout, its end should align with recycler view's end - offset",
+                        orientationHelper.getEndAfterPadding() - offset,
+                        orientationHelper.getDecoratedEnd(child)
+                );
+            } else {
+                assertEquals(
+                        logPrefix + " when scrolling with offset to an invisible child in normal"
+                                + " layout its start should align with recycler view's start + "
+                                + "offset",
+                        orientationHelper.getStartAfterPadding() + offset,
+                        orientationHelper.getDecoratedStart(child)
+                );
+            }
+            offsetMultiplier *= -1;
+        }
+    }
+
+    @Test
+    @MediumTest
+    public void getFirstLastChildrenTest() throws Throwable {
+        final Config config = ((Config) mConfig.clone()).itemCount(300);
+        setupByConfig(config, true);
+        Runnable viewInBoundsTest = new Runnable() {
+            @Override
+            public void run() {
+                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
+                final String boundsLog = mLayoutManager.getBoundsLog();
+                assertEquals(config + ":\nfirst visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.firstVisiblePosition,
+                        mLayoutManager.findFirstVisibleItemPosition()
+                );
+                assertEquals(
+                        config + ":\nfirst fully visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
+                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
+                );
+
+                assertEquals(config + ":\nlast visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.lastVisiblePosition,
+                        mLayoutManager.findLastVisibleItemPosition()
+                );
+                assertEquals(
+                        config + ":\nlast fully visible child should match traversal result\n"
+                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
+                        mLayoutManager.findLastCompletelyVisibleItemPosition()
+                );
+            }
+        };
+        runTestOnUiThread(viewInBoundsTest);
+        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
+        // case
+        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.smoothScrollToPosition(scrollPosition);
+            }
+        });
+        while (mLayoutManager.isSmoothScrolling() ||
+                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
+            runTestOnUiThread(viewInBoundsTest);
+            Thread.sleep(400);
+        }
+        // delete all items
+        mLayoutManager.expectLayouts(2);
+        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
+        mLayoutManager.waitForLayout(2);
+        // test empty case
+        runTestOnUiThread(viewInBoundsTest);
+        // set a new adapter with huge items to test full bounds check
+        mLayoutManager.expectLayouts(1);
+        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
+        final TestAdapter newAdapter = new TestAdapter(100) {
+            @Override
+            public void onBindViewHolder(TestViewHolder holder,
+                    int position) {
+                super.onBindViewHolder(holder, position);
+                if (config.mOrientation == HORIZONTAL) {
+                    holder.itemView.setMinimumWidth(totalSpace + 5);
+                } else {
+                    holder.itemView.setMinimumHeight(totalSpace + 5);
+                }
+            }
+        };
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mRecyclerView.setAdapter(newAdapter);
+            }
+        });
+        mLayoutManager.waitForLayout(2);
+        runTestOnUiThread(viewInBoundsTest);
+    }
+
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private TargetTuple findInvisibleTarget(Config config) {
+        int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE;
+        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
+            View child = mLayoutManager.getChildAt(i);
+            int position = mRecyclerView.getChildLayoutPosition(child);
+            if (position < minPosition) {
+                minPosition = position;
+            }
+            if (position > maxPosition) {
+                maxPosition = position;
+            }
+        }
+        final int tailTarget = maxPosition +
+                (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2;
+        final int headTarget = minPosition / 2;
+        final int target;
+        // where will the child come from ?
+        final int itemLayoutDirection;
+        if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) {
+            target = tailTarget;
+            itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END;
+        } else {
+            target = headTarget;
+            itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START;
+        }
+        if (DEBUG) {
+            Log.d(TAG,
+                    config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition);
+        }
+        return new TargetTuple(target, itemLayoutDirection);
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java
new file mode 100644
index 0000000..2027359
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerPrepareForDropTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 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.v7.widget;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import android.support.test.InstrumentationRegistry;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+
+@RunWith(Parameterized.class)
+public class LinearLayoutManagerPrepareForDropTest extends BaseLinearLayoutManagerTest {
+
+    final BaseLinearLayoutManagerTest.Config mConfig;
+    final SelectTargetChildren mSelectTargetChildren;
+
+    public LinearLayoutManagerPrepareForDropTest(
+            Config config, SelectTargetChildren selectTargetChildren) {
+        mConfig = config;
+        mSelectTargetChildren = selectTargetChildren;
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Parameterized.Parameters(name = "{0}_{1}")
+    public static Iterable<Object[]> params() {
+        SelectTargetChildren[] selectors
+                = new SelectTargetChildren[]{
+                new SelectTargetChildren() {
+                    @Override
+                    public int[] selectTargetChildren(int childCount) {
+                        return new int[]{1, 0};
+                    }
+                },
+                new SelectTargetChildren() {
+                    @Override
+                    public int[] selectTargetChildren(int childCount) {
+                        return new int[]{0, 1};
+                    }
+                },
+                new SelectTargetChildren() {
+                    @Override
+                    public int[] selectTargetChildren(int childCount) {
+                        return new int[]{childCount - 1, childCount - 2};
+                    }
+                },
+                new SelectTargetChildren() {
+                    @Override
+                    public int[] selectTargetChildren(int childCount) {
+                        return new int[]{childCount - 2, childCount - 1};
+                    }
+                },
+                new SelectTargetChildren() {
+                    @Override
+                    public int[] selectTargetChildren(int childCount) {
+                        return new int[]{childCount / 2, childCount / 2 + 1};
+                    }
+                },
+                new SelectTargetChildren() {
+                    @Override
+                    public int[] selectTargetChildren(int childCount) {
+                        return new int[]{childCount / 2 + 1, childCount / 2};
+                    }
+                }
+        };
+        List<Object[]> variations = new ArrayList<>();
+        for (SelectTargetChildren selector : selectors) {
+            for (BaseLinearLayoutManagerTest.Config config : createBaseVariations()) {
+                variations.add(new Object[]{config, selector});
+            }
+        }
+        return variations;
+    }
+
+    @Test
+    @MediumTest
+    public void prepareForDropTest()
+            throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        config.mTestAdapter = new BaseRecyclerViewInstrumentationTest.TestAdapter(100) {
+            @Override
+            public void onBindViewHolder(BaseRecyclerViewInstrumentationTest.TestViewHolder holder,
+                    int position) {
+                super.onBindViewHolder(holder, position);
+                if (config.mOrientation == HORIZONTAL) {
+                    final int base = mRecyclerView.getWidth() / 5;
+                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
+                    holder.itemView.setMinimumWidth(base + itemRand);
+                } else {
+                    final int base = mRecyclerView.getHeight() / 5;
+                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
+                    holder.itemView.setMinimumHeight(base + itemRand);
+                }
+            }
+        };
+        setupByConfig(config, true);
+        mLayoutManager.expectLayouts(1);
+        scrollToPosition(mTestAdapter.getItemCount() / 2);
+        mLayoutManager.waitForLayout(1);
+        int[] positions = mSelectTargetChildren.selectTargetChildren(mRecyclerView.getChildCount());
+        final View fromChild = mLayoutManager.getChildAt(positions[0]);
+        final int fromPos = mLayoutManager.getPosition(fromChild);
+        final View onChild = mLayoutManager.getChildAt(positions[1]);
+        final int toPos = mLayoutManager.getPosition(onChild);
+        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
+        final int dragCoordinate;
+        final boolean towardsHead = toPos < fromPos;
+        final int referenceLine;
+        if (config.mReverseLayout == towardsHead) {
+            referenceLine = helper.getDecoratedEnd(onChild);
+            dragCoordinate = referenceLine + 3 -
+                    helper.getDecoratedMeasurement(fromChild);
+        } else {
+            referenceLine = helper.getDecoratedStart(onChild);
+            dragCoordinate = referenceLine - 3;
+        }
+        mLayoutManager.expectLayouts(2);
+
+        final int x, y;
+        if (config.mOrientation == HORIZONTAL) {
+            x = dragCoordinate;
+            y = fromChild.getTop();
+        } else {
+            y = dragCoordinate;
+            x = fromChild.getLeft();
+        }
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTestAdapter.moveInUIThread(fromPos, toPos);
+                mTestAdapter.notifyItemMoved(fromPos, toPos);
+                mLayoutManager.prepareForDrop(fromChild, onChild, x, y);
+            }
+        });
+        mLayoutManager.waitForLayout(2);
+
+        assertSame(fromChild, mRecyclerView.findViewHolderForAdapterPosition(toPos).itemView);
+        // make sure it has the position we wanted
+        if (config.mReverseLayout == towardsHead) {
+            assertEquals(referenceLine, helper.getDecoratedEnd(fromChild));
+        } else {
+            assertEquals(referenceLine, helper.getDecoratedStart(fromChild));
+        }
+    }
+
+    protected interface SelectTargetChildren {
+
+        int[] selectTargetChildren(int childCount);
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java
new file mode 100644
index 0000000..2a5a377
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerResizeTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 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.v7.widget;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import android.support.test.InstrumentationRegistry;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.FrameLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
+
+@RunWith(Parameterized.class)
+public class LinearLayoutManagerResizeTest extends BaseLinearLayoutManagerTest {
+
+    final Config mConfig;
+
+    public LinearLayoutManagerResizeTest(Config config) {
+        mConfig = config;
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Parameterized.Parameters(name = "{0}")
+    public static List<Config> testResize() throws Throwable {
+        List<Config> configs = new ArrayList<>();
+        for (Config config : addConfigVariation(createBaseVariations(), "mItemCount", 5
+                , Config.DEFAULT_ITEM_COUNT)) {
+            configs.add(config);
+        }
+        return configs;
+    }
+
+    @MediumTest
+    @Test
+    public void resize() throws Throwable {
+        final Config config = (Config) mConfig.clone();
+        final FrameLayout container = getRecyclerViewContainer();
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                container.setPadding(0, 0, 0, 0);
+            }
+        });
+
+        setupByConfig(config, true);
+        int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
+        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
+        int lastCompletelyVisibleItemPosition = mLayoutManager
+                .findLastCompletelyVisibleItemPosition();
+        int firstCompletelyVisibleItemPosition = mLayoutManager
+                .findFirstCompletelyVisibleItemPosition();
+        mLayoutManager.expectLayouts(1);
+        // resize the recycler view to half
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                if (config.mOrientation == HORIZONTAL) {
+                    container.setPadding(0, 0, container.getWidth() / 2, 0);
+                } else {
+                    container.setPadding(0, 0, 0, container.getWidth() / 2);
+                }
+            }
+        });
+        mLayoutManager.waitForLayout(1);
+        if (config.mStackFromEnd) {
+            assertEquals("[" + config + "]: last visible position should not change.",
+                    lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition());
+            assertEquals("[" + config + "]: last completely visible position should not change",
+                    lastCompletelyVisibleItemPosition,
+                    mLayoutManager.findLastCompletelyVisibleItemPosition());
+        } else {
+            assertEquals("[" + config + "]: first visible position should not change.",
+                    firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition());
+            assertEquals("[" + config + "]: last completely visible position should not change",
+                    firstCompletelyVisibleItemPosition,
+                    mLayoutManager.findFirstCompletelyVisibleItemPosition());
+        }
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java
new file mode 100644
index 0000000..bd157b8
--- /dev/null
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerSavedStateTest.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2015 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.v7.widget;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+@RunWith(Parameterized.class)
+public class LinearLayoutManagerSavedStateTest extends BaseLinearLayoutManagerTest {
+    final Config mConfig;
+    final boolean mWaitForLayout;
+    final boolean mLoadDataAfterRestore;
+    final PostLayoutRunnable mPostLayoutOperation;
+    final PostRestoreRunnable mPostRestoreOperation;
+
+    public LinearLayoutManagerSavedStateTest(Config config, boolean waitForLayout,
+            boolean loadDataAfterRestore, PostLayoutRunnable postLayoutOperation,
+            PostRestoreRunnable postRestoreOperation) {
+        mConfig = config;
+        mWaitForLayout = waitForLayout;
+        mLoadDataAfterRestore = loadDataAfterRestore;
+        mPostLayoutOperation = postLayoutOperation;
+        mPostRestoreOperation = postRestoreOperation;
+        mPostLayoutOperation.mLayoutManagerDelegate = new Delegate<WrappedLinearLayoutManager>() {
+            @Override
+            public WrappedLinearLayoutManager get() {
+                return mLayoutManager;
+            }
+        };
+        mPostLayoutOperation.mTestAdapterDelegate = new Delegate<TestAdapter>() {
+            @Override
+            public TestAdapter get() {
+                return mTestAdapter;
+            }
+        };
+        mPostRestoreOperation.mLayoutManagerDelegate = new Delegate<WrappedLinearLayoutManager>() {
+            @Override
+            public WrappedLinearLayoutManager get() {
+                return mLayoutManager;
+            }
+        };
+        mPostRestoreOperation.mTestAdapterDelegate = new Delegate<TestAdapter>() {
+            @Override
+            public TestAdapter get() {
+                return mTestAdapter;
+            }
+        };
+    }
+
+    @Parameterized.Parameters(name = "{0}_waitForLayout:{1}_loadDataAfterRestore:{2}"
+            + "_postLayout:{3}_postRestore:{4}")
+    public static Iterable<Object[]> params()
+            throws IllegalAccessException, CloneNotSupportedException, NoSuchFieldException {
+        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        // do nothing
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "doing nothing";
+                    }
+                },
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        layoutManager().expectLayouts(1);
+                        scrollToPosition(testAdapter().getItemCount() * 3 / 4);
+                        layoutManager().waitForLayout(2);
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "scroll to position";
+                    }
+                },
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        layoutManager().expectLayouts(1);
+                        scrollToPositionWithOffset(testAdapter().getItemCount() / 3,
+                                50);
+                        layoutManager().waitForLayout(2);
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "scroll to position with positive offset";
+                    }
+                },
+                new PostLayoutRunnable() {
+                    @Override
+                    public void run() throws Throwable {
+                        layoutManager().expectLayouts(1);
+                        scrollToPositionWithOffset(testAdapter().getItemCount() * 2 / 3,
+                                -10);  // Some tests break if this value is below the item height.
+                        layoutManager().waitForLayout(2);
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "scroll to position with negative offset";
+                    }
+                }
+        };
+
+        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
+                new PostRestoreRunnable() {
+                    @Override
+                    public String describe() {
+                        return "Doing nothing";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        // update config as well so that restore assertions will work
+                        config.mOrientation = 1 - config.mOrientation;
+                        layoutManager().setOrientation(config.mOrientation);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return config.mItemCount == 0;
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "Changing orientation";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        config.mStackFromEnd = !config.mStackFromEnd;
+                        layoutManager().setStackFromEnd(config.mStackFromEnd);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return true; //stack from end should not move items on change
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "Changing stack from end";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        config.mReverseLayout = !config.mReverseLayout;
+                        layoutManager().setReverseLayout(config.mReverseLayout);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return config.mItemCount == 0;
+                    }
+
+                    @Override
+                    public String describe() {
+                        return "Changing reverse layout";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach;
+                        layoutManager().setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return true;
+                    }
+
+                    @Override
+                    String describe() {
+                        return "Change should recycle children";
+                    }
+                },
+                new PostRestoreRunnable() {
+                    int position;
+                    @Override
+                    void onAfterRestore(Config config) throws Throwable {
+                        position = testAdapter().getItemCount() / 2;
+                        layoutManager().scrollToPosition(position);
+                    }
+
+                    @Override
+                    boolean shouldLayoutMatch(Config config) {
+                        return testAdapter().getItemCount() == 0;
+                    }
+
+                    @Override
+                    String describe() {
+                        return "Scroll to position " + position ;
+                    }
+
+                    @Override
+                    void onAfterReLayout(Config config) {
+                        if (testAdapter().getItemCount() > 0) {
+                            assertEquals(config + ":scrolled view should be last completely visible",
+                                    position,
+                                    config.mStackFromEnd ?
+                                            layoutManager().findLastCompletelyVisibleItemPosition()
+                                            : layoutManager().findFirstCompletelyVisibleItemPosition());
+                        }
+                    }
+                }
+        };
+        boolean[] waitForLayoutOptions = new boolean[]{true, false};
+        boolean[] loadDataAfterRestoreOptions = new boolean[]{true, false};
+        List<Config> variations = addConfigVariation(createBaseVariations(), "mItemCount", 0, 300);
+        variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true);
+
+        List<Object[]> params = new ArrayList<>();
+        for (Config config : variations) {
+            for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
+                for (boolean waitForLayout : waitForLayoutOptions) {
+                    for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) {
+                        for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) {
+                            params.add(new Object[]{
+                                    config.clone(), waitForLayout,
+                                    loadDataAfterRestore, postLayoutRunnable, postRestoreRunnable
+                            });
+                        }
+                    }
+
+                }
+            }
+        }
+        return params;
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    @MediumTest
+    public void savedStateTest()
+            throws Throwable {
+        if (DEBUG) {
+            Log.d(TAG, "testing saved state with wait for layout = " + mWaitForLayout + " config " +
+                    mConfig + " post layout action " + mPostLayoutOperation.describe() +
+                    "post restore action " + mPostRestoreOperation.describe());
+        }
+        setupByConfig(mConfig, false);
+
+        if (mWaitForLayout) {
+            waitForFirstLayout();
+            mPostLayoutOperation.run();
+        }
+        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
+        Parcelable savedState = mRecyclerView.onSaveInstanceState();
+        // we append a suffix to the parcelable to test out of bounds
+        String parcelSuffix = UUID.randomUUID().toString();
+        Parcel parcel = Parcel.obtain();
+        savedState.writeToParcel(parcel, 0);
+        parcel.writeString(parcelSuffix);
+        removeRecyclerView();
+        // reset for reading
+        parcel.setDataPosition(0);
+        // re-create
+        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
+        removeRecyclerView();
+
+        final int itemCount = mTestAdapter.getItemCount();
+        if (mLoadDataAfterRestore) {
+            mTestAdapter.deleteAndNotify(0, itemCount);
+        }
+
+        RecyclerView restored = new RecyclerView(getActivity());
+        // this config should be no op.
+        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
+                mConfig.mOrientation, mConfig.mReverseLayout);
+        mLayoutManager.setStackFromEnd(mConfig.mStackFromEnd);
+        restored.setLayoutManager(mLayoutManager);
+        // use the same adapter for Rect matching
+        restored.setAdapter(mTestAdapter);
+        restored.onRestoreInstanceState(savedState);
+
+        if (mLoadDataAfterRestore) {
+            mTestAdapter.addAndNotify(itemCount);
+        }
+
+        mPostRestoreOperation.onAfterRestore(mConfig);
+        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
+                parcel.readString());
+        mLayoutManager.expectLayouts(1);
+        setRecyclerView(restored);
+        mLayoutManager.waitForLayout(2);
+        // calculate prefix here instead of above to include post restore changes
+        final String logPrefix = mConfig + "\npostLayout:" + mPostLayoutOperation.describe() +
+                "\npostRestore:" + mPostRestoreOperation.describe() + "\n";
+        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
+                mConfig.mReverseLayout, mLayoutManager.getReverseLayout());
+        assertEquals(logPrefix + " on saved state, orientation should be preserved",
+                mConfig.mOrientation, mLayoutManager.getOrientation());
+        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
+                mConfig.mStackFromEnd, mLayoutManager.getStackFromEnd());
+        if (mWaitForLayout) {
+            final boolean strictItemEquality = !mLoadDataAfterRestore;
+            if (mPostRestoreOperation.shouldLayoutMatch(mConfig)) {
+                assertRectSetsEqual(
+                        logPrefix + ": on restore, previous view positions should be preserved",
+                        before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
+            } else {
+                assertRectSetsNotEqual(
+                        logPrefix
+                                + ": on restore with changes, previous view positions should NOT "
+                                + "be preserved",
+                        before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
+            }
+            mPostRestoreOperation.onAfterReLayout(mConfig);
+        }
+    }
+
+    protected static abstract class PostLayoutRunnable {
+        private Delegate<WrappedLinearLayoutManager> mLayoutManagerDelegate;
+        private Delegate<TestAdapter> mTestAdapterDelegate;
+        protected WrappedLinearLayoutManager layoutManager() {
+            return mLayoutManagerDelegate.get();
+        }
+        protected TestAdapter testAdapter() {
+            return mTestAdapterDelegate.get();
+        }
+
+        abstract void run() throws Throwable;
+        void scrollToPosition(final int position) {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+                @Override
+                public void run() {
+                    layoutManager().scrollToPosition(position);
+                }
+            });
+        }
+        void scrollToPositionWithOffset(final int position, final int offset) {
+            InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+                @Override
+                public void run() {
+                    layoutManager().scrollToPositionWithOffset(position, offset);
+                }
+            });
+        }
+        abstract String describe();
+
+        @Override
+        public String toString() {
+            return describe();
+        }
+    }
+
+    protected static abstract class PostRestoreRunnable {
+        private Delegate<WrappedLinearLayoutManager> mLayoutManagerDelegate;
+        private Delegate<TestAdapter> mTestAdapterDelegate;
+        protected WrappedLinearLayoutManager layoutManager() {
+            return mLayoutManagerDelegate.get();
+        }
+        protected TestAdapter testAdapter() {
+            return mTestAdapterDelegate.get();
+        }
+
+        void onAfterRestore(Config config) throws Throwable {
+        }
+
+        abstract String describe();
+
+        boolean shouldLayoutMatch(Config config) {
+            return true;
+        }
+
+        void onAfterReLayout(Config config) {
+
+        };
+
+        @Override
+        public String toString() {
+            return describe();
+        }
+    }
+
+    private interface Delegate<T> {
+        T get();
+    }
+}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
index c59d550..35e1fc8 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -23,6 +23,7 @@
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -50,61 +51,8 @@
  * and stability of LinearLayoutManager in response to different events (state change, scrolling
  * etc) where it is very hard to do manual testing.
  */
-public class LinearLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
-
-    private static final boolean DEBUG = false;
-
-    private static final String TAG = "LinearLayoutManagerTest";
-
-    WrappedLinearLayoutManager mLayoutManager;
-
-    TestAdapter mTestAdapter;
-
-    final List<Config> mBaseVariations = new ArrayList<Config>();
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        for (int orientation : new int[]{VERTICAL, HORIZONTAL}) {
-            for (boolean reverseLayout : new boolean[]{false, true}) {
-                for (boolean stackFromBottom : new boolean[]{false, true}) {
-                    mBaseVariations.add(new Config(orientation, reverseLayout, stackFromBottom));
-                }
-            }
-        }
-    }
-
-    protected List<Config> addConfigVariation(List<Config> base, String fieldName,
-            Object... variations)
-            throws CloneNotSupportedException, NoSuchFieldException, IllegalAccessException {
-        List<Config> newConfigs = new ArrayList<Config>();
-        Field field = Config.class.getDeclaredField(fieldName);
-        for (Config config : base) {
-            for (Object variation : variations) {
-                Config newConfig = (Config) config.clone();
-                field.set(newConfig, variation);
-                newConfigs.add(newConfig);
-            }
-        }
-        return newConfigs;
-    }
-
-    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {
-        mRecyclerView = inflateWrappedRV();
-
-        mRecyclerView.setHasFixedSize(true);
-        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)
-                : config.mTestAdapter;
-        mRecyclerView.setAdapter(mTestAdapter);
-        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,
-                config.mReverseLayout);
-        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
-        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
-        mRecyclerView.setLayoutManager(mLayoutManager);
-        if (waitForFirstLayout) {
-            waitForFirstLayout();
-        }
-    }
+@MediumTest
+public class LinearLayoutManagerTest extends BaseLinearLayoutManagerTest {
 
     public void testRemoveAnchorItem() throws Throwable {
         removeAnchorItemTest(
@@ -401,176 +349,6 @@
         }
     }
 
-    public void testResize() throws Throwable {
-        for(Config config : addConfigVariation(mBaseVariations, "mItemCount", 5
-                , Config.DEFAULT_ITEM_COUNT)) {
-            stackFromEndTest(config);
-            removeRecyclerView();
-        }
-    }
-
-    public void testScrollToPositionWithOffset() throws Throwable {
-        for (Config config : mBaseVariations) {
-            scrollToPositionWithOffsetTest(config.itemCount(300));
-            removeRecyclerView();
-        }
-    }
-
-    public void scrollToPositionWithOffsetTest(Config config) throws Throwable {
-        setupByConfig(config, true);
-        OrientationHelper orientationHelper = OrientationHelper
-                .createOrientationHelper(mLayoutManager, config.mOrientation);
-        Rect layoutBounds = getDecoratedRecyclerViewBounds();
-        // try scrolling towards head, should not affect anything
-        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
-        if (config.mStackFromEnd) {
-            scrollToPositionWithOffset(mTestAdapter.getItemCount() - 1,
-                    mLayoutManager.mOrientationHelper.getEnd() - 500);
-        } else {
-            scrollToPositionWithOffset(0, 20);
-        }
-        assertRectSetsEqual(config + " trying to over scroll with offset should be no-op",
-                before, mLayoutManager.collectChildCoordinates());
-        // try offsetting some visible children
-        int testCount = 10;
-        while (testCount-- > 0) {
-            // get middle child
-            final View child = mLayoutManager.getChildAt(mLayoutManager.getChildCount() / 2);
-            final int position = mRecyclerView.getChildLayoutPosition(child);
-            final int startOffset = config.mReverseLayout ?
-                    orientationHelper.getEndAfterPadding() - orientationHelper
-                            .getDecoratedEnd(child)
-                    : orientationHelper.getDecoratedStart(child) - orientationHelper
-                            .getStartAfterPadding();
-            final int scrollOffset = config.mStackFromEnd ? startOffset + startOffset / 2
-                    : startOffset / 2;
-            mLayoutManager.expectLayouts(1);
-            scrollToPositionWithOffset(position, scrollOffset);
-            mLayoutManager.waitForLayout(2);
-            final int finalOffset = config.mReverseLayout ?
-                    orientationHelper.getEndAfterPadding() - orientationHelper
-                            .getDecoratedEnd(child)
-                    : orientationHelper.getDecoratedStart(child) - orientationHelper
-                            .getStartAfterPadding();
-            assertEquals(config + " scroll with offset on a visible child should work fine " +
-                    " offset:" + finalOffset + " , existing offset:" + startOffset + ", "
-                            + "child " + position,
-                    scrollOffset, finalOffset);
-        }
-
-        // try scrolling to invisible children
-        testCount = 10;
-        // we test above and below, one by one
-        int offsetMultiplier = -1;
-        while (testCount-- > 0) {
-            final TargetTuple target = findInvisibleTarget(config);
-            final String logPrefix = config + " " + target;
-            mLayoutManager.expectLayouts(1);
-            final int offset = offsetMultiplier
-                    * orientationHelper.getDecoratedMeasurement(mLayoutManager.getChildAt(0)) / 3;
-            scrollToPositionWithOffset(target.mPosition, offset);
-            mLayoutManager.waitForLayout(2);
-            final View child = mLayoutManager.findViewByPosition(target.mPosition);
-            assertNotNull(logPrefix + " scrolling to a mPosition with offset " + offset
-                    + " should layout it", child);
-            final Rect bounds = mLayoutManager.getViewBounds(child);
-            if (DEBUG) {
-                Log.d(TAG, logPrefix + " post scroll to invisible mPosition " + bounds + " in "
-                        + layoutBounds + " with offset " + offset);
-            }
-
-            if (config.mReverseLayout) {
-                assertEquals(logPrefix + " when scrolling with offset to an invisible in reverse "
-                                + "layout, its end should align with recycler view's end - offset",
-                        orientationHelper.getEndAfterPadding() - offset,
-                        orientationHelper.getDecoratedEnd(child)
-                );
-            } else {
-                assertEquals(logPrefix + " when scrolling with offset to an invisible child in normal"
-                                + " layout its start should align with recycler view's start + "
-                                + "offset",
-                        orientationHelper.getStartAfterPadding() + offset,
-                        orientationHelper.getDecoratedStart(child)
-                );
-            }
-            offsetMultiplier *= -1;
-        }
-    }
-
-    private TargetTuple findInvisibleTarget(Config config) {
-        int minPosition = Integer.MAX_VALUE, maxPosition = Integer.MIN_VALUE;
-        for (int i = 0; i < mLayoutManager.getChildCount(); i++) {
-            View child = mLayoutManager.getChildAt(i);
-            int position = mRecyclerView.getChildLayoutPosition(child);
-            if (position < minPosition) {
-                minPosition = position;
-            }
-            if (position > maxPosition) {
-                maxPosition = position;
-            }
-        }
-        final int tailTarget = maxPosition +
-                (mRecyclerView.getAdapter().getItemCount() - maxPosition) / 2;
-        final int headTarget = minPosition / 2;
-        final int target;
-        // where will the child come from ?
-        final int itemLayoutDirection;
-        if (Math.abs(tailTarget - maxPosition) > Math.abs(headTarget - minPosition)) {
-            target = tailTarget;
-            itemLayoutDirection = config.mReverseLayout ? LAYOUT_START : LAYOUT_END;
-        } else {
-            target = headTarget;
-            itemLayoutDirection = config.mReverseLayout ? LAYOUT_END : LAYOUT_START;
-        }
-        if (DEBUG) {
-            Log.d(TAG,
-                    config + " target:" + target + " min:" + minPosition + ", max:" + maxPosition);
-        }
-        return new TargetTuple(target, itemLayoutDirection);
-    }
-
-    public void stackFromEndTest(final Config config) throws Throwable {
-        final FrameLayout container = getRecyclerViewContainer();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                container.setPadding(0, 0, 0, 0);
-            }
-        });
-
-        setupByConfig(config, true);
-        int lastVisibleItemPosition = mLayoutManager.findLastVisibleItemPosition();
-        int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
-        int lastCompletelyVisibleItemPosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
-        int firstCompletelyVisibleItemPosition = mLayoutManager.findFirstCompletelyVisibleItemPosition();
-        mLayoutManager.expectLayouts(1);
-        // resize the recycler view to half
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                if (config.mOrientation == HORIZONTAL) {
-                    container.setPadding(0, 0, container.getWidth() / 2, 0);
-                } else {
-                    container.setPadding(0, 0, 0, container.getWidth() / 2);
-                }
-            }
-        });
-        mLayoutManager.waitForLayout(1);
-        if (config.mStackFromEnd) {
-            assertEquals("[" + config + "]: last visible position should not change.",
-                    lastVisibleItemPosition, mLayoutManager.findLastVisibleItemPosition());
-            assertEquals("[" + config + "]: last completely visible position should not change",
-                    lastCompletelyVisibleItemPosition,
-                    mLayoutManager.findLastCompletelyVisibleItemPosition());
-        } else {
-            assertEquals("[" + config + "]: first visible position should not change.",
-                    firstVisibleItemPosition, mLayoutManager.findFirstVisibleItemPosition());
-            assertEquals("[" + config + "]: last completely visible position should not change",
-                    firstCompletelyVisibleItemPosition,
-                    mLayoutManager.findFirstCompletelyVisibleItemPosition());
-        }
-    }
-
     public void testScrollToPositionWithPredictive() throws Throwable {
         scrollToPositionWithPredictive(0, LinearLayoutManager.INVALID_OFFSET);
         removeRecyclerView();
@@ -582,63 +360,6 @@
         scrollToPositionWithPredictive(Config.DEFAULT_ITEM_COUNT / 2, 10);
     }
 
-    public void scrollToPositionWithPredictive(final int scrollPosition, final int scrollOffset)
-            throws Throwable {
-        setupByConfig(new Config(VERTICAL, false, false), true);
-
-        mLayoutManager.mOnLayoutListener = new OnLayoutListener() {
-            @Override
-            void after(RecyclerView.Recycler recycler, RecyclerView.State state) {
-                if (state.isPreLayout()) {
-                    assertEquals("pending scroll position should still be pending",
-                            scrollPosition, mLayoutManager.mPendingScrollPosition);
-                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
-                        assertEquals("pending scroll position offset should still be pending",
-                                scrollOffset, mLayoutManager.mPendingScrollPositionOffset);
-                    }
-                } else {
-                    RecyclerView.ViewHolder vh =
-                            mRecyclerView.findViewHolderForLayoutPosition(scrollPosition);
-                    assertNotNull("scroll to position should work", vh);
-                    if (scrollOffset != LinearLayoutManager.INVALID_OFFSET) {
-                        assertEquals("scroll offset should be applied properly",
-                                mLayoutManager.getPaddingTop() + scrollOffset +
-                                        ((RecyclerView.LayoutParams) vh.itemView
-                                                .getLayoutParams()).topMargin,
-                                mLayoutManager.getDecoratedTop(vh.itemView));
-                    }
-                }
-            }
-        };
-        mLayoutManager.expectLayouts(2);
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mTestAdapter.addAndNotify(0, 1);
-                    if (scrollOffset == LinearLayoutManager.INVALID_OFFSET) {
-                        mLayoutManager.scrollToPosition(scrollPosition);
-                    } else {
-                        mLayoutManager.scrollToPositionWithOffset(scrollPosition,
-                                scrollOffset);
-                    }
-
-                } catch (Throwable throwable) {
-                    throwable.printStackTrace();
-                }
-
-            }
-        });
-        mLayoutManager.waitForLayout(2);
-        checkForMainThreadException();
-    }
-
-    private void waitForFirstLayout() throws Throwable {
-        mLayoutManager.expectLayouts(1);
-        setRecyclerView(mRecyclerView);
-        mLayoutManager.waitForLayout(2);
-    }
-
     public void testRecycleDuringAnimations() throws Throwable {
         final AtomicInteger childCount = new AtomicInteger(0);
         final TestAdapter adapter = new TestAdapter(300) {
@@ -705,12 +426,6 @@
     }
 
 
-    public void testGetFirstLastChildrenTest() throws Throwable {
-        for (Config config : mBaseVariations) {
-            getFirstLastChildrenTest(config);
-        }
-    }
-
     public void testDontRecycleChildrenOnDetach() throws Throwable {
         setupByConfig(new Config().recycleChildrenOnDetach(false), true);
         runTestOnUiThread(new Runnable() {
@@ -740,340 +455,6 @@
         });
     }
 
-    public void getFirstLastChildrenTest(final Config config) throws Throwable {
-        setupByConfig(config, true);
-        Runnable viewInBoundsTest = new Runnable() {
-            @Override
-            public void run() {
-                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
-                final String boundsLog = mLayoutManager.getBoundsLog();
-                assertEquals(config + ":\nfirst visible child should match traversal result\n"
-                                + boundsLog, visibleChildren.firstVisiblePosition,
-                        mLayoutManager.findFirstVisibleItemPosition()
-                );
-                assertEquals(
-                        config + ":\nfirst fully visible child should match traversal result\n"
-                                + boundsLog, visibleChildren.firstFullyVisiblePosition,
-                        mLayoutManager.findFirstCompletelyVisibleItemPosition()
-                );
-
-                assertEquals(config + ":\nlast visible child should match traversal result\n"
-                                + boundsLog, visibleChildren.lastVisiblePosition,
-                        mLayoutManager.findLastVisibleItemPosition()
-                );
-                assertEquals(
-                        config + ":\nlast fully visible child should match traversal result\n"
-                                + boundsLog, visibleChildren.lastFullyVisiblePosition,
-                        mLayoutManager.findLastCompletelyVisibleItemPosition()
-                );
-            }
-        };
-        runTestOnUiThread(viewInBoundsTest);
-        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
-        // case
-        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mRecyclerView.smoothScrollToPosition(scrollPosition);
-            }
-        });
-        while (mLayoutManager.isSmoothScrolling() ||
-                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
-            runTestOnUiThread(viewInBoundsTest);
-            Thread.sleep(400);
-        }
-        // delete all items
-        mLayoutManager.expectLayouts(2);
-        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());
-        mLayoutManager.waitForLayout(2);
-        // test empty case
-        runTestOnUiThread(viewInBoundsTest);
-        // set a new adapter with huge items to test full bounds check
-        mLayoutManager.expectLayouts(1);
-        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();
-        final TestAdapter newAdapter = new TestAdapter(100) {
-            @Override
-            public void onBindViewHolder(TestViewHolder holder,
-                    int position) {
-                super.onBindViewHolder(holder, position);
-                if (config.mOrientation == HORIZONTAL) {
-                    holder.itemView.setMinimumWidth(totalSpace + 5);
-                } else {
-                    holder.itemView.setMinimumHeight(totalSpace + 5);
-                }
-            }
-        };
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mRecyclerView.setAdapter(newAdapter);
-            }
-        });
-        mLayoutManager.waitForLayout(2);
-        runTestOnUiThread(viewInBoundsTest);
-    }
-
-    public void testSavedState() throws Throwable {
-        PostLayoutRunnable[] postLayoutOptions = new PostLayoutRunnable[]{
-                new PostLayoutRunnable() {
-                    @Override
-                    public void run() throws Throwable {
-                        // do nothing
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "doing nothing";
-                    }
-                },
-                new PostLayoutRunnable() {
-                    @Override
-                    public void run() throws Throwable {
-                        mLayoutManager.expectLayouts(1);
-                        scrollToPosition(mTestAdapter.getItemCount() * 3 / 4);
-                        mLayoutManager.waitForLayout(2);
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "scroll to position";
-                    }
-                },
-                new PostLayoutRunnable() {
-                    @Override
-                    public void run() throws Throwable {
-                        mLayoutManager.expectLayouts(1);
-                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 1 / 3,
-                                50);
-                        mLayoutManager.waitForLayout(2);
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "scroll to position with positive offset";
-                    }
-                },
-                new PostLayoutRunnable() {
-                    @Override
-                    public void run() throws Throwable {
-                        mLayoutManager.expectLayouts(1);
-                        scrollToPositionWithOffset(mTestAdapter.getItemCount() * 2 / 3,
-                                -10);  // Some tests break if this value is below the item height.
-                        mLayoutManager.waitForLayout(2);
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "scroll to position with negative offset";
-                    }
-                }
-        };
-
-        PostRestoreRunnable[] postRestoreOptions = new PostRestoreRunnable[]{
-                new PostRestoreRunnable() {
-                    @Override
-                    public String describe() {
-                        return "Doing nothing";
-                    }
-                },
-                new PostRestoreRunnable() {
-                    @Override
-                    void onAfterRestore(Config config) throws Throwable {
-                        // update config as well so that restore assertions will work
-                        config.mOrientation = 1 - config.mOrientation;
-                        mLayoutManager.setOrientation(config.mOrientation);
-                    }
-
-                    @Override
-                    boolean shouldLayoutMatch(Config config) {
-                        return config.mItemCount == 0;
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "Changing orientation";
-                    }
-                },
-                new PostRestoreRunnable() {
-                    @Override
-                    void onAfterRestore(Config config) throws Throwable {
-                        config.mStackFromEnd = !config.mStackFromEnd;
-                        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
-                    }
-
-                    @Override
-                    boolean shouldLayoutMatch(Config config) {
-                        return true; //stack from end should not move items on change
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "Changing stack from end";
-                    }
-                },
-                new PostRestoreRunnable() {
-                    @Override
-                    void onAfterRestore(Config config) throws Throwable {
-                        config.mReverseLayout = !config.mReverseLayout;
-                        mLayoutManager.setReverseLayout(config.mReverseLayout);
-                    }
-
-                    @Override
-                    boolean shouldLayoutMatch(Config config) {
-                        return config.mItemCount == 0;
-                    }
-
-                    @Override
-                    public String describe() {
-                        return "Changing reverse layout";
-                    }
-                },
-                new PostRestoreRunnable() {
-                    @Override
-                    void onAfterRestore(Config config) throws Throwable {
-                        config.mRecycleChildrenOnDetach = !config.mRecycleChildrenOnDetach;
-                        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);
-                    }
-
-                    @Override
-                    boolean shouldLayoutMatch(Config config) {
-                        return true;
-                    }
-
-                    @Override
-                    String describe() {
-                        return "Change should recycle children";
-                    }
-                },
-                new PostRestoreRunnable() {
-                    int position;
-                    @Override
-                    void onAfterRestore(Config config) throws Throwable {
-                        position = mTestAdapter.getItemCount() / 2;
-                        mLayoutManager.scrollToPosition(position);
-                    }
-
-                    @Override
-                    boolean shouldLayoutMatch(Config config) {
-                        return mTestAdapter.getItemCount() == 0;
-                    }
-
-                    @Override
-                    String describe() {
-                        return "Scroll to position " + position ;
-                    }
-
-                    @Override
-                    void onAfterReLayout(Config config) {
-                        if (mTestAdapter.getItemCount() > 0) {
-                            assertEquals(config + ":scrolled view should be last completely visible",
-                                    position,
-                                    config.mStackFromEnd ?
-                                            mLayoutManager.findLastCompletelyVisibleItemPosition()
-                                        : mLayoutManager.findFirstCompletelyVisibleItemPosition());
-                        }
-                    }
-                }
-        };
-        boolean[] waitForLayoutOptions = new boolean[]{true, false};
-        boolean[] loadDataAfterRestoreOptions = new boolean[]{true, false};
-        List<Config> variations = addConfigVariation(mBaseVariations, "mItemCount", 0, 300);
-        variations = addConfigVariation(variations, "mRecycleChildrenOnDetach", true);
-        for (Config config : variations) {
-            for (PostLayoutRunnable postLayoutRunnable : postLayoutOptions) {
-                for (boolean waitForLayout : waitForLayoutOptions) {
-                    for (PostRestoreRunnable postRestoreRunnable : postRestoreOptions) {
-                        for (boolean loadDataAfterRestore : loadDataAfterRestoreOptions) {
-                            savedStateTest((Config) config.clone(), waitForLayout,
-                                    loadDataAfterRestore, postLayoutRunnable, postRestoreRunnable);
-                            removeRecyclerView();
-                        }
-                    }
-
-                }
-            }
-        }
-    }
-
-    public void savedStateTest(Config config, boolean waitForLayout, boolean loadDataAfterRestore,
-            PostLayoutRunnable postLayoutOperation, PostRestoreRunnable postRestoreOperation)
-            throws Throwable {
-        if (DEBUG) {
-            Log.d(TAG, "testing saved state with wait for layout = " + waitForLayout + " config " +
-                    config + " post layout action " + postLayoutOperation.describe() +
-                    "post restore action " + postRestoreOperation.describe());
-        }
-        setupByConfig(config, false);
-        if (waitForLayout) {
-            waitForFirstLayout();
-            postLayoutOperation.run();
-        }
-        Map<Item, Rect> before = mLayoutManager.collectChildCoordinates();
-        Parcelable savedState = mRecyclerView.onSaveInstanceState();
-        // we append a suffix to the parcelable to test out of bounds
-        String parcelSuffix = UUID.randomUUID().toString();
-        Parcel parcel = Parcel.obtain();
-        savedState.writeToParcel(parcel, 0);
-        parcel.writeString(parcelSuffix);
-        removeRecyclerView();
-        // reset for reading
-        parcel.setDataPosition(0);
-        // re-create
-        savedState = RecyclerView.SavedState.CREATOR.createFromParcel(parcel);
-        removeRecyclerView();
-
-        final int itemCount = mTestAdapter.getItemCount();
-        if (loadDataAfterRestore) {
-            mTestAdapter.deleteAndNotify(0, itemCount);
-        }
-
-        RecyclerView restored = new RecyclerView(getActivity());
-        // this config should be no op.
-        mLayoutManager = new WrappedLinearLayoutManager(getActivity(),
-                config.mOrientation, config.mReverseLayout);
-        mLayoutManager.setStackFromEnd(config.mStackFromEnd);
-        restored.setLayoutManager(mLayoutManager);
-        // use the same adapter for Rect matching
-        restored.setAdapter(mTestAdapter);
-        restored.onRestoreInstanceState(savedState);
-
-        if (loadDataAfterRestore) {
-            mTestAdapter.addAndNotify(itemCount);
-        }
-
-        postRestoreOperation.onAfterRestore(config);
-        assertEquals("Parcel reading should not go out of bounds", parcelSuffix,
-                parcel.readString());
-        mLayoutManager.expectLayouts(1);
-        setRecyclerView(restored);
-        mLayoutManager.waitForLayout(2);
-        // calculate prefix here instead of above to include post restore changes
-        final String logPrefix = config + "\npostLayout:" + postLayoutOperation.describe() +
-                "\npostRestore:" + postRestoreOperation.describe() + "\n";
-        assertEquals(logPrefix + " on saved state, reverse layout should be preserved",
-                config.mReverseLayout, mLayoutManager.getReverseLayout());
-        assertEquals(logPrefix + " on saved state, orientation should be preserved",
-                config.mOrientation, mLayoutManager.getOrientation());
-        assertEquals(logPrefix + " on saved state, stack from end should be preserved",
-                config.mStackFromEnd, mLayoutManager.getStackFromEnd());
-        if (waitForLayout) {
-            final boolean strictItemEquality = !loadDataAfterRestore;
-            if (postRestoreOperation.shouldLayoutMatch(config)) {
-                assertRectSetsEqual(
-                        logPrefix + ": on restore, previous view positions should be preserved",
-                        before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
-            } else {
-                assertRectSetsNotEqual(
-                        logPrefix
-                                + ": on restore with changes, previous view positions should NOT "
-                                + "be preserved",
-                        before, mLayoutManager.collectChildCoordinates(), strictItemEquality);
-            }
-            postRestoreOperation.onAfterReLayout(config);
-        }
-    }
-
     public void testScrollAndClear() throws Throwable {
         setupByConfig(new Config(), true);
 
@@ -1093,69 +474,6 @@
     }
 
 
-    void scrollToPositionWithOffset(final int position, final int offset) throws Throwable {
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mLayoutManager.scrollToPositionWithOffset(position, offset);
-            }
-        });
-    }
-
-    public void assertRectSetsNotEqual(String message, Map<Item, Rect> before,
-            Map<Item, Rect> after, boolean strictItemEquality) {
-        Throwable throwable = null;
-        try {
-            assertRectSetsEqual("NOT " + message, before, after, strictItemEquality);
-        } catch (Throwable t) {
-            throwable = t;
-        }
-        assertNotNull(message + "\ntwo layout should be different", throwable);
-    }
-
-    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after) {
-        assertRectSetsEqual(message, before, after, true);
-    }
-
-    public void assertRectSetsEqual(String message, Map<Item, Rect> before, Map<Item, Rect> after,
-            boolean strictItemEquality) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("checking rectangle equality.\n");
-        sb.append("before:\n");
-        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
-            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
-        }
-        sb.append("after:\n");
-        for (Map.Entry<Item, Rect> entry : after.entrySet()) {
-            sb.append(entry.getKey().mAdapterIndex + ":" + entry.getValue()).append("\n");
-        }
-        message = message + "\n" + sb.toString();
-        assertEquals(message + ":\nitem counts should be equal", before.size()
-                , after.size());
-        for (Map.Entry<Item, Rect> entry : before.entrySet()) {
-            final Item beforeItem = entry.getKey();
-            Rect afterRect = null;
-            if (strictItemEquality) {
-                afterRect = after.get(beforeItem);
-                assertNotNull(message + ":\nSame item should be visible after simple re-layout",
-                        afterRect);
-            } else {
-                for (Map.Entry<Item, Rect> afterEntry : after.entrySet()) {
-                    final Item afterItem = afterEntry.getKey();
-                    if (afterItem.mAdapterIndex == beforeItem.mAdapterIndex) {
-                        afterRect = afterEntry.getValue();
-                        break;
-                    }
-                }
-                assertNotNull(message + ":\nItem with same adapter index should be visible " +
-                                "after simple re-layout",
-                        afterRect);
-            }
-            assertEquals(message + ":\nItem should be laid out at the same coordinates",
-                    entry.getValue(), afterRect);
-        }
-    }
-
     public void testAccessibilityPositions() throws Throwable {
         setupByConfig(new Config(VERTICAL, false, false), true);
         final AccessibilityDelegateCompat delegateCompat = mRecyclerView
@@ -1176,453 +494,4 @@
                 record.getToIndex(),
                 mLayoutManager.findLastVisibleItemPosition());
     }
-
-    public void testPrepareForDrop() throws Throwable {
-        SelectTargetChildren[] selectors = new SelectTargetChildren[] {
-                new SelectTargetChildren() {
-                    @Override
-                    public int[] selectTargetChildren(int childCount) {
-                        return new int[]{1, 0};
-                    }
-                },
-                new SelectTargetChildren() {
-                    @Override
-                    public int[] selectTargetChildren(int childCount) {
-                        return new int[]{0, 1};
-                    }
-                },
-                new SelectTargetChildren() {
-                    @Override
-                    public int[] selectTargetChildren(int childCount) {
-                        return new int[]{childCount - 1, childCount - 2};
-                    }
-                },
-                new SelectTargetChildren() {
-                    @Override
-                    public int[] selectTargetChildren(int childCount) {
-                        return new int[]{childCount - 2, childCount - 1};
-                    }
-                },
-                new SelectTargetChildren() {
-                    @Override
-                    public int[] selectTargetChildren(int childCount) {
-                        return new int[]{childCount / 2, childCount / 2 + 1};
-                    }
-                },
-                new SelectTargetChildren() {
-                    @Override
-                    public int[] selectTargetChildren(int childCount) {
-                        return new int[]{childCount / 2 + 1, childCount / 2};
-                    }
-                }
-        };
-        for (SelectTargetChildren selector : selectors) {
-            for (Config config : mBaseVariations) {
-                prepareForDropTest(config, selector);
-                removeRecyclerView();
-            }
-        }
-    }
-
-    public void prepareForDropTest(final Config config, SelectTargetChildren selectTargetChildren)
-            throws Throwable {
-        config.mTestAdapter = new TestAdapter(100) {
-            @Override
-            public void onBindViewHolder(TestViewHolder holder,
-                    int position) {
-                super.onBindViewHolder(holder, position);
-                if (config.mOrientation == HORIZONTAL) {
-                    final int base = mRecyclerView.getWidth() / 5;
-                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
-                    holder.itemView.setMinimumWidth(base + itemRand);
-                } else {
-                    final int base = mRecyclerView.getHeight() / 5;
-                    final int itemRand = holder.mBoundItem.mText.hashCode() % base;
-                    holder.itemView.setMinimumHeight(base + itemRand);
-                }
-            }
-        };
-        setupByConfig(config, true);
-        mLayoutManager.expectLayouts(1);
-        scrollToPosition(mTestAdapter.getItemCount() / 2);
-        mLayoutManager.waitForLayout(1);
-        int[] positions = selectTargetChildren.selectTargetChildren(mRecyclerView.getChildCount());
-        final View fromChild = mLayoutManager.getChildAt(positions[0]);
-        final int fromPos = mLayoutManager.getPosition(fromChild);
-        final View onChild = mLayoutManager.getChildAt(positions[1]);
-        final int toPos = mLayoutManager.getPosition(onChild);
-        final OrientationHelper helper = mLayoutManager.mOrientationHelper;
-        final int dragCoordinate;
-        final boolean towardsHead = toPos < fromPos;
-        final int referenceLine;
-        if (config.mReverseLayout == towardsHead) {
-            referenceLine = helper.getDecoratedEnd(onChild);
-            dragCoordinate = referenceLine + 3 -
-                    helper.getDecoratedMeasurement(fromChild);
-        } else {
-            referenceLine = helper.getDecoratedStart(onChild);
-            dragCoordinate = referenceLine - 3;
-        }
-        mLayoutManager.expectLayouts(2);
-
-        final int x,y;
-        if (config.mOrientation == HORIZONTAL) {
-            x = dragCoordinate;
-            y = fromChild.getTop();
-        } else {
-            y = dragCoordinate;
-            x = fromChild.getLeft();
-        }
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                mTestAdapter.moveInUIThread(fromPos, toPos);
-                mTestAdapter.notifyItemMoved(fromPos, toPos);
-                mLayoutManager.prepareForDrop(fromChild, onChild, x, y);
-            }
-        });
-        mLayoutManager.waitForLayout(2);
-
-        assertSame(fromChild, mRecyclerView.findViewHolderForAdapterPosition(toPos).itemView);
-        // make sure it has the position we wanted
-        if (config.mReverseLayout == towardsHead) {
-            assertEquals(referenceLine, helper.getDecoratedEnd(fromChild));
-        } else {
-            assertEquals(referenceLine, helper.getDecoratedStart(fromChild));
-        }
-    }
-
-    static class VisibleChildren {
-
-        int firstVisiblePosition = RecyclerView.NO_POSITION;
-
-        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;
-
-        int lastVisiblePosition = RecyclerView.NO_POSITION;
-
-        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;
-
-        @Override
-        public String toString() {
-            return "VisibleChildren{" +
-                    "firstVisiblePosition=" + firstVisiblePosition +
-                    ", firstFullyVisiblePosition=" + firstFullyVisiblePosition +
-                    ", lastVisiblePosition=" + lastVisiblePosition +
-                    ", lastFullyVisiblePosition=" + lastFullyVisiblePosition +
-                    '}';
-        }
-    }
-
-    abstract private class PostLayoutRunnable {
-
-        abstract void run() throws Throwable;
-
-        abstract String describe();
-    }
-
-    abstract private class PostRestoreRunnable {
-
-        void onAfterRestore(Config config) throws Throwable {
-        }
-
-        abstract String describe();
-
-        boolean shouldLayoutMatch(Config config) {
-            return true;
-        }
-
-        void onAfterReLayout(Config config) {
-
-        };
-    }
-
-    class WrappedLinearLayoutManager extends LinearLayoutManager {
-
-        CountDownLatch layoutLatch;
-
-        OrientationHelper mSecondaryOrientation;
-
-        OnLayoutListener mOnLayoutListener;
-
-        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
-            super(context, orientation, reverseLayout);
-        }
-
-        public void expectLayouts(int count) {
-            layoutLatch = new CountDownLatch(count);
-        }
-
-        public void waitForLayout(long timeout) throws InterruptedException {
-            waitForLayout(timeout, TimeUnit.SECONDS);
-        }
-
-        @Override
-        public void setOrientation(int orientation) {
-            super.setOrientation(orientation);
-            mSecondaryOrientation = null;
-        }
-
-        @Override
-        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {
-            if (DEBUG) {
-                Log.d(TAG, "recycling view " + mRecyclerView.getChildViewHolder(child));
-            }
-            super.removeAndRecycleView(child, recycler);
-        }
-
-        @Override
-        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {
-            if (DEBUG) {
-                Log.d(TAG, "recycling view at" + mRecyclerView.getChildViewHolder(getChildAt(index)));
-            }
-            super.removeAndRecycleViewAt(index, recycler);
-        }
-
-        @Override
-        void ensureLayoutState() {
-            super.ensureLayoutState();
-            if (mSecondaryOrientation == null) {
-                mSecondaryOrientation = OrientationHelper.createOrientationHelper(this,
-                        1 - getOrientation());
-            }
-        }
-
-        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {
-            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);
-            assertEquals("all expected layouts should be executed at the expected time",
-                    0, layoutLatch.getCount());
-            getInstrumentation().waitForIdleSync();
-        }
-
-        @Override
-        LayoutState createLayoutState() {
-            return new LayoutState() {
-                @Override
-                View next(RecyclerView.Recycler recycler) {
-                    final boolean hadMore = hasMore(mRecyclerView.mState);
-                    final int position = mCurrentPosition;
-                    View next = super.next(recycler);
-                    assertEquals("if has more, should return a view", hadMore, next != null);
-                    assertEquals("position of the returned view must match current position",
-                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
-                    return next;
-                }
-            };
-        }
-
-        public String getBoundsLog() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
-                    .append(",").append(" end").append(mOrientationHelper.getEndAfterPadding());
-            sb.append("\nchildren bounds\n");
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                sb.append("child (ind:").append(i).append(", pos:").append(getPosition(child))
-                        .append("[").append("start:").append(
-                        mOrientationHelper.getDecoratedStart(child)).append(", end:")
-                        .append(mOrientationHelper.getDecoratedEnd(child)).append("]\n");
-            }
-            return sb.toString();
-        }
-
-        public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException {
-            RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();
-            if (itemAnimator == null) {
-                return;
-            }
-            final CountDownLatch latch = new CountDownLatch(1);
-            final boolean running = itemAnimator.isRunning(
-                    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {
-                        @Override
-                        public void onAnimationsFinished() {
-                            latch.countDown();
-                        }
-                    }
-            );
-            if (running) {
-                latch.await(timeoutInSeconds, TimeUnit.SECONDS);
-            }
-        }
-
-        public VisibleChildren traverseAndFindVisibleChildren() {
-            int childCount = getChildCount();
-            final VisibleChildren visibleChildren = new VisibleChildren();
-            final int start = mOrientationHelper.getStartAfterPadding();
-            final int end = mOrientationHelper.getEndAfterPadding();
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                final int childStart = mOrientationHelper.getDecoratedStart(child);
-                final int childEnd = mOrientationHelper.getDecoratedEnd(child);
-                final boolean fullyVisible = childStart >= start && childEnd <= end;
-                final boolean hidden = childEnd <= start || childStart >= end;
-                if (hidden) {
-                    continue;
-                }
-                final int position = getPosition(child);
-                if (fullyVisible) {
-                    if (position < visibleChildren.firstFullyVisiblePosition ||
-                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {
-                        visibleChildren.firstFullyVisiblePosition = position;
-                    }
-
-                    if (position > visibleChildren.lastFullyVisiblePosition) {
-                        visibleChildren.lastFullyVisiblePosition = position;
-                    }
-                }
-
-                if (position < visibleChildren.firstVisiblePosition ||
-                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {
-                    visibleChildren.firstVisiblePosition = position;
-                }
-
-                if (position > visibleChildren.lastVisiblePosition) {
-                    visibleChildren.lastVisiblePosition = position;
-                }
-
-            }
-            return visibleChildren;
-        }
-
-        Rect getViewBounds(View view) {
-            if (getOrientation() == HORIZONTAL) {
-                return new Rect(
-                        mOrientationHelper.getDecoratedStart(view),
-                        mSecondaryOrientation.getDecoratedStart(view),
-                        mOrientationHelper.getDecoratedEnd(view),
-                        mSecondaryOrientation.getDecoratedEnd(view));
-            } else {
-                return new Rect(
-                        mSecondaryOrientation.getDecoratedStart(view),
-                        mOrientationHelper.getDecoratedStart(view),
-                        mSecondaryOrientation.getDecoratedEnd(view),
-                        mOrientationHelper.getDecoratedEnd(view));
-            }
-
-        }
-
-        Map<Item, Rect> collectChildCoordinates() throws Throwable {
-            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();
-            runTestOnUiThread(new Runnable() {
-                @Override
-                public void run() {
-                    final int childCount = getChildCount();
-                    Rect layoutBounds = new Rect(0, 0,
-                            mLayoutManager.getWidth(), mLayoutManager.getHeight());
-                    for (int i = 0; i < childCount; i++) {
-                        View child = getChildAt(i);
-                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child
-                                .getLayoutParams();
-                        TestViewHolder vh = (TestViewHolder) lp.mViewHolder;
-                        Rect childBounds = getViewBounds(child);
-                        if (new Rect(childBounds).intersect(layoutBounds)) {
-                            items.put(vh.mBoundItem, childBounds);
-                        }
-                    }
-                }
-            });
-            return items;
-        }
-
-        @Override
-        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
-            try {
-                if (mOnLayoutListener != null) {
-                    mOnLayoutListener.before(recycler, state);
-                }
-                super.onLayoutChildren(recycler, state);
-                if (mOnLayoutListener != null) {
-                    mOnLayoutListener.after(recycler, state);
-                }
-            } catch (Throwable t) {
-                postExceptionToInstrumentation(t);
-            }
-            layoutLatch.countDown();
-        }
-
-
-    }
-
-    static class OnLayoutListener {
-        void before(RecyclerView.Recycler recycler, RecyclerView.State state){}
-        void after(RecyclerView.Recycler recycler, RecyclerView.State state){}
-    }
-
-    static class Config implements Cloneable {
-
-        private static final int DEFAULT_ITEM_COUNT = 100;
-
-        private boolean mStackFromEnd;
-
-        int mOrientation = VERTICAL;
-
-        boolean mReverseLayout = false;
-
-        boolean mRecycleChildrenOnDetach = false;
-
-        int mItemCount = DEFAULT_ITEM_COUNT;
-
-        TestAdapter mTestAdapter;
-
-        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {
-            mOrientation = orientation;
-            mReverseLayout = reverseLayout;
-            mStackFromEnd = stackFromEnd;
-        }
-
-        public Config() {
-
-        }
-
-        Config adapter(TestAdapter adapter) {
-            mTestAdapter = adapter;
-            return this;
-        }
-
-        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {
-            mRecycleChildrenOnDetach = recycleChildrenOnDetach;
-            return this;
-        }
-
-        Config orientation(int orientation) {
-            mOrientation = orientation;
-            return this;
-        }
-
-        Config stackFromBottom(boolean stackFromBottom) {
-            mStackFromEnd = stackFromBottom;
-            return this;
-        }
-
-        Config reverseLayout(boolean reverseLayout) {
-            mReverseLayout = reverseLayout;
-            return this;
-        }
-
-        public Config itemCount(int itemCount) {
-            mItemCount = itemCount;
-            return this;
-        }
-
-        // required by convention
-        @Override
-        public Object clone() throws CloneNotSupportedException {
-            return super.clone();
-        }
-
-        @Override
-        public String toString() {
-            return "Config{" +
-                    "mStackFromEnd=" + mStackFromEnd +
-                    ", mOrientation=" + mOrientation +
-                    ", mReverseLayout=" + mReverseLayout +
-                    ", mRecycleChildrenOnDetach=" + mRecycleChildrenOnDetach +
-                    ", mItemCount=" + mItemCount +
-                    '}';
-        }
-    }
-
-    private interface SelectTargetChildren {
-        int[] selectTargetChildren(int childCount);
-    }
 }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
index 42ad90a..15d6031 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAccessibilityTest.java
@@ -20,11 +20,13 @@
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
+@MediumTest
 public class RecyclerViewAccessibilityTest extends BaseRecyclerViewInstrumentationTest {
 
     public RecyclerViewAccessibilityTest() {
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
index e2d1569..67ddda4 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewAnimationsTest.java
@@ -18,7 +18,9 @@
 
 import android.graphics.Rect;
 import android.os.Debug;
+import android.support.annotation.NonNull;
 import android.support.v4.view.ViewCompat;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,6 +37,7 @@
 /**
  * Tests for {@link SimpleItemAnimator} API.
  */
+@MediumTest
 public class RecyclerViewAnimationsTest extends BaseRecyclerViewAnimationsTest {
 
     final List<TestViewHolder> recycledVHs = new ArrayList<>();
@@ -769,19 +772,7 @@
         mLayoutManager.waitForLayout(2);
     }
 
-    private static boolean listEquals(List list1, List list2) {
-        if (list1.size() != list2.size()) {
-            return false;
-        }
-        for (int i = 0; i < list1.size(); i++) {
-            if (!list1.get(i).equals(list2.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private void testChangeWithPayload(final boolean supportsChangeAnim,
+    private void testChangeWithPayload(final boolean supportsChangeAnim, final boolean canReUse,
             Object[][] notifyPayloads, Object[][] expectedPayloadsInOnBind)
             throws Throwable {
         final List<Object> expectedPayloads = new ArrayList<Object>();
@@ -809,11 +800,18 @@
                 if (DEBUG) {
                     Log.d(TAG, " onBind to " + position + "" + holder.toString());
                 }
-                assertTrue(listEquals(payloads, expectedPayloads));
+                assertEquals(expectedPayloads, payloads);
             }
         };
         testAdapter.setHasStableIds(false);
         setupBasic(testAdapter.getItemCount(), 0, 10, testAdapter);
+        mRecyclerView.setItemAnimator(new DefaultItemAnimator() {
+            @Override
+            public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder,
+                    @NonNull List<Object> payloads) {
+                return canReUse && super.canReuseUpdatedViewHolder(viewHolder, payloads);
+            }
+        });
         ((SimpleItemAnimator) mRecyclerView.getItemAnimator()).setSupportsChangeAnimations(
                 supportsChangeAnim);
 
@@ -840,7 +838,26 @@
 
     public void testCrossFadingChangeAnimationWithPayload() throws Throwable {
         // for crossfading change animation,  will receive EMPTY payload in onBindViewHolder
-        testChangeWithPayload(true,
+        testChangeWithPayload(true, true,
+                new Object[][]{
+                        new Object[]{"abc"},
+                        new Object[]{"abc", null, "cdf"},
+                        new Object[]{"abc", null},
+                        new Object[]{null, "abc"},
+                        new Object[]{"abc", "cdf"}
+                },
+                new Object[][]{
+                        new Object[]{"abc"},
+                        new Object[0],
+                        new Object[0],
+                        new Object[0],
+                        new Object[]{"abc", "cdf"}
+                });
+    }
+
+    public void testCrossFadingChangeAnimationWithPayloadWithoutReuse() throws Throwable {
+        // for crossfading change animation,  will receive EMPTY payload in onBindViewHolder
+        testChangeWithPayload(true, false,
                 new Object[][]{
                         new Object[]{"abc"},
                         new Object[]{"abc", null, "cdf"},
@@ -860,7 +877,7 @@
     public void testNoChangeAnimationWithPayload() throws Throwable {
         // for Change Animation disabled, payload should match the payloads unless
         // null payload is fired.
-        testChangeWithPayload(false,
+        testChangeWithPayload(false, true,
                 new Object[][]{
                         new Object[]{"abc"},
                         new Object[]{"abc", null, "cdf"},
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 917680b..5cc4e36 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.view.MotionEvent;
@@ -32,6 +33,7 @@
 import java.util.List;
 import java.util.UUID;
 
+@SmallTest
 public class RecyclerViewBasicTest extends AndroidTestCase {
 
     RecyclerView mRecyclerView;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
index d6c1f28..08738f3 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewLayoutTest.java
@@ -29,6 +29,7 @@
 import android.os.SystemClock;
 import android.support.v4.view.ViewCompat;
 import android.test.TouchUtils;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -56,6 +57,7 @@
 import android.support.test.runner.AndroidJUnit4;
 
 @RunWith(AndroidJUnit4.class)
+@MediumTest
 public class RecyclerViewLayoutTest extends BaseRecyclerViewInstrumentationTest {
     private static final int FLAG_HORIZONTAL = 1;
     private static final int FLAG_VERTICAL = 1 << 1;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
index e09fceb..b380e6c 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
@@ -18,7 +18,10 @@
 package android.support.v7.widget;
 
 
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.StateListDrawable;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -26,7 +29,9 @@
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
 import android.support.v4.view.accessibility.AccessibilityRecordCompat;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
+import android.util.StateSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -44,10 +49,15 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static android.support.v7.widget.LayoutState.*;
+import static android.support.v7.widget.LayoutState.LAYOUT_END;
+import static android.support.v7.widget.LayoutState.LAYOUT_START;
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
-import static android.support.v7.widget.StaggeredGridLayoutManager.*;
+import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS;
+import static android.support.v7.widget.StaggeredGridLayoutManager.GAP_HANDLING_NONE;
+import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
+import static android.support.v7.widget.StaggeredGridLayoutManager.LayoutParams;
 
+@MediumTest
 public class StaggeredGridLayoutManagerTest extends BaseRecyclerViewInstrumentationTest {
 
     private static final boolean DEBUG = false;
@@ -79,7 +89,11 @@
     }
 
     void setupByConfig(Config config) throws Throwable {
-        mAdapter = new GridTestAdapter(config.mItemCount, config.mOrientation);
+        setupByConfig(config, new GridTestAdapter(config.mItemCount, config.mOrientation));
+    }
+
+    void setupByConfig(Config config, GridTestAdapter adapter) throws Throwable {
+        mAdapter = adapter;
         mRecyclerView = new RecyclerView(getActivity());
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setHasFixedSize(true);
@@ -308,6 +322,103 @@
 
     }
 
+    public void testFocusSearchFailureUp() throws Throwable {
+        focusSearchFailure(false);
+    }
+
+    public void testFocusSearchFailureDown() throws Throwable {
+        focusSearchFailure(true);
+    }
+
+    public void focusSearchFailure(boolean scrollDown) throws Throwable {
+        int focusDir = scrollDown ? View.FOCUS_DOWN : View.FOCUS_UP;
+        setupByConfig(new Config(VERTICAL, !scrollDown, 3, GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS)
+                , new GridTestAdapter(31, 1) {
+                    RecyclerView mAttachedRv;
+                    @Override
+                    public TestViewHolder onCreateViewHolder(ViewGroup parent,
+                            int viewType) {
+                        TestViewHolder testViewHolder = super.onCreateViewHolder(parent, viewType);
+                        testViewHolder.itemView.setFocusable(true);
+                        testViewHolder.itemView.setFocusableInTouchMode(true);
+                        // Good to have colors for debugging
+                        StateListDrawable stl = new StateListDrawable();
+                        stl.addState(new int[]{android.R.attr.state_focused},
+                                new ColorDrawable(Color.RED));
+                        stl.addState(StateSet.WILD_CARD, new ColorDrawable(Color.BLUE));
+                        testViewHolder.itemView.setBackground(stl);
+                        return testViewHolder;
+                    }
+
+                    @Override
+                    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
+                        mAttachedRv = recyclerView;
+                    }
+
+                    @Override
+                    public void onBindViewHolder(TestViewHolder holder,
+                            int position) {
+                        super.onBindViewHolder(holder, position);
+                        holder.itemView.setMinimumHeight(mAttachedRv.getHeight() / 3);
+                    }
+                });
+        /**
+         * 0  1  2
+         * 3  4  5
+         * 6  7  8
+         * 9  10 11
+         * 12 13 14
+         * 15 16 17
+         * 18 18 18
+         * 19
+         * 20 20 20
+         * 21 22
+         * 23 23 23
+         * 24 25 26
+         * 27 28 29
+         * 30
+         */
+        mAdapter.mFullSpanItems.add(18);
+        mAdapter.mFullSpanItems.add(20);
+        mAdapter.mFullSpanItems.add(23);
+        waitFirstLayout();
+        View viewToFocus = mRecyclerView.findViewHolderForAdapterPosition(1).itemView;
+        assertTrue(requestFocus(viewToFocus));
+        getInstrumentation().waitForIdleSync();
+        assertSame(viewToFocus, mRecyclerView.getFocusedChild());
+        int pos = 1;
+        View focusedView = viewToFocus;
+        while (pos < 16) {
+            focusSearchAndWaitForScroll(focusedView, focusDir);
+            focusedView = mRecyclerView.getFocusedChild();
+            assertEquals(pos + 3,
+                    mRecyclerView.getChildViewHolder(focusedView).getAdapterPosition());
+            pos += 3;
+        }
+        for (int i : new int[]{18, 19, 20, 21, 23, 24}) {
+            focusSearchAndWaitForScroll(focusedView, focusDir);
+            focusedView = mRecyclerView.getFocusedChild();
+            assertEquals(i, mRecyclerView.getChildViewHolder(focusedView).getAdapterPosition());
+        }
+        // now move right
+        focusSearch(focusedView, View.FOCUS_RIGHT);
+        focusedView = mRecyclerView.getFocusedChild();
+        assertEquals(25, mRecyclerView.getChildViewHolder(focusedView).getAdapterPosition());
+        for (int i : new int[]{28, 30}) {
+            focusSearchAndWaitForScroll(focusedView, focusDir);
+            focusedView = mRecyclerView.getFocusedChild();
+            assertEquals(i, mRecyclerView.getChildViewHolder(focusedView).getAdapterPosition());
+        }
+    }
+
+    private void focusSearchAndWaitForScroll(View focused, int dir) throws Throwable {
+        focusSearch(focused, dir);
+        getInstrumentation().waitForIdleSync();
+        while (mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
+            Thread.sleep(100);
+        }
+    }
+
 
     public void testScrollBackAndPreservePositions() throws Throwable {
         for (boolean saveRestore : new boolean[]{false, true}) {
@@ -514,9 +625,9 @@
                         .findLastVisibleItemPositions(
                                 provideArr ? new int[mLayoutManager.getSpanCount()] : null);
                 assertEquals(config + ":\nfirst visible child should match traversal result\n"
-                                + "traversed:" + visibleChildren + "\n"
-                                + "queried:" + queryResult + "\n"
-                                + boundsLog, visibleChildren, queryResult
+                        + "traversed:" + visibleChildren + "\n"
+                        + "queried:" + queryResult + "\n"
+                        + boundsLog, visibleChildren, queryResult
                 );
             }
         };
@@ -927,6 +1038,7 @@
                     lp.height = mRecyclerView.getHeight() / 3;
                 }
             }
+
             @Override
             boolean assignRandomSize() {
                 return false;
@@ -940,7 +1052,7 @@
             Rect recyclerViewBounds = getDecoratedRecyclerViewBounds();
             // workaround for SGLM's span distribution issue. Right now, it may leave gaps so we
             // avoid it by setting its layout params directly
-            if(config.mOrientation == HORIZONTAL) {
+            if (config.mOrientation == HORIZONTAL) {
                 recyclerViewBounds.bottom -= recyclerViewBounds.height() % config.mSpanCount;
             } else {
                 recyclerViewBounds.right -= recyclerViewBounds.width() % config.mSpanCount;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
index c2fac6e..b837e76 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/helper/ItemTouchHelperTest.java
@@ -23,6 +23,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.WrappedRecyclerView;
 import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -34,6 +35,7 @@
 
 import static android.support.v7.widget.helper.ItemTouchHelper.*;
 
+@MediumTest
 public class ItemTouchHelperTest extends BaseRecyclerViewInstrumentationTest {
 
     TestAdapter mAdapter;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java
index 6565e6f..4fcc657 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/test/RecyclerViewTest.java
@@ -24,8 +24,10 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.StaggeredGridLayoutManager;
 import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.widget.LinearLayout;
 
+@SmallTest
 public class RecyclerViewTest extends ActivityInstrumentationTestCase2<RecyclerViewTestActivity> {
 
     public RecyclerViewTest() {