MediaBrowserCompat: Make getItem work properly
am: 9623e68d8a

Change-Id: I9d0131d3464adf387b88f65efa195ca38cc06e0c
diff --git a/build.gradle b/build.gradle
index 340984f..4113929 100644
--- a/build.gradle
+++ b/build.gradle
@@ -24,7 +24,7 @@
     }
     dependencies {
         // Keep gradle plugin version in sync with ub_supportlib-master manifest.
-        classpath 'com.android.tools.build:gradle:2.2.1'
+        classpath 'com.android.tools.build:gradle:2.2.4'
     }
 }
 
@@ -41,8 +41,8 @@
 }
 
 gradle.ext.currentSdk = 25
-ext.supportVersion = '25.1.1'
-ext.extraVersion = 42
+ext.supportVersion = '25.2.0-SNAPSHOT'
+ext.extraVersion = 41
 ext.supportRepoOut = ''
 ext.buildToolsVersion = '24.0.1'
 ext.buildNumber = Integer.toString(ext.extraVersion)
diff --git a/compat/build.gradle b/compat/build.gradle
index 5722998..e87db0e 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -15,9 +15,6 @@
     testCompile 'junit:junit:4.12'
 }
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-
 android {
     compileSdkVersion project.ext.currentSdk
 
@@ -58,11 +55,7 @@
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
     }
-
-    testOptions {
-        unitTests.returnDefaultValues = true
         compileSdkVersion project.ext.currentSdk
-    }
 }
 
 android.libraryVariants.all { variant ->
@@ -73,22 +66,6 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
@@ -96,7 +73,6 @@
         exclude('android/service/media/**')
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 89796ea..0b94a96 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -16,9 +16,6 @@
     testCompile 'junit:junit:4.12'
 }
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-
 android {
     compileSdkVersion project.ext.currentSdk
 
@@ -54,7 +51,6 @@
 
     testOptions {
         unitTests.returnDefaultValues = true
-        compileSdkVersion project.ext.currentSdk
     }
 }
 
@@ -66,22 +62,6 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
@@ -89,7 +69,6 @@
         exclude('android/service/media/**')
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index 750cf49..d40781d 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -48,11 +48,6 @@
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
     }
-
-    testOptions {
-        unitTests.returnDefaultValues = true
-        compileSdkVersion project.ext.currentSdk
-    }
 }
 
 android.libraryVariants.all { variant ->
@@ -63,22 +58,6 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
@@ -86,7 +65,6 @@
         exclude('android/service/media/**')
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/customtabs/build.gradle b/customtabs/build.gradle
index a409dba..e427d1e 100644
--- a/customtabs/build.gradle
+++ b/customtabs/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'customtabs'
 
 dependencies {
@@ -48,27 +47,10 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/design/build.gradle b/design/build.gradle
index abd99c0..37e6625 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -83,28 +83,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index bc73970..82d983e 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -255,6 +255,7 @@
         }
         removeAllViews();
         if (mMenu.size() == 0) {
+            mButtons = null;
             return;
         }
         mButtons = new BottomNavigationItemView[mMenu.size()];
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index 4c7ef0a..8a34239 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -111,6 +111,7 @@
     static final int PENDING_ACTION_EXPANDED = 0x1;
     static final int PENDING_ACTION_COLLAPSED = 0x2;
     static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
+    static final int PENDING_ACTION_FORCE = 0x8;
 
     /**
      * Interface definition for a callback to be invoked when an {@link AppBarLayout}'s vertical
@@ -172,7 +173,7 @@
                 0, R.style.Widget_Design_AppBarLayout);
         ViewCompat.setBackground(this, a.getDrawable(R.styleable.AppBarLayout_android_background));
         if (a.hasValue(R.styleable.AppBarLayout_expanded)) {
-            setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false));
+            setExpanded(a.getBoolean(R.styleable.AppBarLayout_expanded, false), false, false);
         }
         if (Build.VERSION.SDK_INT >= 21 && a.hasValue(R.styleable.AppBarLayout_elevation)) {
             ViewUtilsLollipop.setDefaultAppBarLayoutStateListAnimator(
@@ -299,8 +300,13 @@
      * @attr ref android.support.design.R.styleable#AppBarLayout_expanded
      */
     public void setExpanded(boolean expanded, boolean animate) {
+        setExpanded(expanded, animate, true);
+    }
+
+    private void setExpanded(boolean expanded, boolean animate, boolean force) {
         mPendingAction = (expanded ? PENDING_ACTION_EXPANDED : PENDING_ACTION_COLLAPSED)
-                | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0);
+                | (animate ? PENDING_ACTION_ANIMATE_ENABLED : 0)
+                | (force ? PENDING_ACTION_FORCE : 0);
         requestLayout();
     }
 
@@ -1048,8 +1054,21 @@
                 int layoutDirection) {
             boolean handled = super.onLayoutChild(parent, abl, layoutDirection);
 
+            // The priority for for actions here is (first which is true wins):
+            // 1. forced pending actions
+            // 2. offsets for restorations
+            // 3. non-forced pending actions
             final int pendingAction = abl.getPendingAction();
-            if (pendingAction != PENDING_ACTION_NONE) {
+            if (mOffsetToChildIndexOnLayout >= 0 && (pendingAction & PENDING_ACTION_FORCE) == 0) {
+                View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
+                int offset = -child.getBottom();
+                if (mOffsetToChildIndexOnLayoutIsMinHeight) {
+                    offset += ViewCompat.getMinimumHeight(child) + abl.getTopInset();
+                } else {
+                    offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
+                }
+                setHeaderTopBottomOffset(parent, abl, offset);
+            } else if (pendingAction != PENDING_ACTION_NONE) {
                 final boolean animate = (pendingAction & PENDING_ACTION_ANIMATE_ENABLED) != 0;
                 if ((pendingAction & PENDING_ACTION_COLLAPSED) != 0) {
                     final int offset = -abl.getUpNestedPreScrollRange();
@@ -1065,15 +1084,6 @@
                         setHeaderTopBottomOffset(parent, abl, 0);
                     }
                 }
-            } else if (mOffsetToChildIndexOnLayout >= 0) {
-                View child = abl.getChildAt(mOffsetToChildIndexOnLayout);
-                int offset = -child.getBottom();
-                if (mOffsetToChildIndexOnLayoutIsMinHeight) {
-                    offset += ViewCompat.getMinimumHeight(child);
-                } else {
-                    offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
-                }
-                setTopAndBottomOffset(offset);
             }
 
             // Finally reset any pending states
@@ -1085,6 +1095,11 @@
             setTopAndBottomOffset(
                     MathUtils.constrain(getTopAndBottomOffset(), -abl.getTotalScrollRange(), 0));
 
+            // Update the AppBarLayout's drawable state for any elevation changes.
+            // This is needed so that the elevation is set in the first layout, so that
+            // we don't get a visual elevation jump pre-N (due to the draw dispatch skip)
+            updateAppBarLayoutDrawableState(parent, abl, getTopAndBottomOffset(), 0, true);
+
             // Make sure we dispatch the offset update
             abl.dispatchOffsetUpdates(getTopAndBottomOffset());
 
@@ -1161,7 +1176,7 @@
 
                     // Update the AppBarLayout's drawable state (for any elevation changes)
                     updateAppBarLayoutDrawableState(coordinatorLayout, appBarLayout, newOffset,
-                            newOffset < curOffset ? -1 : 1);
+                            newOffset < curOffset ? -1 : 1, false);
                 }
             } else {
                 // Reset the offset delta
@@ -1224,7 +1239,8 @@
         }
 
         private void updateAppBarLayoutDrawableState(final CoordinatorLayout parent,
-                final AppBarLayout layout, final int offset, final int direction) {
+                final AppBarLayout layout, final int offset, final int direction,
+                final boolean forceJump) {
             final View child = getAppBarChildOnOffset(layout, offset);
             if (child != null) {
                 final AppBarLayout.LayoutParams childLp = (LayoutParams) child.getLayoutParams();
@@ -1248,8 +1264,8 @@
 
                 final boolean changed = layout.setCollapsedState(collapsed);
 
-                if (changed && Build.VERSION.SDK_INT >= 11
-                        && shouldJumpElevationState(parent, layout)) {
+                if (Build.VERSION.SDK_INT >= 11 && (forceJump
+                        || (changed && shouldJumpElevationState(parent, layout)))) {
                     // If the collapsed state changed, we may need to
                     // jump to the current state if we have an overlapping view
                     layout.jumpDrawablesToCurrentState();
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index bac67f9..1f570b6 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -1293,7 +1293,7 @@
                 offsetChildByInset(child, inset, layoutDirection);
             }
 
-            if (type == EVENT_PRE_DRAW) {
+            if (type != EVENT_VIEW_REMOVED) {
                 // Did it change? if not continue
                 getLastChildRect(child, lastDrawRect);
                 if (lastDrawRect.equals(drawRect)) {
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 22f4546..10da563 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -201,8 +201,6 @@
         mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
         mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | GravityCompat.START);
 
-        mHintExpanded = mCollapsingTextHelper.getExpansionFraction() == 1f;
-
         final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs,
                 R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
         mHintEnabled = a.getBoolean(R.styleable.TextInputLayout_hintEnabled, true);
@@ -380,8 +378,8 @@
 
         updatePasswordToggleView();
 
-        // Update the label visibility with no animation
-        updateLabelState(false);
+        // Update the label visibility with no animation, but force a state change
+        updateLabelState(false, true);
     }
 
     private void updateInputLayoutMargins() {
@@ -408,6 +406,10 @@
     }
 
     void updateLabelState(boolean animate) {
+        updateLabelState(animate, false);
+    }
+
+    void updateLabelState(final boolean animate, final boolean force) {
         final boolean isEnabled = isEnabled();
         final boolean hasText = mEditText != null && !TextUtils.isEmpty(mEditText.getText());
         final boolean isFocused = arrayContains(getDrawableState(), android.R.attr.state_focused);
@@ -427,12 +429,12 @@
 
         if (hasText || (isEnabled() && (isFocused || isErrorShowing))) {
             // We should be showing the label so do so if it isn't already
-            if (mHintExpanded) {
+            if (force || mHintExpanded) {
                 collapseHint(animate);
             }
         } else {
             // We should not be showing the label so hide it
-            if (!mHintExpanded) {
+            if (force || !mHintExpanded) {
                 expandHint(animate);
             }
         }
diff --git a/design/tests/AndroidManifest.xml b/design/tests/AndroidManifest.xml
index 29d46e0..886540d 100755
--- a/design/tests/AndroidManifest.xml
+++ b/design/tests/AndroidManifest.xml
@@ -88,6 +88,10 @@
         <activity
                 android:name="android.support.v7.app.AppCompatActivity"/>
 
+        <activity
+            android:name="android.support.design.widget.AppBarLayoutCollapsePinTestActivity"
+            android:theme="@style/Theme.TranslucentStatus"/>
+
     </application>
 
     <instrumentation
diff --git a/design/tests/res/layout/design_appbar_toolbar_collapse_pin_restore_test.xml b/design/tests/res/layout/design_appbar_toolbar_collapse_pin_restore_test.xml
new file mode 100644
index 0000000..fbe031b
--- /dev/null
+++ b/design/tests/res/layout/design_appbar_toolbar_collapse_pin_restore_test.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<android.support.design.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/coordinator_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+    <android.support.design.widget.AppBarLayout
+        android:id="@+id/app_bar"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/appbar_height"
+        android:fitsSystemWindows="true"
+        app:expanded="true">
+
+        <android.support.design.widget.CollapsingToolbarLayout
+            android:id="@+id/collapsing_app_bar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed">
+
+            <android.support.v7.widget.Toolbar
+                android:id="@+id/toolbar"
+                android:layout_height="?attr/actionBarSize"
+                android:layout_width="match_parent"
+                app:layout_collapseMode="pin"/>
+
+        </android.support.design.widget.CollapsingToolbarLayout>
+
+    </android.support.design.widget.AppBarLayout>
+
+    <include layout="@layout/include_appbar_scrollview" />
+
+</android.support.design.widget.CoordinatorLayout>
diff --git a/design/tests/res/layout/design_text_input.xml b/design/tests/res/layout/design_text_input.xml
index f4bd1d1..4dba825 100644
--- a/design/tests/res/layout/design_text_input.xml
+++ b/design/tests/res/layout/design_text_input.xml
@@ -58,4 +58,18 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"/>
 
+    <android.support.design.widget.TextInputLayout
+        android:id="@+id/textinput_with_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <android.support.design.widget.TextInputEditText
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:imeOptions="flagNoExtractUi"
+            android:hint="@string/textinput_hint"
+            android:text="@string/snackbar_text"/>
+
+    </android.support.design.widget.TextInputLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/AppBarLayoutMatchers.java b/design/tests/src/android/support/design/testutils/AppBarLayoutMatchers.java
new file mode 100755
index 0000000..4d6fc63
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/AppBarLayoutMatchers.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.testutils;
+
+import android.support.design.widget.AppBarLayout;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class AppBarLayoutMatchers {
+
+    /**
+     * Returns a matcher that matches AppBarLayouts which are collapsed.
+     */
+    public static Matcher isCollapsed() {
+        return new TypeSafeMatcher<AppBarLayout>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("AppBarLayout is collapsed");
+            }
+
+            @Override
+            protected boolean matchesSafely(AppBarLayout item) {
+                return item.getBottom() == (item.getHeight() - item.getTotalScrollRange());
+            }
+        };
+    }
+
+}
diff --git a/design/tests/src/android/support/design/testutils/SwipeUtils.java b/design/tests/src/android/support/design/testutils/SwipeUtils.java
new file mode 100644
index 0000000..cf92883
--- /dev/null
+++ b/design/tests/src/android/support/design/testutils/SwipeUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.testutils;
+
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralSwipeAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Swipe;
+import android.view.View;
+
+public class SwipeUtils {
+
+    public static GeneralSwipeAction swipeUp(final int swipeX,
+            final int swipeStartY, final int swipeAmountY) {
+        return new GeneralSwipeAction(
+                Swipe.SLOW,
+                new CoordinatesProvider() {
+                    @Override
+                    public float[] calculateCoordinates(View view) {
+                        return new float[] { swipeX, swipeStartY };
+                    }
+                },
+                new CoordinatesProvider() {
+                    @Override
+                    public float[] calculateCoordinates(View view) {
+                        return new float[] { swipeX, swipeStartY - swipeAmountY };
+                    }
+                },
+                Press.FINGER
+        );
+    }
+
+    public static GeneralSwipeAction swipeDown(final int swipeX,
+            final int swipeStartY, final int swipeAmountY) {
+        return new GeneralSwipeAction(
+                Swipe.SLOW,
+                new CoordinatesProvider() {
+                    @Override
+                    public float[] calculateCoordinates(View view) {
+                        return new float[] { swipeX, swipeStartY };
+                    }
+                },
+                new CoordinatesProvider() {
+                    @Override
+                    public float[] calculateCoordinates(View view) {
+                        return new float[] { swipeX, swipeStartY + swipeAmountY };
+                    }
+                },
+                Press.FINGER
+        );
+    }
+
+
+}
diff --git a/design/tests/src/android/support/design/testutils/TestUtils.java b/design/tests/src/android/support/design/testutils/TestUtils.java
index b9aae7a..2f9187d 100644
--- a/design/tests/src/android/support/design/testutils/TestUtils.java
+++ b/design/tests/src/android/support/design/testutils/TestUtils.java
@@ -16,7 +16,10 @@
 
 package android.support.design.testutils;
 
+import android.app.Activity;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -124,4 +127,20 @@
             }
         }
     }
+
+    /**
+     * Rotates the given Activity to either portrait or landscape, depending on the current
+     * orientation.
+     */
+    public static void rotateOrientation(@NonNull Activity activity) {
+        switch (activity.getResources().getConfiguration().orientation) {
+            case Configuration.ORIENTATION_PORTRAIT:
+                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+                break;
+            case Configuration.ORIENTATION_LANDSCAPE:
+            default:
+                activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+                break;
+        }
+    }
 }
\ No newline at end of file
diff --git a/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java b/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java
index 2f1f30d..9d13ce7 100644
--- a/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java
+++ b/design/tests/src/android/support/design/testutils/TestUtilsMatchers.java
@@ -22,6 +22,7 @@
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.design.widget.FloatingActionButton;
@@ -461,6 +462,24 @@
     }
 
     /**
+     * Returns a matcher that matches views which have a z-value greater than 0. Also matches if
+     * the platform we're running on does not support z-values.
+     */
+    public static Matcher<View> hasZ() {
+        return new TypeSafeMatcher<View>() {
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("has a z value greater than 0");
+            }
+
+            @Override
+            public boolean matchesSafely(View view) {
+                return Build.VERSION.SDK_INT < 21 || ViewCompat.getZ(view) > 0f;
+            }
+        };
+    }
+
+    /**
      * Returns a matcher that matches TextViews with the specified typeface.
      */
     public static Matcher withTypeface(@NonNull final Typeface typeface) {
diff --git a/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java b/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java
index fc131a4..6fd4470 100644
--- a/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarLayoutBaseTest.java
@@ -17,6 +17,8 @@
 package android.support.design.widget;
 
 import static android.support.design.testutils.CollapsingToolbarLayoutActions.setContentScrimColor;
+import static android.support.design.testutils.SwipeUtils.swipeDown;
+import static android.support.design.testutils.SwipeUtils.swipeUp;
 import static android.support.design.testutils.TestUtilsActions.setText;
 import static android.support.design.testutils.TestUtilsActions.setTitle;
 import static android.support.test.espresso.Espresso.onView;
@@ -35,15 +37,10 @@
 import android.support.annotation.StringRes;
 import android.support.design.test.R;
 import android.support.design.testutils.Shakespeare;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralSwipeAction;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Swipe;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
 import android.text.TextUtils;
-import android.view.View;
 import android.widget.TextView;
 
 import org.hamcrest.Description;
@@ -64,38 +61,12 @@
 
     protected static void performVerticalSwipeUpGesture(@IdRes int containerId, final int swipeX,
             final int swipeStartY, final int swipeAmountY) {
-        onView(withId(containerId)).perform(new GeneralSwipeAction(
-                Swipe.SLOW,
-                new CoordinatesProvider() {
-                    @Override
-                    public float[] calculateCoordinates(View view) {
-                        return new float[] { swipeX, swipeStartY };
-                    }
-                },
-                new CoordinatesProvider() {
-                    @Override
-                    public float[] calculateCoordinates(View view) {
-                        return new float[] { swipeX, swipeStartY - swipeAmountY };
-                    }
-                }, Press.FINGER));
+        onView(withId(containerId)).perform(swipeUp(swipeX, swipeStartY, swipeAmountY));
     }
 
     protected static void performVerticalSwipeDownGesture(@IdRes int containerId, final int swipeX,
             final int swipeStartY, final int swipeAmountY) {
-        onView(withId(containerId)).perform(new GeneralSwipeAction(
-                Swipe.SLOW,
-                new CoordinatesProvider() {
-                    @Override
-                    public float[] calculateCoordinates(View view) {
-                        return new float[] { swipeX, swipeStartY };
-                    }
-                },
-                new CoordinatesProvider() {
-                    @Override
-                    public float[] calculateCoordinates(View view) {
-                        return new float[] { swipeX, swipeStartY + swipeAmountY };
-                    }
-                }, Press.FINGER));
+        onView(withId(containerId)).perform(swipeDown(swipeX, swipeStartY, swipeAmountY));
     }
 
     @CallSuper
diff --git a/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java b/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java
new file mode 100644
index 0000000..38ea4fc
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/AppBarLayoutCollapsePinTestActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import android.support.design.test.R;
+import android.support.v7.widget.Toolbar;
+
+public class AppBarLayoutCollapsePinTestActivity extends BaseTestActivity {
+
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.design_appbar_toolbar_collapse_pin_restore_test;
+    }
+
+    @Override
+    protected void onContentViewSet() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+    }
+}
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java
new file mode 100644
index 0000000..52f4ab2
--- /dev/null
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarStateRestoreTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.design.widget;
+
+import static android.support.design.testutils.AppBarLayoutMatchers.isCollapsed;
+import static android.support.design.testutils.SwipeUtils.swipeUp;
+import static android.support.design.testutils.TestUtils.rotateOrientation;
+import static android.support.design.testutils.TestUtilsMatchers.hasZ;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import android.app.Activity;
+import android.support.design.test.R;
+
+import org.junit.Test;
+
+public class AppBarWithCollapsingToolbarStateRestoreTest
+        extends BaseInstrumentationTestCase<AppBarLayoutCollapsePinTestActivity> {
+
+    public AppBarWithCollapsingToolbarStateRestoreTest() {
+        super(AppBarLayoutCollapsePinTestActivity.class);
+    }
+
+    @Test
+    public void testRotateAndRestore() {
+        Activity activity = mActivityTestRule.getActivity();
+        final AppBarLayout appBar = (AppBarLayout) activity.findViewById(R.id.app_bar);
+
+        // Swipe up and collapse the AppBarLayout
+        onView(withId(R.id.coordinator_layout))
+                .perform(swipeUp(
+                        appBar.getLeft() + (appBar.getWidth() / 2),
+                        appBar.getBottom() + 20,
+                        appBar.getHeight()));
+        onView(withId(R.id.app_bar))
+                .check(matches(hasZ()))
+                .check(matches(isCollapsed()));
+
+        // Now rotate the Activity
+        rotateOrientation(activity);
+
+        // And check that the app bar still is restored correctly
+        onView(withId(R.id.app_bar))
+                .check(matches(hasZ()))
+                .check(matches(isCollapsed()));
+    }
+
+}
diff --git a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
index 37a58a6..f06a85a 100644
--- a/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/BottomNavigationViewTest.java
@@ -223,6 +223,16 @@
         checkAndVerifyExclusiveItem(menu, R.id.destination_people);
     }
 
+    @UiThreadTest
+    @Test
+    @SmallTest
+    public void testClearingMenu() throws Throwable {
+        mBottomNavigation.getMenu().clear();
+        assertEquals(0, mBottomNavigation.getMenu().size());
+        mBottomNavigation.inflateMenu(R.menu.bottom_navigation_view_content);
+        assertEquals(3, mBottomNavigation.getMenu().size());
+    }
+
     private void checkAndVerifyExclusiveItem(final Menu menu, final int id) throws Throwable {
         menu.findItem(id).setChecked(true);
         for (int i = 0; i < menu.size(); i++) {
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index 1d4f0a7..f8e4f99 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -16,6 +16,7 @@
 
 package android.support.design.widget;
 
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.swipeUp;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -40,8 +41,8 @@
 import android.graphics.Rect;
 import android.support.design.test.R;
 import android.support.design.testutils.CoordinatorLayoutUtils;
+import android.support.design.testutils.CoordinatorLayoutUtils.DependentBehavior;
 import android.support.design.widget.CoordinatorLayout.Behavior;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.v4.view.ViewCompat;
@@ -69,13 +70,13 @@
 
     @Before
     public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mInstrumentation = getInstrumentation();
     }
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
     public void testSetFitSystemWindows() throws Throwable {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
         final View view = new View(col.getContext());
 
@@ -183,7 +184,7 @@
 
     @Test
     public void testInsetEdge() throws Throwable {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
         final View insetView = new View(col.getContext());
@@ -236,7 +237,7 @@
 
     @Test
     public void testDependentViewChanged() throws Throwable {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
         // Add two views, A & B, where B depends on A
@@ -250,7 +251,7 @@
         lpB.width = 100;
         lpB.height = 100;
         final CoordinatorLayout.Behavior behavior =
-                spy(new CoordinatorLayoutUtils.DependentBehavior(viewA));
+                spy(new DependentBehavior(viewA));
         lpB.setBehavior(behavior);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -282,7 +283,7 @@
 
     @Test
     public void testDependentViewRemoved() throws Throwable {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
         // Add two views, A & B, where B depends on A
@@ -290,7 +291,7 @@
         final View viewB = new View(col.getContext());
         final CoordinatorLayout.LayoutParams lpB = col.generateDefaultLayoutParams();
         final CoordinatorLayout.Behavior behavior =
-                spy(new CoordinatorLayoutUtils.DependentBehavior(viewA));
+                spy(new DependentBehavior(viewA));
         lpB.setBehavior(behavior);
 
         mActivityTestRule.runOnUiThread(new Runnable() {
@@ -316,7 +317,7 @@
 
     @Test
     public void testGetDependenciesAfterDependentViewRemoved() throws Throwable {
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
         // Add two views, A & B, where B depends on A
@@ -572,6 +573,55 @@
     }
 
     @Test
+    public void testNestedScrollingTriggeringDependentViewChanged() throws Throwable {
+        final CoordinatorLayoutActivity activity = mActivityTestRule.getActivity();
+        final CoordinatorLayout col = activity.mCoordinatorLayout;
+
+        // First a NestedScrollView to trigger nested scrolling
+        final View scrollView = LayoutInflater.from(activity).inflate(
+                R.layout.include_nestedscrollview, col, false);
+
+        // Now create a View and Behavior which depend on the scrollview
+        final ImageView dependentView = new ImageView(activity);
+        final CoordinatorLayout.Behavior dependentBehavior = spy(new DependentBehavior(scrollView));
+
+        // Finally a view which accepts nested scrolling in the CoordinatorLayout
+        final ImageView nestedScrollAwareView = new ImageView(activity);
+
+        mActivityTestRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // First add the ScrollView
+                col.addView(scrollView);
+
+                // Now add the view which depends on the scrollview
+                CoordinatorLayout.LayoutParams clp = new CoordinatorLayout.LayoutParams(200, 200);
+                clp.setBehavior(dependentBehavior);
+                col.addView(dependentView, clp);
+
+                // Now add the nested scrolling aware view
+                clp = new CoordinatorLayout.LayoutParams(200, 200);
+                clp.setBehavior(new NestedScrollingBehavior());
+                col.addView(nestedScrollAwareView, clp);
+            }
+        });
+
+        // Wait for any layouts, and reset the Behavior so that the call counts are 0
+        getInstrumentation().waitForIdleSync();
+        reset(dependentBehavior);
+
+        // Now vertically swipe up on the NSV, causing nested scrolling to occur
+        onView(withId(R.id.nested_scrollview)).perform(swipeUp());
+
+        // Verify that the Behavior's onDependentViewChanged is not called due to the
+        // nested scroll
+        verify(dependentBehavior, never()).onDependentViewChanged(
+                eq(col), // parent
+                eq(dependentView), // child
+                eq(scrollView)); // axes
+    }
+
+    @Test
     public void testDodgeInsetViewWithEmptyBounds() throws Throwable {
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
 
@@ -614,9 +664,9 @@
                 .getInsetDodgeRect(same(col), same(view), any(Rect.class));
     }
 
-    public static class NestedScrollingBehavior extends CoordinatorLayout.Behavior<ImageView> {
+    public static class NestedScrollingBehavior extends CoordinatorLayout.Behavior<View> {
         @Override
-        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, ImageView child,
+        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child,
                 View directTargetChild, View target, int nestedScrollAxes) {
             // Return true so that we always accept nested scroll events
             return true;
diff --git a/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java b/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
index 069055b..e8cc701 100644
--- a/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
+++ b/design/tests/src/android/support/design/widget/FloatingActionButtonTest.java
@@ -33,11 +33,15 @@
 import static android.support.design.testutils.TestUtilsMatchers.withFabContentHeight;
 import static android.support.design.widget.DesignViewActions.setVisibility;
 import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import static org.hamcrest.Matchers.not;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
 import android.content.res.ColorStateList;
@@ -229,6 +233,19 @@
     }
 
     @Test
+    public void testOnClickListener() {
+        final View.OnClickListener listener = mock(View.OnClickListener.class);
+        final View view = mActivityTestRule.getActivity().findViewById(R.id.fab_standard);
+        view.setOnClickListener(listener);
+
+        // Click on the fab
+        onView(withId(R.id.fab_standard)).perform(click());
+
+        // And verify that the listener was invoked once
+        verify(listener, times(1)).onClick(view);
+    }
+
+    @Test
     public void testSetCompatElevation() {
         onView(withId(R.id.fab_standard))
                 .perform(setEnabled(false))
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
index 4944170..2ba1e6e 100755
--- a/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutTest.java
@@ -460,6 +460,12 @@
     }
 
     @Test
+    public void testTextSetViaAttributeCollapsedHint() {
+        onView(withId(R.id.textinput_with_text))
+                .check(isHintExpanded(false));
+    }
+
+    @Test
     public void testFocusMovesToEditTextWithPasswordEnabled() {
         // Focus the preceding EditText
         onView(withId(R.id.textinput_edittext))
diff --git a/exifinterface/build.gradle b/exifinterface/build.gradle
index 42f50c1..f6fd633 100644
--- a/exifinterface/build.gradle
+++ b/exifinterface/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'exifinterface'
 
 dependencies {
@@ -33,28 +32,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
index a8323dc..879be4a 100644
--- a/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
+++ b/fragment/api21/android/support/v4/app/FragmentTransitionCompat21.java
@@ -24,7 +24,6 @@
 import android.transition.TransitionSet;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -343,18 +342,15 @@
             }
         }
 
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                        for (int i = 0; i < numSharedElements; i++) {
-                            sharedElementsIn.get(i).setTransitionName(inNames.get(i));
-                            sharedElementsOut.get(i).setTransitionName(outNames.get(i));
-                        }
-                        return true;
-                    }
-                });
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                for (int i = 0; i < numSharedElements; i++) {
+                    sharedElementsIn.get(i).setTransitionName(inNames.get(i));
+                    sharedElementsOut.get(i).setTransitionName(outNames.get(i));
+                }
+            }
+        });
     }
 
     /**
@@ -406,23 +402,20 @@
 
     public static void setNameOverridesUnoptimized(final View sceneRoot,
             final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                        final int numSharedElements = sharedElementsIn.size();
-                        for (int i = 0; i < numSharedElements; i++) {
-                            View view = sharedElementsIn.get(i);
-                            String name = view.getTransitionName();
-                            if (name != null) {
-                                String inName = findKeyForValue(nameOverrides, name);
-                                view.setTransitionName(inName);
-                            }
-                        }
-                        return true;
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                final int numSharedElements = sharedElementsIn.size();
+                for (int i = 0; i < numSharedElements; i++) {
+                    View view = sharedElementsIn.get(i);
+                    String name = view.getTransitionName();
+                    if (name != null) {
+                        String inName = findKeyForValue(nameOverrides, name);
+                        view.setTransitionName(inName);
                     }
-                });
+                }
+            }
+        });
     }
 
     /**
@@ -566,20 +559,17 @@
 
     public static void scheduleNameReset(final ViewGroup sceneRoot,
             final ArrayList<View> sharedElementsIn, final Map<String, String> nameOverrides) {
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                        final int numSharedElements = sharedElementsIn.size();
-                        for (int i = 0; i < numSharedElements; i++) {
-                            final View view = sharedElementsIn.get(i);
-                            final String name = view.getTransitionName();
-                            final String inName = nameOverrides.get(name);
-                            view.setTransitionName(inName);
-                        }
-                        return true;
-                    }
-                });
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                final int numSharedElements = sharedElementsIn.size();
+                for (int i = 0; i < numSharedElements; i++) {
+                    final View view = sharedElementsIn.get(i);
+                    final String name = view.getTransitionName();
+                    final String inName = nameOverrides.get(name);
+                    view.setTransitionName(inName);
+                }
+            }
+        });
     }
 }
diff --git a/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java b/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java
new file mode 100644
index 0000000..502af1f
--- /dev/null
+++ b/fragment/api21/android/support/v4/app/OneShotPreDrawListener.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.app;
+
+
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+/**
+ * An OnPreDrawListener that will remove itself after one OnPreDraw call. Typical
+ * usage is:
+ * <pre><code>
+ *     OneShotPreDrawListener.add(view, () -> { view.doSomething(); })
+ * </code></pre>
+ * <p>
+ * The onPreDraw always returns true.
+ * <p>
+ * The listener will also remove itself from the ViewTreeObserver when the view
+ * is detached from the view hierarchy. In that case, the Runnable will never be
+ * executed.
+ */
+class OneShotPreDrawListener implements ViewTreeObserver.OnPreDrawListener,
+        View.OnAttachStateChangeListener {
+    private final View mView;
+    private ViewTreeObserver mViewTreeObserver;
+    private final Runnable mRunnable;
+
+    private OneShotPreDrawListener(View view, Runnable runnable) {
+        mView = view;
+        mViewTreeObserver = view.getViewTreeObserver();
+        mRunnable = runnable;
+    }
+
+    /**
+     * Creates a OneShotPreDrawListener and adds it to view's ViewTreeObserver.
+     * @param view The view whose ViewTreeObserver the OnPreDrawListener should listen.
+     * @param runnable The Runnable to execute in the OnPreDraw (once)
+     * @return The added OneShotPreDrawListener. It can be removed prior to
+     * the onPreDraw by calling {@link #removeListener()}.
+     */
+    public static OneShotPreDrawListener add(View view, Runnable runnable) {
+        OneShotPreDrawListener listener = new OneShotPreDrawListener(view, runnable);
+        view.getViewTreeObserver().addOnPreDrawListener(listener);
+        view.addOnAttachStateChangeListener(listener);
+        return listener;
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        removeListener();
+        mRunnable.run();
+        return true;
+    }
+
+    /**
+     * Removes the listener from the ViewTreeObserver. This is useful to call if the
+     * callback should be removed prior to {@link #onPreDraw()}.
+     */
+    public void removeListener() {
+        if (mViewTreeObserver.isAlive()) {
+            mViewTreeObserver.removeOnPreDrawListener(this);
+        } else {
+            mView.getViewTreeObserver().removeOnPreDrawListener(this);
+        }
+        mView.removeOnAttachStateChangeListener(this);
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View v) {
+        mViewTreeObserver = v.getViewTreeObserver();
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View v) {
+        removeListener();
+    }
+}
diff --git a/fragment/build.gradle b/fragment/build.gradle
index 26e9f1c..7c9098a 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -18,9 +18,6 @@
     testCompile 'junit:junit:4.12'
 }
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-
 android {
     compileSdkVersion project.ext.currentSdk
 
@@ -50,11 +47,6 @@
         sourceCompatibility JavaVersion.VERSION_1_7
         targetCompatibility JavaVersion.VERSION_1_7
     }
-
-    testOptions {
-        unitTests.returnDefaultValues = true
-        compileSdkVersion project.ext.currentSdk
-    }
 }
 
 android.libraryVariants.all { variant ->
@@ -65,22 +57,6 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
@@ -88,7 +64,6 @@
         exclude('android/service/media/**')
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/fragment/java/android/support/v4/app/BackStackRecord.java b/fragment/java/android/support/v4/app/BackStackRecord.java
index 85ded71..acfa924 100644
--- a/fragment/java/android/support/v4/app/BackStackRecord.java
+++ b/fragment/java/android/support/v4/app/BackStackRecord.java
@@ -216,7 +216,7 @@
 
     ArrayList<String> mSharedElementSourceNames;
     ArrayList<String> mSharedElementTargetNames;
-    boolean mAllowOptimization = true;
+    boolean mAllowOptimization = false;
 
     @Override
     public String toString() {
@@ -513,6 +513,12 @@
             if (mSharedElementSourceNames == null) {
                 mSharedElementSourceNames = new ArrayList<String>();
                 mSharedElementTargetNames = new ArrayList<String>();
+            } else if (mSharedElementTargetNames.contains(name)) {
+                throw new IllegalArgumentException("A shared element with the target name '"
+                        + name + "' has already been added to the transaction.");
+            } else if (mSharedElementSourceNames.contains(transitionName)) {
+                throw new IllegalArgumentException("A shared element with the source name '"
+                        + transitionName + " has already been added to the transaction.");
             }
 
             mSharedElementSourceNames.add(transitionName);
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index 829e974..ecc24d0 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -1306,15 +1306,17 @@
                                 }
                                 if (container != null) {
                                     container.addView(f.mView);
-                                    f.mIsNewlyAdded = true;
                                 }
                                 if (f.mHidden) {
                                     f.mView.setVisibility(View.GONE);
-                                    f.mIsNewlyAdded = false; // No animation
                                 }
                                 f.onViewCreated(f.mView, f.mSavedFragmentState);
                                 dispatchOnFragmentViewCreated(f, f.mView, f.mSavedFragmentState,
                                         false);
+                                // Only animate the view if it is visible. This is done after
+                                // dispatchOnFragmentViewCreated in case visibility is changed
+                                f.mIsNewlyAdded = (f.mView.getVisibility() == View.VISIBLE)
+                                        && f.mContainer != null;
                             } else {
                                 f.mInnerView = null;
                             }
@@ -2353,7 +2355,10 @@
                 final int stateAfterAnimating = fragment.getStateAfterAnimating();
                 final View animatingAway = fragment.getAnimatingAway();
                 fragment.setAnimatingAway(null);
-                animatingAway.clearAnimation();
+                Animation animation = animatingAway.getAnimation();
+                if (animation != null) {
+                    animation.cancel();
+                }
                 moveToState(fragment, stateAfterAnimating, 0, 0, false);
             }
         }
diff --git a/fragment/java/android/support/v4/app/FragmentTransaction.java b/fragment/java/android/support/v4/app/FragmentTransaction.java
index b03f002..0171681 100644
--- a/fragment/java/android/support/v4/app/FragmentTransaction.java
+++ b/fragment/java/android/support/v4/app/FragmentTransaction.java
@@ -296,7 +296,7 @@
      * With optimization, fragment B cannot expect fragment A to exist when
      * it has been created because fragment A's add/remove will be optimized out.
      * <p>
-     * The default is {@code true}.
+     * The default is {@code false}.
      *
      * @param allowOptimization {@code true} to enable optimizing operations
      *                          or {@code false} to disable optimizing
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
index 9efd1bc..aa64859 100644
--- a/fragment/java/android/support/v4/app/FragmentTransition.java
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -22,7 +22,6 @@
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -250,15 +249,12 @@
             FragmentTransitionCompat21.scheduleHideFragmentView(exitTransition,
                     exitingFragment.getView(), exitingViews);
             final ViewGroup container = exitingFragment.mContainer;
-            container.getViewTreeObserver().addOnPreDrawListener(
-                    new ViewTreeObserver.OnPreDrawListener() {
-                        @Override
-                        public boolean onPreDraw() {
-                            container.getViewTreeObserver().removeOnPreDrawListener(this);
-                            setViewVisibility(exitingViews, View.INVISIBLE);
-                            return true;
-                        }
-                    });
+            OneShotPreDrawListener.add(container, new Runnable() {
+                @Override
+                public void run() {
+                    setViewVisibility(exitingViews, View.INVISIBLE);
+                }
+            });
         }
     }
 
@@ -356,33 +352,29 @@
             final ArrayList<View> sharedElementsIn,
             final Object enterTransition, final ArrayList<View> enteringViews,
             final Object exitTransition, final ArrayList<View> exitingViews) {
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                if (enterTransition != null) {
+                    FragmentTransitionCompat21.removeTarget(enterTransition,
+                            nonExistentView);
+                    ArrayList<View> views = configureEnteringExitingViews(
+                            enterTransition, inFragment, sharedElementsIn, nonExistentView);
+                    enteringViews.addAll(views);
+                }
 
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-
-                        if (enterTransition != null) {
-                            FragmentTransitionCompat21.removeTarget(enterTransition,
-                                    nonExistentView);
-                            ArrayList<View> views = configureEnteringExitingViews(
-                                    enterTransition, inFragment, sharedElementsIn, nonExistentView);
-                            enteringViews.addAll(views);
-                        }
-
-                        if (exitingViews != null) {
-                            ArrayList<View> tempExiting = new ArrayList<>();
-                            tempExiting.add(nonExistentView);
-                            FragmentTransitionCompat21.replaceTargets(exitTransition, exitingViews,
-                                    tempExiting);
-                            exitingViews.clear();
-                            exitingViews.add(nonExistentView);
-                        }
-
-                        return true;
+                if (exitingViews != null) {
+                    if (exitTransition != null) {
+                        ArrayList<View> tempExiting = new ArrayList<>();
+                        tempExiting.add(nonExistentView);
+                        FragmentTransitionCompat21.replaceTargets(exitTransition, exitingViews,
+                                tempExiting);
                     }
-                });
+                    exitingViews.clear();
+                    exitingViews.add(nonExistentView);
+                }
+            }
+        });
     }
 
     /**
@@ -486,9 +478,17 @@
 
         if (nameOverrides.isEmpty()) {
             sharedElementTransition = null;
+            if (outSharedElements != null) {
+                outSharedElements.clear();
+            }
+            if (inSharedElements != null) {
+                inSharedElements.clear();
+            }
         } else {
-            sharedElementsOut.addAll(outSharedElements.values());
-            sharedElementsIn.addAll(inSharedElements.values());
+            addSharedElementsWithMatchingNames(sharedElementsOut, outSharedElements,
+                    nameOverrides.keySet());
+            addSharedElementsWithMatchingNames(sharedElementsIn, inSharedElements,
+                    nameOverrides.values());
         }
 
         if (enterTransition == null && exitTransition == null && sharedElementTransition == null) {
@@ -519,23 +519,39 @@
             epicenterView = null;
         }
 
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                        callSharedElementStartEnd(inFragment, outFragment, inIsPop,
-                                inSharedElements, false);
-                        if (epicenterView != null) {
-                            FragmentTransitionCompat21.getBoundsOnScreen(epicenterView, epicenter);
-                        }
-                        return true;
-                    }
-                });
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+                        inSharedElements, false);
+                if (epicenterView != null) {
+                    FragmentTransitionCompat21.getBoundsOnScreen(epicenterView, epicenter);
+                }
+            }
+        });
         return sharedElementTransition;
     }
 
     /**
+     * Add Views from sharedElements into views that have the transitionName in the
+     * nameOverridesSet.
+     *
+     * @param views               Views list to add shared elements to
+     * @param sharedElements      List of shared elements
+     * @param nameOverridesSet    The transition names for all views to be copied from
+     *                            sharedElements to views.
+     */
+    private static void addSharedElementsWithMatchingNames(ArrayList<View> views,
+            ArrayMap<String, View> sharedElements, Collection<String> nameOverridesSet) {
+        for (int i = sharedElements.size() - 1; i >= 0; i--) {
+            View view = sharedElements.valueAt(i);
+            if (nameOverridesSet.contains(ViewCompat.getTransitionName(view))) {
+                views.add(view);
+            }
+        }
+    }
+
+    /**
      * Configures the shared elements of an unoptimized fragment transaction's transition.
      * This retrieves the shared elements of the incoming fragments, and schedules capturing
      * the incoming fragment's shared elements. It also maps the views, and sets up the epicenter
@@ -612,38 +628,36 @@
             inEpicenter = null;
         }
 
+
         final Object finalSharedElementTransition = sharedElementTransition;
+        OneShotPreDrawListener.add(sceneRoot, new Runnable() {
+            @Override
+            public void run() {
+                ArrayMap<String, View> inSharedElements = captureInSharedElements(
+                        nameOverrides, finalSharedElementTransition, fragments);
 
-        sceneRoot.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
-                        ArrayMap<String, View> inSharedElements = captureInSharedElements(
-                                nameOverrides, finalSharedElementTransition, fragments);
+                if (inSharedElements != null) {
+                    sharedElementsIn.addAll(inSharedElements.values());
+                    sharedElementsIn.add(nonExistentView);
+                }
 
-                        if (inSharedElements != null) {
-                            sharedElementsIn.addAll(inSharedElements.values());
-                            sharedElementsIn.add(nonExistentView);
-                        }
+                callSharedElementStartEnd(inFragment, outFragment, inIsPop,
+                        inSharedElements, false);
+                if (finalSharedElementTransition != null) {
+                    FragmentTransitionCompat21.swapSharedElementTargets(
+                            finalSharedElementTransition, sharedElementsOut,
+                            sharedElementsIn);
 
-                        callSharedElementStartEnd(inFragment, outFragment, inIsPop,
-                                inSharedElements, false);
-                        if (finalSharedElementTransition != null) {
-                            FragmentTransitionCompat21.swapSharedElementTargets(
-                                    finalSharedElementTransition, sharedElementsOut,
-                                    sharedElementsIn);
-
-                            final View inEpicenterView = getInEpicenterView(inSharedElements,
-                                    fragments, enterTransition, inIsPop);
-                            if (inEpicenterView != null) {
-                                FragmentTransitionCompat21.getBoundsOnScreen(inEpicenterView,
-                                        inEpicenter);
-                            }
-                        }
-                        return true;
+                    final View inEpicenterView = getInEpicenterView(inSharedElements,
+                            fragments, enterTransition, inIsPop);
+                    if (inEpicenterView != null) {
+                        FragmentTransitionCompat21.getBoundsOnScreen(inEpicenterView,
+                                inEpicenter);
                     }
-                });
+                }
+            }
+        });
+
         return sharedElementTransition;
     }
 
diff --git a/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
index 9f6b263..873e36d 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentAnimationTest.java
@@ -234,6 +234,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .add(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -258,6 +259,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .remove(fragment)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -274,6 +276,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -284,6 +287,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -307,6 +311,7 @@
         final AnimatorFragment fragment1 = new AnimatorFragment();
         fm.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         assertEquals(0, fragment1.numAnimators);
@@ -318,6 +323,7 @@
                 .setCustomAnimations(ENTER, EXIT, POP_ENTER, POP_EXIT)
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
diff --git a/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
index 39dbf11..08be463 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentOptimizationTest.java
@@ -62,10 +62,12 @@
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .replace(R.id.fragmentContainer, fragment2)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.executePendingTransactions();
             }
@@ -90,7 +92,11 @@
     public void startWithPop() throws Throwable {
         // Start with a single fragment on the back stack
         final CountCallsFragment fragment1 = new CountCallsFragment();
-        mFM.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+        mFM.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .addToBackStack(null)
+                .setAllowOptimization(true)
+                .commit();
 
         FragmentTestUtil.executePendingTransactions(mActivityRule);
         FragmentTestUtil.assertChildren(mContainer, fragment1);
@@ -103,6 +109,7 @@
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.executePendingTransactions();
             }
@@ -126,11 +133,13 @@
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.popBackStack();
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment2)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.executePendingTransactions();
             }
@@ -156,9 +165,18 @@
                 id[0] = mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
-                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().remove(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .remove(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
                 mFM.executePendingTransactions();
             }
         });
@@ -186,6 +204,7 @@
         int id = mFM.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.executePendingTransactions(mActivityRule);
         assertEquals(1, fragment1.onCreateViewCount);
@@ -196,10 +215,12 @@
                 mFM.beginTransaction()
                         .remove(fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.executePendingTransactions();
             }
@@ -223,6 +244,7 @@
         int id = mFM.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.executePendingTransactions(mActivityRule);
         assertEquals(1, fragment1.onAttachCount);
@@ -231,9 +253,21 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
                 mFM.executePendingTransactions();
             }
         });
@@ -268,6 +302,7 @@
                 .add(R.id.fragmentContainer, fragment1)
                 .detach(fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.executePendingTransactions(mActivityRule);
 
@@ -281,9 +316,21 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
                 mFM.executePendingTransactions();
             }
         });
@@ -316,6 +363,7 @@
                 .add(R.id.fragmentContainer, fragment1)
                 .hide(fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.executePendingTransactions(mActivityRule);
         assertEquals(0, fragment1.onShowCount);
@@ -328,18 +376,22 @@
                 mFM.beginTransaction()
                         .show(fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .remove(fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .hide(fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.executePendingTransactions();
             }
@@ -360,8 +412,16 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
                 mFM.executePendingTransactions();
             }
         });
@@ -380,10 +440,26 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
                 mFM.executePendingTransactions();
             }
         });
@@ -406,6 +482,7 @@
         int id = mFM.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.executePendingTransactions(mActivityRule);
         assertEquals(0, fragment1.onShowCount);
@@ -415,10 +492,26 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                mFM.beginTransaction().hide(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().detach(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().attach(fragment1).addToBackStack(null).commit();
-                mFM.beginTransaction().show(fragment1).addToBackStack(null).commit();
+                mFM.beginTransaction()
+                        .hide(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .detach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .attach(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
+                mFM.beginTransaction()
+                        .show(fragment1)
+                        .addToBackStack(null)
+                        .setAllowOptimization(true)
+                        .commit();
                 mFM.executePendingTransactions();
             }
         });
@@ -443,6 +536,7 @@
         int id = mFM.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.executePendingTransactions(mActivityRule);
         FragmentTestUtil.assertChildren(mContainer, fragment1);
@@ -455,10 +549,12 @@
                 mFM.beginTransaction()
                         .replace(R.id.fragmentContainer, fragment2)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
 
                 mFM.executePendingTransactions();
@@ -480,6 +576,7 @@
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.popBackStack();
                 mFM.executePendingTransactions();
@@ -503,9 +600,11 @@
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .replace(R.id.fragmentContainer, fragment2)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.popBackStack();
                 mFM.executePendingTransactions();
@@ -529,6 +628,7 @@
                 mFM.beginTransaction()
                         .add(R.id.fragmentContainer, fragment1)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
                 mFM.beginTransaction()
                         .replace(R.id.fragmentContainer, fragment2)
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
index bc8e9e1..ab319b7 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTransitionTest.java
@@ -19,6 +19,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -26,6 +27,7 @@
 import android.app.Instrumentation;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Bundle;
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
@@ -440,6 +442,7 @@
                 .addSharedElement(startGreen, "greenSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -592,6 +595,109 @@
         verifyNoOtherTransitions(fragment2);
     }
 
+    // Ensure that shared element without matching transition name doesn't error out
+    @Test
+    public void sharedElementMismatch() throws Throwable {
+        final TransitionFragment fragment1 = setupInitialFragment();
+
+        // Now do a transition to scene2
+        TransitionFragment fragment2 = new TransitionFragment();
+        fragment2.setLayoutId(R.layout.scene2);
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+        final Rect startBlueBounds = getBoundsOnScreen(startBlue);
+
+        mFragmentManager.beginTransaction()
+                .addSharedElement(startBlue, "fooSquare")
+                .replace(R.id.fragmentContainer, fragment2)
+                .setAllowOptimization(mOptimize)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        fragment1.waitForTransition();
+        fragment2.waitForTransition();
+
+        final View endBlue = findBlue();
+        final View endGreen = findGreen();
+
+        if (mOptimize) {
+            verifyAndClearTransition(fragment1.exitTransition, null, startGreen, startBlue);
+        } else {
+            verifyAndClearTransition(fragment1.exitTransition, startBlueBounds, startGreen);
+            verifyAndClearTransition(fragment2.sharedElementEnter, startBlueBounds, startBlue);
+        }
+        verifyNoOtherTransitions(fragment1);
+
+        verifyAndClearTransition(fragment2.enterTransition, null, endGreen, endBlue);
+        verifyNoOtherTransitions(fragment2);
+    }
+
+    // Ensure that using the same source or target shared element results in an exception.
+    @Test
+    public void sharedDuplicateTargetNames() throws Throwable {
+        setupInitialFragment();
+
+        final View startBlue = findBlue();
+        final View startGreen = findGreen();
+
+        FragmentTransaction ft = mFragmentManager.beginTransaction();
+        ft.addSharedElement(startBlue, "blueSquare");
+        try {
+            ft.addSharedElement(startGreen, "blueSquare");
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            ft.addSharedElement(startBlue, "greenSquare");
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    // Test that invisible fragment views don't participate in transitions
+    @Test
+    public void invisibleNoTransitions() throws Throwable {
+        if (!mOptimize) {
+            return; // only optimized transitions can avoid interaction
+        }
+        // enter transition
+        TransitionFragment fragment = new InvisibleFragment();
+        fragment.setLayoutId(R.layout.scene1);
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .add(R.id.fragmentContainer, fragment)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+        fragment.waitForNoTransition();
+        verifyNoOtherTransitions(fragment);
+
+        // exit transition
+        mFragmentManager.beginTransaction()
+                .setAllowOptimization(mOptimize)
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+
+        fragment.waitForNoTransition();
+        verifyNoOtherTransitions(fragment);
+
+        // reenter transition
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        fragment.waitForNoTransition();
+        verifyNoOtherTransitions(fragment);
+
+        // return transition
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+        fragment.waitForNoTransition();
+        verifyNoOtherTransitions(fragment);
+    }
+
     private TransitionFragment setupInitialFragment() throws Throwable {
         TransitionFragment fragment1 = new TransitionFragment();
         fragment1.setLayoutId(R.layout.scene1);
@@ -888,6 +994,13 @@
             setSharedElementEnterTransition(sharedElementEnterTransition);
             setSharedElementReturnTransition(sharedElementReturnTransition);
         }
+    }
 
+    public static class InvisibleFragment extends TransitionFragment {
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            view.setVisibility(View.INVISIBLE);
+            super.onViewCreated(view, savedInstanceState);
+        }
     }
 }
diff --git a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
index 22a52ed..ad970c8 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.fail;
 
 import android.app.Instrumentation;
+import android.os.Bundle;
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
@@ -873,6 +874,34 @@
         assertNotNull(findViewById(R.id.textC));
     }
 
+    // Test that adding a fragment with invisible or gone views does not end up with the view
+    // being visible
+    @Test
+    public void addInvisibleAndGoneFragments() throws Throwable {
+        FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+        ViewGroup container = (ViewGroup)
+                mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final StrictViewFragment fragment1 = new InvisibleFragment();
+        fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment1);
+
+        assertEquals(View.INVISIBLE, fragment1.getView().getVisibility());
+
+        final InvisibleFragment fragment2 = new InvisibleFragment();
+        fragment2.visibility = View.GONE;
+        fm.beginTransaction()
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+        FragmentTestUtil.executePendingTransactions(mActivityRule);
+        FragmentTestUtil.assertChildren(container, fragment2);
+
+        assertEquals(View.GONE, fragment2.getView().getVisibility());
+    }
+
     private View findViewById(int viewId) {
         return mActivityRule.getActivity().findViewById(viewId);
     }
@@ -886,4 +915,14 @@
                     fragments[i].getView());
         }
     }
+
+    public static class InvisibleFragment extends StrictViewFragment {
+        public int visibility = View.INVISIBLE;
+
+        @Override
+        public void onViewCreated(View view, Bundle savedInstanceState) {
+            view.setVisibility(visibility);
+            super.onViewCreated(view, savedInstanceState);
+        }
+    }
 }
diff --git a/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
index b20298d..fe41976 100644
--- a/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
+++ b/fragment/tests/java/android/support/v4/app/PostponedTransitionTest.java
@@ -61,6 +61,7 @@
         mBeginningFragment = new PostponedFragment1();
         fm.beginTransaction()
                 .add(R.id.fragmentContainer, mBeginningFragment)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -81,6 +82,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -126,12 +128,14 @@
                         .addSharedElement(startBlue, "blueSquare")
                         .replace(R.id.fragmentContainer, fragment2)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
 
                 fm.beginTransaction()
                         .addSharedElement(startBlue, "blueSquare")
                         .replace(R.id.fragmentContainer, fragment3)
                         .addToBackStack(null)
+                        .setAllowOptimization(true)
                         .commit();
             }
         });
@@ -176,6 +180,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -187,6 +192,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment3)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         // This should cancel the mBeginningFragment -> fragment2 transition
@@ -238,6 +244,7 @@
                 .attach(fragment2)
                 .show(fragment2)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -266,7 +273,10 @@
     @Test
     public void differentContainers() throws Throwable {
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
-        fm.beginTransaction().remove(mBeginningFragment).commit();
+        fm.beginTransaction()
+                .remove(mBeginningFragment)
+                .setAllowOptimization(true)
+                .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
 
@@ -276,6 +286,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -294,6 +305,7 @@
                 .addSharedElement(startBlue1, "blueSquare")
                 .replace(R.id.fragmentContainer1, fragment3)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -306,6 +318,7 @@
                 .addSharedElement(startBlue2, "blueSquare")
                 .replace(R.id.fragmentContainer2, fragment4)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -358,7 +371,10 @@
     @Test
     public void outOfOrderContainers() throws Throwable {
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
-        fm.beginTransaction().remove(mBeginningFragment).commit();
+        fm.beginTransaction()
+                .remove(mBeginningFragment)
+                .setAllowOptimization(true)
+                .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
 
@@ -368,6 +384,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -386,6 +403,7 @@
                 .addSharedElement(startBlue1, "blueSquare")
                 .replace(R.id.fragmentContainer1, fragment3)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -398,6 +416,7 @@
                 .addSharedElement(startBlue2, "blueSquare")
                 .replace(R.id.fragmentContainer2, fragment4)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -450,7 +469,10 @@
     @Test
     public void commitNowNoEffect() throws Throwable {
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
-        fm.beginTransaction().remove(mBeginningFragment).commit();
+        fm.beginTransaction()
+                .remove(mBeginningFragment)
+                .setAllowOptimization(true)
+                .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
 
@@ -460,6 +482,7 @@
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
                 .add(R.id.fragmentContainer2, fragment2)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -480,6 +503,7 @@
                 .replace(R.id.fragmentContainer1, fragment3)
                 .add(strictFragment1, "1")
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
 
         FragmentTestUtil.waitForExecution(mActivityRule);
@@ -497,6 +521,7 @@
                         .replace(R.id.fragmentContainer2, fragment4)
                         .remove(strictFragment1)
                         .add(strictFragment2, "2")
+                        .setAllowOptimization(true)
                         .commitNow();
             }
         });
@@ -534,6 +559,7 @@
                 .addSharedElement(startBlue1, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment2)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -545,6 +571,7 @@
                 fm.beginTransaction()
                         .addSharedElement(startBlue2, "blueSquare")
                         .replace(R.id.fragmentContainer, fragment1)
+                        .setAllowOptimization(true)
                         .commitNow();
             }
         });
@@ -562,7 +589,10 @@
     @Test
     public void noAccidentalRemoval() throws Throwable {
         final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
-        fm.beginTransaction().remove(mBeginningFragment).commit();
+        fm.beginTransaction()
+                .remove(mBeginningFragment)
+                .setAllowOptimization(true)
+                .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         FragmentTestUtil.setContentView(mActivityRule, R.layout.double_container);
 
@@ -570,6 +600,7 @@
 
         fm.beginTransaction()
                 .add(R.id.fragmentContainer1, fragment1)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         fragment1.startPostponedEnterTransition();
@@ -580,6 +611,7 @@
         // Create a postponed transaction that removes a view
         fm.beginTransaction()
                 .replace(R.id.fragmentContainer1, fragment2)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
         assertPostponedTransition(fragment1, fragment2, null);
@@ -588,6 +620,7 @@
         // Create a transaction that doesn't interfere with the previously postponed one
         fm.beginTransaction()
                 .replace(R.id.fragmentContainer2, fragment3)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -613,6 +646,7 @@
                 .addSharedElement(startBlue, "blueSquare")
                 .replace(R.id.fragmentContainer, fragment)
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
@@ -646,6 +680,7 @@
         fm1.beginTransaction()
                 .add(R.id.fragmentContainer, fragment1, "1")
                 .addToBackStack(null)
+                .setAllowOptimization(true)
                 .commit();
         FragmentTestUtil.waitForExecution(mActivityRule);
 
diff --git a/fragment/tests/java/android/support/v4/app/TransitionFragment.java b/fragment/tests/java/android/support/v4/app/TransitionFragment.java
index fda2784..e6493c2 100644
--- a/fragment/tests/java/android/support/v4/app/TransitionFragment.java
+++ b/fragment/tests/java/android/support/v4/app/TransitionFragment.java
@@ -51,9 +51,11 @@
         setSharedElementEnterTransition(sharedElementEnter);
         setSharedElementReturnTransition(sharedElementReturn);
         enterTransition.addListener(mListener);
+        sharedElementEnter.addListener(mListener);
         reenterTransition.addListener(mListener);
         exitTransition.addListener(mListener);
         returnTransition.addListener(mListener);
+        sharedElementReturn.addListener(mListener);
     }
 
     @Override
diff --git a/graphics/drawable/animated/build.gradle b/graphics/drawable/animated/build.gradle
index bb110a1..10d112a 100644
--- a/graphics/drawable/animated/build.gradle
+++ b/graphics/drawable/animated/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'animated-vector-drawable'
 
 dependencies {
@@ -41,14 +40,6 @@
         additionalParameters "--no-version-vectors"
     }
 
-    packagingOptions {
-        exclude 'LICENSE.txt'
-    }
-
-    testOptions {
-        unitTests.returnDefaultValues = true
-    }
-
     buildTypes.all {
         consumerProguardFiles 'proguard-rules.pro'
     }
@@ -62,28 +53,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar) {
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/graphics/drawable/static/build.gradle b/graphics/drawable/static/build.gradle
index 2c72f9f..fdb306c 100644
--- a/graphics/drawable/static/build.gradle
+++ b/graphics/drawable/static/build.gradle
@@ -42,14 +42,6 @@
     aaptOptions {
         additionalParameters "--no-version-vectors"
     }
-
-    packagingOptions {
-        exclude 'LICENSE.txt'
-    }
-
-    testOptions {
-        unitTests.returnDefaultValues = true
-    }
 }
 
 android.libraryVariants.all { variant ->
@@ -60,28 +52,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar) {
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index 9bec2a3..8dd44bf 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -15,9 +15,6 @@
     androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
 }
 
-sourceCompatibility = JavaVersion.VERSION_1_7
-targetCompatibility = JavaVersion.VERSION_1_7
-
 android {
     compileSdkVersion project.ext.currentSdk
 
@@ -61,22 +58,6 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
@@ -84,7 +65,6 @@
         exclude('android/service/media/**')
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/media-compat/tests/AndroidManifest.xml b/media-compat/tests/AndroidManifest.xml
index 00de2db..1216194 100644
--- a/media-compat/tests/AndroidManifest.xml
+++ b/media-compat/tests/AndroidManifest.xml
@@ -27,6 +27,11 @@
     <application android:supportsRtl="true">
         <uses-library android:name="android.test.runner"/>
         <activity android:name="android.support.v4.media.session.TestActivity" />
+        <receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
+            <intent-filter>
+                <action android:name="android.intent.action.MEDIA_BUTTON" />
+            </intent-filter>
+        </receiver>
     </application>
 
     <instrumentation
diff --git a/percent/build.gradle b/percent/build.gradle
index c3f386e..b120075 100644
--- a/percent/build.gradle
+++ b/percent/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'percent'
 
 dependencies {
@@ -53,28 +52,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/recommendation/build.gradle b/recommendation/build.gradle
index 75d68d1..dadad58 100644
--- a/recommendation/build.gradle
+++ b/recommendation/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'recommendation'
 
 dependencies {
@@ -41,27 +40,10 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
diff --git a/transition/build.gradle b/transition/build.gradle
index 05675f9..2f47f83 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -61,28 +61,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v13/build.gradle b/v13/build.gradle
index 85aa8a8..fb1c25d 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -55,28 +55,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v14/preference/build.gradle b/v14/preference/build.gradle
index a7e63d5..5583e93 100644
--- a/v14/preference/build.gradle
+++ b/v14/preference/build.gradle
@@ -14,10 +14,7 @@
  * limitations under the License
  */
 
-
-
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'preference-v14'
 
 dependencies {
@@ -62,28 +59,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index f5befa7..9ed65a8 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -64,28 +64,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
index ed4cc1f..07701f9 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
@@ -809,8 +809,8 @@
     }
 
     /**
-     * This listener is called every time there is a selection in {@link RowsSupportFragment}.
-     * This can be used by users to take additional actions such as animations.
+     * This listener is called every time there is a selection in {@link RowsSupportFragment}. This can
+     * be used by users to take additional actions such as animations.
      * @hide
      */
     public void setOnItemViewSelectedListener(final BaseOnItemViewSelectedListener listener) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
index ca486ed..d3a45a0 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsFragment.java
@@ -133,12 +133,13 @@
 
     static final String TAG = "RowsFragment";
     static final boolean DEBUG = false;
+    static final int ALIGN_TOP_NOT_SET = Integer.MIN_VALUE;
 
     ItemBridgeAdapter.ViewHolder mSelectedViewHolder;
     private int mSubPosition;
     boolean mExpand = true;
     boolean mViewsCreated;
-    private int mAlignedTop;
+    private int mAlignedTop = ALIGN_TOP_NOT_SET;
     boolean mAfterEntranceTransition = true;
 
     BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
@@ -540,6 +541,9 @@
 
     @Override
     public void setAlignment(int windowAlignOffsetFromTop) {
+        if (windowAlignOffsetFromTop == ALIGN_TOP_NOT_SET) {
+            return;
+        }
         mAlignedTop = windowAlignOffsetFromTop;
         final VerticalGridView gridView = getVerticalGridView();
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
index d6c8d3f..5582644 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/RowsSupportFragment.java
@@ -136,12 +136,13 @@
 
     static final String TAG = "RowsSupportFragment";
     static final boolean DEBUG = false;
+    static final int ALIGN_TOP_NOT_SET = Integer.MIN_VALUE;
 
     ItemBridgeAdapter.ViewHolder mSelectedViewHolder;
     private int mSubPosition;
     boolean mExpand = true;
     boolean mViewsCreated;
-    private int mAlignedTop;
+    private int mAlignedTop = ALIGN_TOP_NOT_SET;
     boolean mAfterEntranceTransition = true;
 
     BaseOnItemViewSelectedListener mOnItemViewSelectedListener;
@@ -543,6 +544,9 @@
 
     @Override
     public void setAlignment(int windowAlignOffsetFromTop) {
+        if (windowAlignOffsetFromTop == ALIGN_TOP_NOT_SET) {
+            return;
+        }
         mAlignedTop = windowAlignOffsetFromTop;
         final VerticalGridView gridView = getVerticalGridView();
 
diff --git a/v17/leanback/tests/AndroidManifest.xml b/v17/leanback/tests/AndroidManifest.xml
index a30e5c9..6e6b157 100644
--- a/v17/leanback/tests/AndroidManifest.xml
+++ b/v17/leanback/tests/AndroidManifest.xml
@@ -38,6 +38,10 @@
                 android:theme="@style/Theme.Leanback.GuidedStep"
                 android:exported="true" />
 
+        <activity android:name="android.support.v17.leanback.app.RowsFragmentTestActivity"
+            android:theme="@style/Theme.Leanback"
+            android:exported="true" />
+
         <activity android:name="android.support.v17.leanback.app.BrowseFragmentTestActivity"
                   android:theme="@style/Theme.Leanback.Browse"
                   android:exported="true" />
@@ -46,6 +50,10 @@
                   android:theme="@style/Theme.Leanback"
                   android:exported="true" />
 
+        <activity android:name="android.support.v17.leanback.app.RowsSupportFragmentTestActivity"
+            android:theme="@style/Theme.Leanback"
+            android:exported="true" />
+
         <activity android:name="android.support.v17.leanback.app.BrowseSupportFragmentTestActivity"
                   android:theme="@style/Theme.Leanback.Browse"
                   android:exported="true" />
diff --git a/v17/leanback/tests/generatev4.py b/v17/leanback/tests/generatev4.py
index 3e25503..ecae656 100755
--- a/v17/leanback/tests/generatev4.py
+++ b/v17/leanback/tests/generatev4.py
@@ -19,11 +19,11 @@
 
 print "Generate v4 fragment related code for leanback"
 
-files = ['BrowseTest', 'GuidedStepTest']
+files = ['BrowseTest', 'GuidedStepTest', 'RowsTest']
 
 cls = ['BrowseTest', 'Background', 'Base', 'BaseRow', 'Browse', 'Details', 'Error', 'Headers',
       'PlaybackOverlay', 'Rows', 'Search', 'VerticalGrid', 'Branded',
-      'GuidedStepTest', 'GuidedStep']
+      'GuidedStepTest', 'GuidedStep', 'RowsTest']
 
 for w in files:
     print "copy {}Fragment to {}SupportFragment".format(w, w)
@@ -68,7 +68,7 @@
     file.close()
     outfile.close()
 
-testcls = ['Browse', 'GuidedStep', 'VerticalGrid']
+testcls = ['Browse', 'GuidedStep', 'VerticalGrid', 'Rows']
 
 for w in testcls:
     print "copy {}FrgamentTest to {}SupportFragmentTest".format(w, w)
@@ -95,7 +95,7 @@
     file.close()
     outfile.close()
 
-testcls = ['Browse', 'GuidedStep']
+testcls = ['Browse', 'GuidedStep', 'Rows']
 
 for w in testcls:
     print "copy {}FragmentTestActivity to {}SupportFragmentTestActivity".format(w, w)
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java
new file mode 100644
index 0000000..50d5f24
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class RowsFragmentTest {
+
+    static final long ACTIVITY_LOAD_DELAY = 2000;
+
+    @Rule
+    public ActivityTestRule<RowsFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(RowsFragmentTestActivity.class, false, false);
+    private RowsFragmentTestActivity mActivity;
+
+    @After
+    public void afterTest() throws Throwable {
+        activityTestRule.runOnUiThread(new Runnable() {
+            public void run() {
+                if (mActivity != null) {
+                    mActivity.finish();
+                    mActivity = null;
+                }
+            }
+        });
+    }
+
+    private void sendKeys(int ...keys) {
+        for (int i = 0; i < keys.length; i++) {
+            InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]);
+        }
+    }
+
+    void launchAndWaitActivity(Intent intent) {
+        mActivity = activityTestRule.launchActivity(intent);
+        SystemClock.sleep(ACTIVITY_LOAD_DELAY);
+    }
+
+    @Test
+    public void defaultAlignment() throws InterruptedException {
+        Intent intent = new Intent();
+        intent.putExtra(RowsFragmentTestActivity.EXTRA_NUM_ROWS, 10);
+        intent.putExtra(RowsFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, 1l);
+        launchAndWaitActivity(intent);
+
+        final Rect rect = new Rect();
+
+        final VerticalGridView gridView = mActivity.getRowsTestFragment().getVerticalGridView();
+        View row0 = gridView.findViewHolderForAdapterPosition(0).itemView;
+        rect.set(0, 0, row0.getWidth(), row0.getHeight());
+        gridView.offsetDescendantRectToMyCoords(row0, rect);
+        assertEquals("First row is initially aligned to top of screen", 0, rect.top);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        View row1 = gridView.findViewHolderForAdapterPosition(1).itemView;
+        PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(row1));
+
+        rect.set(0, 0, row1.getWidth(), row1.getHeight());
+        gridView.offsetDescendantRectToMyCoords(row1, rect);
+        assertTrue("Second row should not be aligned to top of screen", rect.top > 0);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTestActivity.java
new file mode 100644
index 0000000..fe2dd25
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsFragmentTestActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+
+public class RowsFragmentTestActivity extends Activity {
+
+    public static final String EXTRA_NUM_ROWS = "numRows";
+    public static final String EXTRA_REPEAT_PER_ROW = "repeatPerRow";
+    public static final String EXTRA_LOAD_DATA_DELAY = "loadDataDelay";
+    public final static String EXTRA_SET_ADAPTER_AFTER_DATA_LOAD = "set_adapter_after_data_load";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+
+        setContentView(R.layout.rows);
+        if (savedInstanceState == null) {
+            RowsTestFragment fragment = new RowsTestFragment();
+            Bundle arguments = new Bundle();
+            if (intent.getExtras() != null) {
+                arguments.putAll(intent.getExtras());
+            }
+            fragment.setArguments(arguments);
+            FragmentTransaction ft = getFragmentManager().beginTransaction();
+            ft.replace(R.id.main_frame, fragment);
+            ft.commit();
+        }
+    }
+
+    public RowsTestFragment getRowsTestFragment() {
+        return (RowsTestFragment) getFragmentManager().findFragmentById(R.id.main_frame);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java
new file mode 100644
index 0000000..c024b6c
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTest.java
@@ -0,0 +1,99 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from RowsFragmentTest.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.testutils.PollingCheck;
+import android.support.v17.leanback.widget.VerticalGridView;
+import android.view.KeyEvent;
+import android.view.View;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class RowsSupportFragmentTest {
+
+    static final long ACTIVITY_LOAD_DELAY = 2000;
+
+    @Rule
+    public ActivityTestRule<RowsSupportFragmentTestActivity> activityTestRule =
+            new ActivityTestRule<>(RowsSupportFragmentTestActivity.class, false, false);
+    private RowsSupportFragmentTestActivity mActivity;
+
+    @After
+    public void afterTest() throws Throwable {
+        activityTestRule.runOnUiThread(new Runnable() {
+            public void run() {
+                if (mActivity != null) {
+                    mActivity.finish();
+                    mActivity = null;
+                }
+            }
+        });
+    }
+
+    private void sendKeys(int ...keys) {
+        for (int i = 0; i < keys.length; i++) {
+            InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]);
+        }
+    }
+
+    void launchAndWaitActivity(Intent intent) {
+        mActivity = activityTestRule.launchActivity(intent);
+        SystemClock.sleep(ACTIVITY_LOAD_DELAY);
+    }
+
+    @Test
+    public void defaultAlignment() throws InterruptedException {
+        Intent intent = new Intent();
+        intent.putExtra(RowsSupportFragmentTestActivity.EXTRA_NUM_ROWS, 10);
+        intent.putExtra(RowsSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, 1l);
+        launchAndWaitActivity(intent);
+
+        final Rect rect = new Rect();
+
+        final VerticalGridView gridView = mActivity.getRowsTestSupportFragment().getVerticalGridView();
+        View row0 = gridView.findViewHolderForAdapterPosition(0).itemView;
+        rect.set(0, 0, row0.getWidth(), row0.getHeight());
+        gridView.offsetDescendantRectToMyCoords(row0, rect);
+        assertEquals("First row is initially aligned to top of screen", 0, rect.top);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        View row1 = gridView.findViewHolderForAdapterPosition(1).itemView;
+        PollingCheck.waitFor(new PollingCheck.ViewStableOnScreen(row1));
+
+        rect.set(0, 0, row1.getWidth(), row1.getHeight());
+        gridView.offsetDescendantRectToMyCoords(row1, rect);
+        assertTrue("Second row should not be aligned to top of screen", rect.top > 0);
+    }
+
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTestActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTestActivity.java
new file mode 100644
index 0000000..d736458
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsSupportFragmentTestActivity.java
@@ -0,0 +1,56 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from RowsFragmentTestActivity.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+
+public class RowsSupportFragmentTestActivity extends FragmentActivity {
+
+    public static final String EXTRA_NUM_ROWS = "numRows";
+    public static final String EXTRA_REPEAT_PER_ROW = "repeatPerRow";
+    public static final String EXTRA_LOAD_DATA_DELAY = "loadDataDelay";
+    public final static String EXTRA_SET_ADAPTER_AFTER_DATA_LOAD = "set_adapter_after_data_load";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+
+        setContentView(R.layout.rows);
+        if (savedInstanceState == null) {
+            RowsTestSupportFragment fragment = new RowsTestSupportFragment();
+            Bundle arguments = new Bundle();
+            if (intent.getExtras() != null) {
+                arguments.putAll(intent.getExtras());
+            }
+            fragment.setArguments(arguments);
+            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+            ft.replace(R.id.main_frame, fragment);
+            ft.commit();
+        }
+    }
+
+    public RowsTestSupportFragment getRowsTestSupportFragment() {
+        return (RowsTestSupportFragment) getSupportFragmentManager().findFragmentById(R.id.main_frame);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsTestFragment.java
new file mode 100644
index 0000000..d1f71db
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsTestFragment.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+
+import static android.support.v17.leanback.app.RowsFragmentTestActivity.EXTRA_LOAD_DATA_DELAY;
+import static android.support.v17.leanback.app.RowsFragmentTestActivity.EXTRA_NUM_ROWS;
+import static android.support.v17.leanback.app.RowsFragmentTestActivity.EXTRA_REPEAT_PER_ROW;
+import static android.support.v17.leanback.app.RowsFragmentTestActivity.EXTRA_SET_ADAPTER_AFTER_DATA_LOAD;
+
+public class RowsTestFragment extends RowsFragment {
+    private static final String TAG = "RowsTestFragment";
+
+    final static int DEFAULT_NUM_ROWS = 100;
+    final static int DEFAULT_REPEAT_PER_ROW = 20;
+    final static long DEFAULT_LOAD_DATA_DELAY = 2000;
+    final static boolean DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD = false;
+
+    private ArrayObjectAdapter mRowsAdapter;
+
+    // For good performance, it's important to use a single instance of
+    // a card presenter for all rows using that presenter.
+    final static StringPresenter sCardPresenter = new StringPresenter();
+
+    int NUM_ROWS;
+    int REPEAT_PER_ROW;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        Bundle arguments = getArguments();
+        NUM_ROWS = arguments.getInt(EXTRA_NUM_ROWS, RowsTestFragment.DEFAULT_NUM_ROWS);
+        REPEAT_PER_ROW = arguments.getInt(EXTRA_REPEAT_PER_ROW,
+                DEFAULT_REPEAT_PER_ROW);
+        long LOAD_DATA_DELAY = arguments.getLong(EXTRA_LOAD_DATA_DELAY,
+                DEFAULT_LOAD_DATA_DELAY);
+        final boolean SET_ADAPTER_AFTER_DATA_LOAD = arguments.getBoolean(
+                EXTRA_SET_ADAPTER_AFTER_DATA_LOAD,
+                DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD);
+
+        if (!SET_ADAPTER_AFTER_DATA_LOAD) {
+            setupRows();
+        }
+
+        setOnItemViewClickedListener(new ItemViewClickedListener());
+        setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
+            @Override
+            public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                    RowPresenter.ViewHolder rowViewHolder, Row row) {
+                Log.i(TAG, "onItemSelected: " + item + " row " + row.getHeaderItem().getName()
+                        + " " + rowViewHolder
+                        + " " + ((ListRowPresenter.ViewHolder) rowViewHolder).getGridView());
+            }
+        });
+        // simulates in a real world use case  data being loaded two seconds later
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (getActivity() == null || getActivity().isDestroyed()) {
+                    return;
+                }
+                if (SET_ADAPTER_AFTER_DATA_LOAD) {
+                    setupRows();
+                }
+                loadData();
+            }
+        }, LOAD_DATA_DELAY);
+    }
+
+    private void setupRows() {
+        ListRowPresenter lrp = new ListRowPresenter();
+
+        mRowsAdapter = new ArrayObjectAdapter(lrp);
+
+        setAdapter(mRowsAdapter);
+    }
+
+    private void loadData() {
+        for (int i = 0; i < NUM_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter);
+            int index = 0;
+            for (int j = 0; j < REPEAT_PER_ROW; ++j) {
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("This is a test-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("GuidedStepFragment-" + (index++));
+            }
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+        }
+    }
+
+    private final class ItemViewClickedListener implements OnItemViewClickedListener {
+        @Override
+        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                RowPresenter.ViewHolder rowViewHolder, Row row) {
+            Log.i(TAG, "onItemClicked: " + item + " row " + row);
+        }
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/RowsTestSupportFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsTestSupportFragment.java
new file mode 100644
index 0000000..e095f94
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/RowsTestSupportFragment.java
@@ -0,0 +1,132 @@
+// CHECKSTYLE:OFF Generated code
+/* This file is auto-generated from RowsTestFragment.java.  DO NOT MODIFY. */
+
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.app;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.HeaderItem;
+import android.support.v17.leanback.widget.ListRow;
+import android.support.v17.leanback.widget.ListRowPresenter;
+import android.support.v17.leanback.widget.OnItemViewClickedListener;
+import android.support.v17.leanback.widget.OnItemViewSelectedListener;
+import android.support.v17.leanback.widget.Presenter;
+import android.support.v17.leanback.widget.Row;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.util.Log;
+
+import static android.support.v17.leanback.app.RowsSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY;
+import static android.support.v17.leanback.app.RowsSupportFragmentTestActivity.EXTRA_NUM_ROWS;
+import static android.support.v17.leanback.app.RowsSupportFragmentTestActivity.EXTRA_REPEAT_PER_ROW;
+import static android.support.v17.leanback.app.RowsSupportFragmentTestActivity.EXTRA_SET_ADAPTER_AFTER_DATA_LOAD;
+
+public class RowsTestSupportFragment extends RowsSupportFragment {
+    private static final String TAG = "RowsTestSupportFragment";
+
+    final static int DEFAULT_NUM_ROWS = 100;
+    final static int DEFAULT_REPEAT_PER_ROW = 20;
+    final static long DEFAULT_LOAD_DATA_DELAY = 2000;
+    final static boolean DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD = false;
+
+    private ArrayObjectAdapter mRowsAdapter;
+
+    // For good performance, it's important to use a single instance of
+    // a card presenter for all rows using that presenter.
+    final static StringPresenter sCardPresenter = new StringPresenter();
+
+    int NUM_ROWS;
+    int REPEAT_PER_ROW;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        Bundle arguments = getArguments();
+        NUM_ROWS = arguments.getInt(EXTRA_NUM_ROWS, RowsTestSupportFragment.DEFAULT_NUM_ROWS);
+        REPEAT_PER_ROW = arguments.getInt(EXTRA_REPEAT_PER_ROW,
+                DEFAULT_REPEAT_PER_ROW);
+        long LOAD_DATA_DELAY = arguments.getLong(EXTRA_LOAD_DATA_DELAY,
+                DEFAULT_LOAD_DATA_DELAY);
+        final boolean SET_ADAPTER_AFTER_DATA_LOAD = arguments.getBoolean(
+                EXTRA_SET_ADAPTER_AFTER_DATA_LOAD,
+                DEFAULT_SET_ADAPTER_AFTER_DATA_LOAD);
+
+        if (!SET_ADAPTER_AFTER_DATA_LOAD) {
+            setupRows();
+        }
+
+        setOnItemViewClickedListener(new ItemViewClickedListener());
+        setOnItemViewSelectedListener(new OnItemViewSelectedListener() {
+            @Override
+            public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
+                    RowPresenter.ViewHolder rowViewHolder, Row row) {
+                Log.i(TAG, "onItemSelected: " + item + " row " + row.getHeaderItem().getName()
+                        + " " + rowViewHolder
+                        + " " + ((ListRowPresenter.ViewHolder) rowViewHolder).getGridView());
+            }
+        });
+        // simulates in a real world use case  data being loaded two seconds later
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (getActivity() == null || getActivity().isDestroyed()) {
+                    return;
+                }
+                if (SET_ADAPTER_AFTER_DATA_LOAD) {
+                    setupRows();
+                }
+                loadData();
+            }
+        }, LOAD_DATA_DELAY);
+    }
+
+    private void setupRows() {
+        ListRowPresenter lrp = new ListRowPresenter();
+
+        mRowsAdapter = new ArrayObjectAdapter(lrp);
+
+        setAdapter(mRowsAdapter);
+    }
+
+    private void loadData() {
+        for (int i = 0; i < NUM_ROWS; ++i) {
+            ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(sCardPresenter);
+            int index = 0;
+            for (int j = 0; j < REPEAT_PER_ROW; ++j) {
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("This is a test-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("Hello world-" + (index++));
+                listRowAdapter.add("Android TV-" + (index++));
+                listRowAdapter.add("Leanback-" + (index++));
+                listRowAdapter.add("GuidedStepSupportFragment-" + (index++));
+            }
+            HeaderItem header = new HeaderItem(i, "Row " + i);
+            mRowsAdapter.add(new ListRow(header, listRowAdapter));
+        }
+    }
+
+    private final class ItemViewClickedListener implements OnItemViewClickedListener {
+        @Override
+        public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
+                RowPresenter.ViewHolder rowViewHolder, Row row) {
+            Log.i(TAG, "onItemClicked: " + item + " row " + row);
+        }
+    }
+}
diff --git a/v17/leanback/tests/res/layout/rows.xml b/v17/leanback/tests/res/layout/rows.xml
new file mode 100644
index 0000000..7a7c0b0
--- /dev/null
+++ b/v17/leanback/tests/res/layout/rows.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/main_frame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</FrameLayout>
diff --git a/v17/preference-leanback/build.gradle b/v17/preference-leanback/build.gradle
index 9bfd0f3..e58fa8b 100644
--- a/v17/preference-leanback/build.gradle
+++ b/v17/preference-leanback/build.gradle
@@ -36,28 +36,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v4/build.gradle b/v4/build.gradle
index f0226e3..f226a87 100644
--- a/v4/build.gradle
+++ b/v4/build.gradle
@@ -1,6 +1,6 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'support-v4'
+
 dependencies {
     compile project(':support-compat')
     compile project(':support-media-compat')
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index aa62632..4935085 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'appcompat-v7'
 
 dependencies {
@@ -61,28 +60,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
index 3390da2..3fe899d 100755
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutTest.java
@@ -270,10 +270,17 @@
             // specified, so it should have its height reduced by the height of the system status
             // bar.
 
+            final int[] contentViewLocationOnScreen = new int[2];
+            mContentView.getLocationOnScreen(contentViewLocationOnScreen);
+            final int statusBarHeight = contentViewLocationOnScreen[1];
             // Get the system window top inset that was propagated to the top-level DrawerLayout
             // during its layout.
             int drawerTopInset = mDrawerLayout.getSystemWindowInsetTop();
-            assertTrue("Drawer top inset is positive on L+", drawerTopInset > 0);
+            if (statusBarHeight > 0) {
+                assertEquals("Drawer top inset is positive on L+", statusBarHeight, drawerTopInset);
+            } else {
+                assertEquals("Drawer top inset 0 due to no status bar", 0, drawerTopInset);
+            }
             assertEquals("Drawer layout and drawer heights on L+",
                     drawerLayoutHeight - drawerTopInset, contentHeight);
         }
diff --git a/v7/cardview/build.gradle b/v7/cardview/build.gradle
index b88aad7..ce3f28d 100644
--- a/v7/cardview/build.gradle
+++ b/v7/cardview/build.gradle
@@ -38,28 +38,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/gridlayout/build.gradle b/v7/gridlayout/build.gradle
index 542d5a7..56f320f 100644
--- a/v7/gridlayout/build.gradle
+++ b/v7/gridlayout/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'gridlayout-v7'
 
 dependencies {
@@ -53,28 +52,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/mediarouter/build.gradle b/v7/mediarouter/build.gradle
index 3e51602..7796565 100644
--- a/v7/mediarouter/build.gradle
+++ b/v7/mediarouter/build.gradle
@@ -49,28 +49,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/palette/build.gradle b/v7/palette/build.gradle
index 2494370..686fe71 100644
--- a/v7/palette/build.gradle
+++ b/v7/palette/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'palette-v7'
 
 dependencies {
@@ -34,28 +33,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/preference/build.gradle b/v7/preference/build.gradle
index a63cc9e..b5fd656 100644
--- a/v7/preference/build.gradle
+++ b/v7/preference/build.gradle
@@ -15,7 +15,6 @@
  */
 
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'preference-v7'
 
 dependencies {
@@ -80,28 +79,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index 11a098e..e262ec7 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -1,5 +1,4 @@
 apply plugin: 'com.android.library'
-
 archivesBaseName = 'recyclerview-v7'
 
 dependencies {
@@ -47,10 +46,6 @@
         targetCompatibility JavaVersion.VERSION_1_7
     }
 
-    packagingOptions {
-        exclude 'LICENSE.txt'
-    }
-
     testOptions {
         unitTests.returnDefaultValues = true
     }
@@ -68,28 +63,11 @@
     }
     def suffix = name.capitalize()
 
-    def jarTask = project.tasks.create(name: "jar${suffix}", type: Jar){
-        dependsOn variant.javaCompile
-        from variant.javaCompile.destinationDir
-        from 'LICENSE.txt'
-    }
-    def javadocTask = project.tasks.create(name: "javadoc${suffix}", type: Javadoc) {
-        source android.sourceSets.main.java
-        classpath = files(variant.javaCompile.classpath.files) + files(
-                "${android.sdkDirectory}/platforms/${android.compileSdkVersion}/android.jar")
-    }
-
-    def javadocJarTask = project.tasks.create(name: "javadocJar${suffix}", type: Jar) {
-        classifier = 'javadoc'
-        from 'build/docs/javadoc'
-    }
-
     def sourcesJarTask = project.tasks.create(name: "sourceJar${suffix}", type: Jar) {
         classifier = 'sources'
         from android.sourceSets.main.java.srcDirs
     }
 
-    artifacts.add('archives', javadocJarTask);
     artifacts.add('archives', sourcesJarTask);
 }
 
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 066054c..7027acb 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -189,6 +189,16 @@
      */
     private static final boolean FORCE_ABS_FOCUS_SEARCH_DIRECTION = Build.VERSION.SDK_INT <= 15;
 
+    /**
+     * on API 15-, a focused child can still be considered a focused child of RV even after
+     * it's being removed or its focusable flag is set to false. This is because when this focused
+     * child is detached, the reference to this child is not removed in clearFocus. API 16 and above
+     * properly handle this case by calling ensureInputFocusOnFirstFocusable or rootViewRequestFocus
+     * to request focus on a new child, which will clear the focus on the old (detached) child as a
+     * side-effect.
+     */
+    private static final boolean IGNORE_DETACHED_FOCUSED_CHILD = Build.VERSION.SDK_INT <= 15;
+
     static final boolean DISPATCH_TEMP_DETACH = false;
     public static final int HORIZONTAL = 0;
     public static final int VERTICAL = 1;
@@ -3341,9 +3351,28 @@
         // only recover focus if RV itself has the focus or the focused view is hidden
         if (!isFocused()) {
             final View focusedChild = getFocusedChild();
-            if (!mChildHelper.isHidden(focusedChild)
-                    // on API 15, this happens :/.
-                    && focusedChild.getParent() == this && focusedChild.hasFocus()) {
+            if (IGNORE_DETACHED_FOCUSED_CHILD
+                    && (focusedChild.getParent() == null || !focusedChild.hasFocus())) {
+                // Special handling of API 15-. A focused child can be invalid because mFocus is not
+                // cleared when the child is detached (mParent = null),
+                // This happens because clearFocus on API 15- does not invalidate mFocus of its
+                // parent when this child is detached.
+                // For API 16+, this is not an issue because requestFocus takes care of clearing the
+                // prior detached focused child. For API 15- the problem happens in 2 cases because
+                // clearChild does not call clearChildFocus on RV: 1. setFocusable(false) is called
+                // for the current focused item which calls clearChild or 2. when the prior focused
+                // child is removed, removeDetachedView called in layout step 3 which calls
+                // clearChild. We should ignore this invalid focused child in all our calculations
+                // for the next view to receive focus, and apply the focus recovery logic instead.
+                if (mChildHelper.getChildCount() == 0) {
+                    // No children left. Request focus on the RV itself since one of its children
+                    // was holding focus previously.
+                    requestFocus();
+                    return;
+                }
+            } else if (!mChildHelper.isHidden(focusedChild)) {
+                // If the currently focused child is hidden, apply the focus recovery logic.
+                // Otherwise return, i.e. the currently (unhidden) focused child is good enough :/.
                 return;
             }
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java
index 0c324a9..0354d53 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewFocusRecoveryTest.java
@@ -421,6 +421,11 @@
      */
     private void assertFocusAfterLayout(int focusedChildIndexWhenRecoveryEnabled,
                                         int focusedChildIndexWhenRecoveryDisabled) {
+        if (mDisableAnimation && mDisableRecovery) {
+            // This case is not quite handled properly at the moment. For now, RV may become focused
+            // without re-delivering the focus down to the children. Skip the checks for now.
+            return;
+        }
         if (mRecyclerView.getChildCount() == 0) {
             assertThat("RV should have focus when it has no children",
                     mRecyclerView.hasFocus(), is(true));
@@ -428,11 +433,6 @@
                     mRecyclerView.isFocused(), is(true));
             return;
         }
-        if (mDisableAnimation && mDisableRecovery) {
-            // This case is not quite handled properly at the moment. For now, RV may become focused
-            // without re-delivering the focus down to the children. Skip the checks for now.
-            return;
-        }
 
         assertThat("RV should still have focus after layout", mRecyclerView.hasFocus(), is(true));
         if ((mDisableRecovery && focusedChildIndexWhenRecoveryDisabled == -1)