Merge "Import translations. DO NOT MERGE" into androidx-master-dev
diff --git a/annotations/src/main/resources/META-INF/proguard/androidx-annotations.pro b/annotations/src/main/resources/META-INF/proguard/androidx-annotations.pro
index c93c552..cd1cd53 100644
--- a/annotations/src/main/resources/META-INF/proguard/androidx-annotations.pro
+++ b/annotations/src/main/resources/META-INF/proguard/androidx-annotations.pro
@@ -1,3 +1,4 @@
+-keep,allowobfuscation @interface androidx.annotation.Keep
 -keep @androidx.annotation.Keep class * {*;}
 
 -keepclasseswithmembers class * {
diff --git a/appcompat/api/1.1.0-alpha03.ignore b/appcompat/api/1.1.0-alpha03.ignore
new file mode 100644
index 0000000..f8fc8c9
--- /dev/null
+++ b/appcompat/api/1.1.0-alpha03.ignore
@@ -0,0 +1,5 @@
+// Baseline format: 1.0
+RemovedPackage: androidx.appcompat.content.res:
+    Removed package androidx.appcompat.content.res
+
+
diff --git a/appcompat/api/1.1.0-alpha03.txt b/appcompat/api/1.1.0-alpha03.txt
index 973d64c..0b1a7fa 100644
--- a/appcompat/api/1.1.0-alpha03.txt
+++ b/appcompat/api/1.1.0-alpha03.txt
@@ -337,15 +337,6 @@
 
 }
 
-package androidx.appcompat.content.res {
-
-  public final class AppCompatResources {
-    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
-    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
-  }
-
-}
-
 package androidx.appcompat.graphics.drawable {
 
   public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
diff --git a/appcompat/api/current.txt b/appcompat/api/current.txt
index 973d64c..0b1a7fa 100644
--- a/appcompat/api/current.txt
+++ b/appcompat/api/current.txt
@@ -337,15 +337,6 @@
 
 }
 
-package androidx.appcompat.content.res {
-
-  public final class AppCompatResources {
-    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
-    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
-  }
-
-}
-
 package androidx.appcompat.graphics.drawable {
 
   public class DrawerArrowDrawable extends android.graphics.drawable.Drawable {
diff --git a/appcompat/res/values-as/strings.xml b/appcompat/res/values-as/strings.xml
new file mode 100644
index 0000000..700c8fb
--- /dev/null
+++ b/appcompat/res/values-as/strings.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="abc_action_mode_done" msgid="2571498368472823490">"সম্পন্ন হ’ল"</string>
+    <string name="abc_action_bar_home_description" msgid="7903180715631665059">"গৃহ পৃষ্ঠালৈ যাওক"</string>
+    <string name="abc_action_bar_up_description" msgid="6794660482873516081">"ওপৰলৈ যাওক"</string>
+    <string name="abc_action_menu_overflow_description" msgid="1155814932213556626">"অধিক বিকল্প"</string>
+    <string name="abc_toolbar_collapse_description" msgid="6389460216547290468">"সংকোচন কৰক"</string>
+    <string name="abc_searchview_description_search" msgid="5466662225065974044">"সন্ধান"</string>
+    <string name="abc_search_hint" msgid="940844115270746197">"সন্ধান কৰক…"</string>
+    <string name="abc_searchview_description_query" msgid="908784302972860853">"সন্ধান কৰা প্ৰশ্ন"</string>
+    <string name="abc_searchview_description_clear" msgid="1769270744562318534">"সন্ধান কৰা প্ৰশ্ন মচক"</string>
+    <string name="abc_searchview_description_submit" msgid="8203855622131699655">"প্ৰশ্ন দাখিল কৰক"</string>
+    <string name="abc_searchview_description_voice" msgid="3478748990613108725">"কণ্ঠধ্বনিৰ দ্বাৰা সন্ধান"</string>
+    <string name="abc_activitychooserview_choose_application" msgid="1798588241954930982">"কোনো এপ্ বাছনি কৰক"</string>
+    <string name="abc_activity_chooser_view_see_all" msgid="3732416590524162402">"সকলো চাওক"</string>
+    <string name="abc_shareactionprovider_share_with_application" msgid="9009661856846212431">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>ৰ জৰিয়তে শ্বেয়াৰ কৰক"</string>
+    <string name="abc_shareactionprovider_share_with" msgid="2650565705514630347">"ইয়াৰ জৰিয়তে শ্বেয়াৰ কৰক"</string>
+    <string name="abc_capital_on" msgid="7831734969929204599">"অন"</string>
+    <string name="abc_capital_off" msgid="3403923230105792483">"অফ"</string>
+    <string name="search_menu_title" msgid="730395136688082741">"সন্ধান"</string>
+    <string name="abc_prepend_shortcut_label" msgid="3570106412128999382">"Menu+"</string>
+    <string name="abc_menu_meta_shortcut_label" msgid="8046416353848716905">"Meta+"</string>
+    <string name="abc_menu_ctrl_shortcut_label" msgid="944415252197684443">"Ctrl+"</string>
+    <string name="abc_menu_alt_shortcut_label" msgid="5725160506500770567">"Alt+"</string>
+    <string name="abc_menu_shift_shortcut_label" msgid="3271697756921353410">"Shift+"</string>
+    <string name="abc_menu_sym_shortcut_label" msgid="8327365089695024394">"Sym+"</string>
+    <string name="abc_menu_function_shortcut_label" msgid="4974283687810130415">"Function+"</string>
+    <string name="abc_menu_space_shortcut_label" msgid="2304645930658438191">"space"</string>
+    <string name="abc_menu_enter_shortcut_label" msgid="6840127756824236027">"enter"</string>
+    <string name="abc_menu_delete_shortcut_label" msgid="129742188101734366">"delete"</string>
+</resources>
diff --git a/appcompat/res/values-ca/strings.xml b/appcompat/res/values-ca/strings.xml
index 705de23..b558afa2 100644
--- a/appcompat/res/values-ca/strings.xml
+++ b/appcompat/res/values-ca/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="abc_action_mode_done" msgid="2571498368472823490">"Fet"</string>
     <string name="abc_action_bar_home_description" msgid="7903180715631665059">"Navega a la pàgina d\'inici"</string>
-    <string name="abc_action_bar_up_description" msgid="6794660482873516081">"Navega cap a dalt"</string>
+    <string name="abc_action_bar_up_description" msgid="6794660482873516081">"Navega cap amunt"</string>
     <string name="abc_action_menu_overflow_description" msgid="1155814932213556626">"Més opcions"</string>
     <string name="abc_toolbar_collapse_description" msgid="6389460216547290468">"Replega"</string>
     <string name="abc_searchview_description_search" msgid="5466662225065974044">"Cerca"</string>
diff --git a/appcompat/resources/api/1.1.0-alpha03.txt b/appcompat/resources/api/1.1.0-alpha03.txt
index 5499c81..990b059 100644
--- a/appcompat/resources/api/1.1.0-alpha03.txt
+++ b/appcompat/resources/api/1.1.0-alpha03.txt
@@ -1,4 +1,13 @@
 // Signature format: 3.0
+package androidx.appcompat.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+  }
+
+}
+
 package androidx.appcompat.graphics.drawable {
 
   public class AnimatedStateListDrawableCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback androidx.core.graphics.drawable.TintAwareDrawable {
diff --git a/appcompat/resources/api/current.txt b/appcompat/resources/api/current.txt
index 5499c81..990b059 100644
--- a/appcompat/resources/api/current.txt
+++ b/appcompat/resources/api/current.txt
@@ -1,4 +1,13 @@
 // Signature format: 3.0
+package androidx.appcompat.content.res {
+
+  public final class AppCompatResources {
+    method public static android.content.res.ColorStateList! getColorStateList(android.content.Context, @ColorRes int);
+    method public static android.graphics.drawable.Drawable? getDrawable(android.content.Context, @DrawableRes int);
+  }
+
+}
+
 package androidx.appcompat.graphics.drawable {
 
   public class AnimatedStateListDrawableCompat extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback androidx.core.graphics.drawable.TintAwareDrawable {
diff --git a/appcompat/resources/src/androidTest/java/androidx/appcompat/content/res/AppCompatResourcesTestCase.java b/appcompat/resources/src/androidTest/java/androidx/appcompat/content/res/AppCompatResourcesTestCase.java
new file mode 100644
index 0000000..917ea00
--- /dev/null
+++ b/appcompat/resources/src/androidTest/java/androidx/appcompat/content/res/AppCompatResourcesTestCase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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 androidx.appcompat.content.res;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+
+import androidx.appcompat.resources.test.R;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatResourcesTestCase {
+    private Context mContext;
+
+    public AppCompatResourcesTestCase() {
+        mContext = ApplicationProvider.getApplicationContext();
+    }
+
+    @Test
+    public void testColorStateListCaching() {
+        final ColorStateList result1 = AppCompatResources.getColorStateList(
+                mContext, R.color.color_state_list_themed_attrs);
+        final ColorStateList result2 = AppCompatResources.getColorStateList(
+                mContext, R.color.color_state_list_themed_attrs);
+        assertNotNull(result1);
+        assertNotNull(result2);
+        assertEquals(result1, result2);
+    }
+
+    @Test
+    public void testGetDrawableVectorResource() {
+        assertNotNull(AppCompatResources.getDrawable(mContext, R.drawable.test_vector_off));
+    }
+
+    @Test
+    public void testGetAnimatedStateListDrawable() {
+        assertNotNull(AppCompatResources.getDrawable(mContext, R.drawable.asl_heart));
+    }
+}
diff --git a/appcompat/resources/src/androidTest/res/color/color_state_list_themed_attrs.xml b/appcompat/resources/src/androidTest/res/color/color_state_list_themed_attrs.xml
new file mode 100644
index 0000000..9ee972d
--- /dev/null
+++ b/appcompat/resources/src/androidTest/res/color/color_state_list_themed_attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item android:color="?android:attr/colorForeground" android:state_enabled="false" app:alpha="0.5"/>
+    <item android:color="?android:attr/colorForeground"/>
+</selector>
+
diff --git a/appcompat/resources/src/androidTest/res/drawable/test_vector_off.xml b/appcompat/resources/src/androidTest/res/drawable/test_vector_off.xml
new file mode 100644
index 0000000..53427a2
--- /dev/null
+++ b/appcompat/resources/src/androidTest/res/drawable/test_vector_off.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<vector
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:name="btn_radio_to_on_mtrl"
+        android:width="32dp"
+        android:viewportWidth="32"
+        android:height="32dp"
+        android:viewportHeight="32">
+    <group
+            android:name="btn_radio_to_on_mtrl_0"
+            android:translateX="16"
+            android:translateY="16" >
+        <group
+                android:name="ring_outer" >
+            <path
+                    android:name="ring_outer_path"
+                    android:strokeColor="#FF000000"
+                    android:strokeWidth="2"
+                    android:pathData="M 0.0,-9.0 c 4.9705627482,0.0 9.0,4.0294372518 9.0,9.0 c 0.0,4.9705627482 -4.0294372518,9.0 -9.0,9.0 c -4.9705627482,0.0 -9.0,-4.0294372518 -9.0,-9.0 c 0.0,-4.9705627482 4.0294372518,-9.0 9.0,-9.0 Z" />
+        </group>
+        <group
+                android:name="dot_group"
+                android:scaleX="0"
+                android:scaleY="0" >
+            <path
+                    android:name="dot_path"
+                    android:pathData="M 0.0,-5.0 c -2.7619934082,0.0 -5.0,2.2380065918 -5.0,5.0 c 0.0,2.7619934082 2.2380065918,5.0 5.0,5.0 c 2.7619934082,0.0 5.0,-2.2380065918 5.0,-5.0 c 0.0,-2.7619934082 -2.2380065918,-5.0 -5.0,-5.0 Z"
+                    android:fillColor="#FF000000" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/appcompat/src/main/java/androidx/appcompat/content/res/AppCompatResources.java b/appcompat/resources/src/main/java/androidx/appcompat/content/res/AppCompatResources.java
similarity index 97%
rename from appcompat/src/main/java/androidx/appcompat/content/res/AppCompatResources.java
rename to appcompat/resources/src/main/java/androidx/appcompat/content/res/AppCompatResources.java
index 4652f04..2bd036f 100644
--- a/appcompat/src/main/java/androidx/appcompat/content/res/AppCompatResources.java
+++ b/appcompat/resources/src/main/java/androidx/appcompat/content/res/AppCompatResources.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright 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.
@@ -30,7 +30,7 @@
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.appcompat.widget.AppCompatDrawableManager;
+import androidx.appcompat.widget.ResourceManagerInternal;
 import androidx.core.content.ContextCompat;
 import androidx.core.content.res.ColorStateListInflaterCompat;
 
@@ -99,7 +99,7 @@
      */
     @Nullable
     public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
-        return AppCompatDrawableManager.get().getDrawable(context, resId);
+        return ResourceManagerInternal.get().getDrawable(context, resId);
     }
 
     /**
diff --git a/appcompat/src/androidTest/java/androidx/appcompat/res/content/AppCompatResourcesTestCase.java b/appcompat/src/androidTest/java/androidx/appcompat/res/content/AppCompatResourcesTestCase.java
deleted file mode 100644
index 43f5ce24..0000000
--- a/appcompat/src/androidTest/java/androidx/appcompat/res/content/AppCompatResourcesTestCase.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2018 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 androidx.appcompat.res.content;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import android.app.Activity;
-import android.content.res.ColorStateList;
-
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.appcompat.test.R;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class AppCompatResourcesTestCase {
-    @Rule
-    public final ActivityTestRule<AppCompatActivity> mActivityTestRule;
-
-    public AppCompatResourcesTestCase() {
-        mActivityTestRule = new ActivityTestRule<>(AppCompatActivity.class);
-    }
-
-    @Test
-    public void testColorStateListCaching() {
-        final Activity context = mActivityTestRule.getActivity();
-        final ColorStateList result1 = AppCompatResources.getColorStateList(
-                context, R.color.color_state_list_themed_attrs);
-        final ColorStateList result2 = AppCompatResources.getColorStateList(
-                context, R.color.color_state_list_themed_attrs);
-        assertNotNull(result1);
-        assertNotNull(result2);
-        assertEquals(result1, result2);
-    }
-
-    @Test
-    public void testGetDrawableVectorResource() {
-        final Activity context = mActivityTestRule.getActivity();
-        assertNotNull(AppCompatResources.getDrawable(context, R.drawable.test_vector_off));
-    }
-
-    @Test
-    public void testGetAnimatedStateListDrawable() {
-        final Activity context = mActivityTestRule.getActivity();
-        assertNotNull(AppCompatResources.getDrawable(context, R.drawable.asl_heart));
-    }
-
-}
diff --git a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
index beb5b3f..bc93b5e 100644
--- a/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
+++ b/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java
@@ -303,6 +303,12 @@
                 sLocalNightModes.remove(mHost.getClass());
             }
         }
+
+        // Preload appcompat-specific handling of drawables that should be handled in a special
+        // way (for tinting etc). After the following line completes, calls from AppCompatResources
+        // to ResourceManagerInternal (in appcompat-resources) will handle those internal drawable
+        // paths correctly without having to go through AppCompatDrawableManager APIs.
+        AppCompatDrawableManager.preload();
     }
 
     @Override
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatDrawableManager.java b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatDrawableManager.java
index 6d582e8..9777298 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/AppCompatDrawableManager.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/AppCompatDrawableManager.java
@@ -50,10 +50,7 @@
 
     private static AppCompatDrawableManager INSTANCE;
 
-    /**
-     * Returns the singleton instance of this class.
-     */
-    public static synchronized AppCompatDrawableManager get() {
+    public static synchronized void preload() {
         if (INSTANCE == null) {
             INSTANCE = new AppCompatDrawableManager();
             INSTANCE.mResourceManager = ResourceManagerInternal.get();
@@ -391,6 +388,15 @@
                 }
             });
         }
+    }
+
+    /**
+     * Returns the singleton instance of this class.
+     */
+    public static synchronized AppCompatDrawableManager get() {
+        if (INSTANCE == null) {
+            preload();
+        }
         return INSTANCE;
     }
 
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java b/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java
index 0b6cf15..bd16798 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/LinearLayoutCompat.java
@@ -135,6 +135,10 @@
     private static final int INDEX_BOTTOM = 2;
     private static final int INDEX_FILL = 3;
 
+    /** Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage. */
+    private static final String ACCESSIBILITY_CLASS_NAME =
+            "androidx.appcompat.widget.LinearLayoutCompat";
+
     private Drawable mDivider;
     private int mDividerWidth;
     private int mDividerHeight;
@@ -1754,13 +1758,13 @@
     @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
-        event.setClassName(LinearLayoutCompat.class.getName());
+        event.setClassName(ACCESSIBILITY_CLASS_NAME);
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(LinearLayoutCompat.class.getName());
+        info.setClassName(ACCESSIBILITY_CLASS_NAME);
     }
 
     /**
diff --git a/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java b/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java
index 6f22484..07da6b9 100644
--- a/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java
+++ b/appcompat/src/main/java/androidx/appcompat/widget/ScrollingTabContainerView.java
@@ -387,6 +387,10 @@
         private ImageView mIconView;
         private View mCustomView;
 
+        // Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage.
+        private static final String ACCESSIBILITY_CLASS_NAME =
+                "androidx.appcompat.app.ActionBar$Tab";
+
         public TabView(Context context, ActionBar.Tab tab, boolean forList) {
             super(context, null, R.attr.actionBarTabStyle);
             mTab = tab;
@@ -423,7 +427,7 @@
         public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
             super.onInitializeAccessibilityEvent(event);
             // This view masquerades as an action bar tab.
-            event.setClassName(ActionBar.Tab.class.getName());
+            event.setClassName(ACCESSIBILITY_CLASS_NAME);
         }
 
         @Override
@@ -431,7 +435,7 @@
             super.onInitializeAccessibilityNodeInfo(info);
 
             // This view masquerades as an action bar tab.
-            info.setClassName(ActionBar.Tab.class.getName());
+            info.setClassName(ACCESSIBILITY_CLASS_NAME);
         }
 
         @Override
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index 2914133..183190a 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -24,6 +24,7 @@
     const val ANIMATION = "androidx.animation"
     const val ANNOTATION = "androidx.annotation"
     const val APPCOMPAT = "androidx.appcompat"
+    const val ARCH_CORE = "androidx.arch.core"
     const val ASYNCLAYOUTINFLATER = "androidx.asynclayoutinflater"
     const val BIOMETRIC = "androidx.biometric"
     const val BROWSER = "androidx.browser"
@@ -47,15 +48,20 @@
     const val GRIDLAYOUT = "androidx.gridlayout"
     const val HEIFWRITER = "androidx.heifwriter"
     const val INTERPOLATOR = "androidx.interpolator"
+    const val JETIFIER = "com.android.tools.build.jetifier"
     const val LEANBACK = "androidx.leanback"
     const val LEGACY = "androidx.legacy"
+    const val LIFECYCLE = "androidx.lifecycle"
     const val LOADER = "androidx.loader"
     const val LOCALBROADCASTMANAGER = "androidx.localbroadcastmanager"
     const val MEDIA = "androidx.media"
     const val MEDIA2 = "androidx.media2"
     const val MEDIAROUTER = "androidx.mediarouter"
+    const val NAVIGATION = "android.arch.navigation"
+    const val PAGING = "androidx.paging"
     const val PALETTE = "androidx.palette"
     const val PERCENTLAYOUT = "androidx.percentlayout"
+    const val PERSISTENCE = "androidx.sqlite"
     const val PREFERENCE = "androidx.preference"
     const val PRINT = "androidx.print"
     const val RECOMMENDATION = "androidx.recommendation"
@@ -65,23 +71,17 @@
     const val SHARETARGET = "androidx.sharetarget"
     const val SLICE = "androidx.slice"
     const val REMOTECALLBACK = "androidx.remotecallback"
+    const val ROOM = "androidx.room"
     const val SLIDINGPANELAYOUT = "androidx.slidingpanelayout"
     const val SWIPEREFRESHLAYOUT = "androidx.swiperefreshlayout"
     const val TEXTCLASSIFIER = "androidx.textclassifier"
     const val TRANSITION = "androidx.transition"
     const val TVPROVIDER = "androidx.tvprovider"
     const val VECTORDRAWABLE = "androidx.vectordrawable"
+    const val VERSIONEDPARCELABLE = "androidx.versionedparcelable"
     const val VIEWPAGER = "androidx.viewpager"
     const val VIEWPAGER2 = "androidx.viewpager2"
     const val WEAR = "androidx.wear"
     const val WEBKIT = "androidx.webkit"
-    const val ROOM = "androidx.room"
-    const val PERSISTENCE = "androidx.sqlite"
-    const val LIFECYCLE = "androidx.lifecycle"
-    const val ARCH_CORE = "androidx.arch.core"
-    const val PAGING = "androidx.paging"
-    const val NAVIGATION = "android.arch.navigation"
-    const val JETIFIER = "com.android.tools.build.jetifier"
     const val WORKMANAGER = "android.arch.work"
-    const val VERSIONEDPARCELABLE = "androidx.versionedparcelable"
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index 366805e..ddab3c2 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -79,7 +79,7 @@
     val RECYCLERVIEW = Version("1.1.0-alpha03")
     val REMOTECALLBACK = Version("1.0.0-alpha02")
     val ROOM = Version("2.1.0-alpha05")
-    val SAVEDSTATE = Version("1.0.0-alpha01")
+    val SAVEDSTATE = Version("1.0.0-alpha02")
     val SECURITY = Version("1.0.0-alpha01")
     val SHARETARGET = Version("1.0.0-alpha01")
     val SLICE = Version("1.1.0-alpha01")
@@ -97,5 +97,5 @@
     val VIEWPAGER2 = Version("1.0.0-alpha02")
     val WEAR = Version("1.1.0-alpha01")
     val WEBKIT = Version("1.1.0-alpha01")
-    val WORKMANAGER = Version("1.0.0-rc02")
+    val WORKMANAGER = Version("1.0.0-rc03")
 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
index a290522..e3412a5 100644
--- a/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/PublishDocsRules.kt
@@ -29,6 +29,7 @@
     prebuilts(LibraryGroups.ANNOTATION, "1.1.0-alpha01")
     ignore(LibraryGroups.APPCOMPAT, "appcompat-resources")
     prebuilts(LibraryGroups.APPCOMPAT, "1.1.0-alpha02")
+    prebuilts(LibraryGroups.ARCH_CORE, "2.0.0")
     prebuilts(LibraryGroups.ASYNCLAYOUTINFLATER, "1.0.0")
     prebuilts(LibraryGroups.BIOMETRIC, "biometric", "1.0.0-alpha03")
     prebuilts(LibraryGroups.BROWSER, "1.0.0")
@@ -57,6 +58,14 @@
     prebuilts(LibraryGroups.INTERPOLATOR, "1.0.0")
     prebuilts(LibraryGroups.LEANBACK, "1.1.0-alpha01")
     prebuilts(LibraryGroups.LEGACY, "1.0.0")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-savedstate-core")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-savedstate-fragment")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-viewmodel-savedstate")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-viewmodel-fragment")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-livedata-ktx")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-livedata-core-ktx")
+    ignore(LibraryGroups.LIFECYCLE, "lifecycle-compiler")
+    prebuilts(LibraryGroups.LIFECYCLE, "2.1.0-alpha02")
     prebuilts(LibraryGroups.LOADER, "1.1.0-alpha01")
     prebuilts(LibraryGroups.LOCALBROADCASTMANAGER, "1.1.0-alpha01")
     prebuilts(LibraryGroups.MEDIA, "media", "1.1.0-alpha01")
@@ -66,8 +75,12 @@
     ignore(LibraryGroups.MEDIA2, "media2-exoplayer")
     prebuilts(LibraryGroups.MEDIA2, "1.0.0-alpha03")
     prebuilts(LibraryGroups.MEDIAROUTER, "1.1.0-alpha01")
+    ignore(LibraryGroups.NAVIGATION, "navigation-testing")
+    prebuilts(LibraryGroups.NAVIGATION, "1.0.0-rc01")
+    prebuilts(LibraryGroups.PAGING, "2.1.0")
     prebuilts(LibraryGroups.PALETTE, "1.0.0")
     prebuilts(LibraryGroups.PERCENTLAYOUT, "1.0.0")
+    prebuilts(LibraryGroups.PERSISTENCE, "2.0.0")
     prebuilts(LibraryGroups.PREFERENCE, "preference-ktx", "1.1.0-alpha03")
     prebuilts(LibraryGroups.PREFERENCE, "1.1.0-alpha03")
     prebuilts(LibraryGroups.PRINT, "1.0.0")
@@ -75,6 +88,7 @@
     prebuilts(LibraryGroups.RECYCLERVIEW, "recyclerview", "1.1.0-alpha02")
     prebuilts(LibraryGroups.RECYCLERVIEW, "recyclerview-selection", "1.1.0-alpha01")
     prebuilts(LibraryGroups.REMOTECALLBACK, "1.0.0-alpha01")
+    prebuilts(LibraryGroups.ROOM, "2.1.0-alpha04")
     prebuilts(LibraryGroups.SLICE, "slice-builders", "1.0.0")
     prebuilts(LibraryGroups.SLICE, "slice-builders-ktx", "1.0.0-alpha6")
     prebuilts(LibraryGroups.SLICE, "slice-core", "1.0.0")
@@ -94,21 +108,7 @@
     prebuilts(LibraryGroups.WEAR, "1.0.0")
             .addStubs("wear/wear_stubs/com.google.android.wearable-stubs.jar")
     prebuilts(LibraryGroups.WEBKIT, "1.0.0")
-    prebuilts(LibraryGroups.ROOM, "2.1.0-alpha04")
-    prebuilts(LibraryGroups.PERSISTENCE, "2.0.0")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-savedstate-core")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-savedstate-fragment")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-viewmodel-savedstate")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-viewmodel-fragment")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-livedata-ktx")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-livedata-core-ktx")
-    ignore(LibraryGroups.LIFECYCLE, "lifecycle-compiler")
-    prebuilts(LibraryGroups.LIFECYCLE, "2.1.0-alpha02")
-    prebuilts(LibraryGroups.ARCH_CORE, "2.0.0")
-    prebuilts(LibraryGroups.PAGING, "2.1.0")
-    ignore(LibraryGroups.NAVIGATION, "navigation-testing")
-    prebuilts(LibraryGroups.NAVIGATION, "1.0.0-beta02")
-    prebuilts(LibraryGroups.WORKMANAGER, "1.0.0-rc01")
+    prebuilts(LibraryGroups.WORKMANAGER, "1.0.0-rc02")
     default(Ignore)
 }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
index ad78fb1..4c103ae 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/AffectedModuleDetector.kt
@@ -20,7 +20,6 @@
 import androidx.build.getDistributionDirectory
 import androidx.build.gradle.isRoot
 import androidx.build.isRunningOnBuildServer
-import com.android.annotations.VisibleForTesting
 import org.gradle.BuildAdapter
 import org.gradle.api.GradleException
 import org.gradle.api.Project
@@ -29,6 +28,22 @@
 import org.gradle.api.logging.Logger
 
 /**
+ * The subsets we allow the projects to be partitioned into.
+ * This is to allow more granular testing. Specifically, to enable running large tests on
+ * CHANGED_PROJECTS, and
+ *
+ * The subsets are:
+ *  CHANGED_PROJECTS -- The containing projects for any files that were changed in this CL.
+ *
+ *  DEPENDENT_PROJECTS -- Any projects that have a dependency on any of the projects
+ *      in the CHANGED_PROJECTS set.
+ *
+ *  ALL_AFFECTED_PROJECTS -- The union of CHANGED_PROJECTS and DEPENDENT_PROJECTS,
+ *      which encompasses all projects that could possibly break due to the changes.
+ */
+internal enum class ProjectSubset { DEPENDENT_PROJECTS, CHANGED_PROJECTS, ALL }
+
+/**
  * A utility class that can discover which files are changed based on git history.
  *
  * To enable this, you need to pass [ENABLE_ARG] into the build as a command line parameter
@@ -51,9 +66,17 @@
         private const val ROOT_PROP_NAME = "affectedModuleDetector"
         private const val LOG_FILE_NAME = "affected_module_detector_log.txt"
         private const val ENABLE_ARG = "androidx.enableAffectedModuleDetection"
+        private const val DEPENDENT_PROJECTS_ARG = "androidx.onlyDependent"
+        private const val CHANGED_PROJECTS_ARG = "androidx.onlyDirectlyAffected"
         @JvmStatic
         fun configure(gradle: Gradle, rootProject: Project) {
             val enabled = rootProject.hasProperty(ENABLE_ARG)
+            val subset = when {
+                rootProject.hasProperty(DEPENDENT_PROJECTS_ARG) -> ProjectSubset.DEPENDENT_PROJECTS
+                rootProject.hasProperty(CHANGED_PROJECTS_ARG)
+                    -> ProjectSubset.CHANGED_PROJECTS
+                else -> ProjectSubset.ALL
+            }
             val inBuildServer = isRunningOnBuildServer()
             if (!enabled && !inBuildServer) {
                 setInstance(rootProject, AcceptAll())
@@ -74,7 +97,8 @@
                     AffectedModuleDetectorImpl(
                             rootProject = rootProject,
                             logger = logger,
-                            ignoreUnknownProjects = false
+                            ignoreUnknownProjects = false,
+                            projectSubset = subset
                     ).also {
                         if (!enabled) {
                             logger.info("swapping with accept all")
@@ -147,12 +171,12 @@
  *
  * When a file in a module is changed, all modules that depend on it are considered as changed.
  */
-@VisibleForTesting
 internal class AffectedModuleDetectorImpl constructor(
     private val rootProject: Project,
     private val logger: Logger?,
         // used for debugging purposes when we want to ignore non module files
     private val ignoreUnknownProjects: Boolean = false,
+    private val projectSubset: ProjectSubset = ProjectSubset.ALL,
     private val injectedGitClient: GitClient? = null
 ) : AffectedModuleDetector() {
     private val git by lazy {
@@ -182,7 +206,11 @@
     }
 
     /**
-     * Finds all modules that are affected by current changes.
+     * By default, finds all modules that are affected by current changes, and always built modules
+     *
+     * With param onlyDependent, finds only modules dependent on directly affected modules
+     *
+     * With param onlyDirectlyAffected, finds only directly affectedModules and always built modules
      *
      * If it cannot determine the containing module for a file (e.g. buildSrc or root), it
      * defaults to all projects unless [ignoreUnknownProjects] is set to true.
@@ -210,7 +238,7 @@
             logger?.info(
                     """
                         if i was going to check for what i've found, i would've returned
-                        ${expandToDependants(containingProjects.filterNotNull())}
+                        ${expandToDependents(containingProjects.filterNotNull())}
                     """.trimIndent()
             )
             return allProjects
@@ -220,13 +248,19 @@
                 project.name.contains(it)
             }
         }
-        // expand the list to all of their dependants
-        return expandToDependants(containingProjects + alwaysBuild)
+
+        return when (projectSubset) {
+            ProjectSubset.DEPENDENT_PROJECTS
+                -> expandToDependents(containingProjects) - containingProjects.filterNotNull()
+            ProjectSubset.CHANGED_PROJECTS
+                -> (containingProjects + alwaysBuild).filterNotNull().toSet()
+            else -> expandToDependents(containingProjects) + alwaysBuild
+        }
     }
 
-    private fun expandToDependants(containingProjects: List<Project?>): Set<Project> {
+    private fun expandToDependents(containingProjects: List<Project?>): Set<Project> {
         return containingProjects.flatMapTo(mutableSetOf()) {
-            dependencyTracker.findAllDependants(it!!)
+            dependencyTracker.findAllDependents(it!!)
         }
     }
 
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/DependencyTracker.kt b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/DependencyTracker.kt
index 6808ede..baec3ce 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/DependencyTracker.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencyTracker/DependencyTracker.kt
@@ -29,7 +29,7 @@
     private val rootProject: Project,
     private val logger: Logger?
 ) {
-    private val dependantList: Map<Project, Set<Project>> by lazy {
+    private val dependentList: Map<Project, Set<Project>> by lazy {
         val result = mutableMapOf<Project, MutableSet<Project>>()
         rootProject.subprojects.forEach { project ->
             logger?.info("checking ${project.path} for dependencies")
@@ -49,16 +49,16 @@
         result
     }
 
-    fun findAllDependants(project: Project): Set<Project> {
-        logger?.info("finding dependants of ${project.path}")
+    fun findAllDependents(project: Project): Set<Project> {
+        logger?.info("finding dependents of ${project.path}")
         val result = mutableSetOf<Project>()
-        fun addAllDependants(project: Project) {
+        fun addAllDependents(project: Project) {
             if (result.add(project)) {
-                dependantList[project]?.forEach(::addAllDependants)
+                dependentList[project]?.forEach(::addAllDependents)
             }
         }
-        addAllDependants(project)
-        logger?.info("dependants of ${project.path} is ${result.map {
+        addAllDependents(project)
+        logger?.info("dependents of ${project.path} is ${result.map {
             it.path
         }}")
         return result
diff --git a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt b/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
index b012be6..99ea5cf 100644
--- a/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
+++ b/buildSrc/src/test/kotlin/androidx/build/dependencyTracker/AffectedModuleDetectorImplTest.kt
@@ -41,11 +41,38 @@
     private lateinit var root: Project
     private lateinit var p1: Project
     private lateinit var p2: Project
+    private lateinit var p3: Project
+    private lateinit var p4: Project
+    private lateinit var p5: Project
+    private lateinit var p6: Project
+    private lateinit var p7: Project
 
     @Before
     fun init() {
         val tmpDir = tmpFolder.root
 
+        /*
+
+        Dummy project file tree:
+
+               root
+              / |  \
+            p1  p7  p2
+           /         \
+          p3          p5
+         /  \
+       p4   p6
+
+        Dependency tree:
+
+            p1    p2
+           /  \  /  \
+          p3   p5   p6
+         /
+        p4
+
+         */
+
         root = ProjectBuilder.builder()
                 .withProjectDir(tmpDir)
                 .withName("root")
@@ -60,6 +87,40 @@
                 .withName("p2")
                 .withParent(root)
                 .build()
+        p3 = ProjectBuilder.builder()
+            .withProjectDir(tmpDir.resolve("p1:p3"))
+            .withName("p3")
+            .withParent(p1)
+            .build()
+        val p3config = p3.configurations.create("p3config")
+        p3config.dependencies.add(p3.dependencies.project(mutableMapOf("path" to ":p1")))
+        p4 = ProjectBuilder.builder()
+            .withProjectDir(tmpDir.resolve("p1:p3:p4"))
+            .withName("p4")
+            .withParent(p3)
+            .build()
+        val p4config = p4.configurations.create("p4config")
+        p4config.dependencies.add(p4.dependencies.project(mutableMapOf("path" to ":p1:p3")))
+        p5 = ProjectBuilder.builder()
+            .withProjectDir(tmpDir.resolve("p2:p5"))
+            .withName("p5")
+            .withParent(p2)
+            .build()
+        val p5config = p5.configurations.create("p5config")
+        p5config.dependencies.add(p5.dependencies.project(mutableMapOf("path" to ":p2")))
+        p5config.dependencies.add(p5.dependencies.project(mutableMapOf("path" to ":p1:p3")))
+        p6 = ProjectBuilder.builder()
+            .withProjectDir(tmpDir.resolve("p1:p3:p6"))
+            .withName("p6")
+            .withParent(p3)
+            .build()
+        val p6config = p6.configurations.create("p6config")
+        p6config.dependencies.add(p6.dependencies.project(mutableMapOf("path" to ":p2")))
+        p7 = ProjectBuilder.builder()
+            .withProjectDir(tmpDir.resolve("p7"))
+            .withName("p7")
+            .withParent(root)
+            .build()
     }
 
     @Test
@@ -68,12 +129,45 @@
                 rootProject = root,
                 logger = logger,
                 ignoreUnknownProjects = false,
+                projectSubset = ProjectSubset.ALL,
                 injectedGitClient = MockGitClient(
                         lastMergeSha = "foo",
                         changedFiles = emptyList())
         )
         MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
-                setOf(p1, p2)
+                setOf(p1, p2, p3, p4, p5, p6, p7)
+        ))
+    }
+
+    @Test
+    fun noChangeCLsOnlyDependent() {
+        val detector = AffectedModuleDetectorImpl(
+                rootProject = root,
+                logger = logger,
+                ignoreUnknownProjects = false,
+                projectSubset = ProjectSubset.DEPENDENT_PROJECTS,
+                injectedGitClient = MockGitClient(
+                        lastMergeSha = "foo",
+                        changedFiles = emptyList())
+                )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+                setOf(p1, p2, p3, p4, p5, p6, p7)
+        ))
+    }
+
+    @Test
+    fun noChangeCLsOnlyDirectlyAffected() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.CHANGED_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = emptyList())
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p1, p2, p3, p4, p5, p6, p7)
         ))
     }
 
@@ -83,21 +177,55 @@
                 rootProject = root,
                 logger = logger,
                 ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.ALL,
                 injectedGitClient = MockGitClient(
                         lastMergeSha = "foo",
                         changedFiles = listOf(convertToFilePath("p1", "foo.java")))
         )
         MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
-                setOf(p1)
+                setOf(p1, p3, p4, p5)
         ))
     }
 
     @Test
-    fun changeInBoth() {
+    fun changeInOneOnlyDependent() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.DEPENDENT_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = listOf(convertToFilePath("p1", "foo.java")))
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p3, p4, p5)
+        ))
+    }
+
+    @Test
+    fun changeInOneOnlyDirectlyAffected() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.CHANGED_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = listOf(convertToFilePath("p1", "foo.java")))
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p1)
+        ))
+    }
+
+    @Test
+    fun changeInTwo() {
         val detector = AffectedModuleDetectorImpl(
                 rootProject = root,
                 logger = logger,
                 ignoreUnknownProjects = false,
+                projectSubset = ProjectSubset.ALL,
                 injectedGitClient = MockGitClient(
                         lastMergeSha = "foo",
                         changedFiles = listOf(
@@ -105,7 +233,43 @@
                                 convertToFilePath("p2", "bar.java")))
         )
         MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
-                setOf(p1, p2)
+                setOf(p1, p2, p3, p4, p5, p6)
+        ))
+    }
+
+    @Test
+    fun changeInTwoOnlyDependent() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.DEPENDENT_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = listOf(
+                    convertToFilePath("p1", "foo.java"),
+                    convertToFilePath("p2", "bar.java")))
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p3, p4, p5, p6)
+        ))
+    }
+
+    @Test
+    fun changeInTwoOnlyDirectlyAffected() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.CHANGED_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = listOf(
+                    convertToFilePath("p1", "foo.java"),
+                    convertToFilePath("p2", "bar.java")))
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p1, p2)
         ))
     }
 
@@ -115,12 +279,45 @@
                 rootProject = root,
                 logger = logger,
                 ignoreUnknownProjects = false,
+                projectSubset = ProjectSubset.ALL,
                 injectedGitClient = MockGitClient(
                         lastMergeSha = "foo",
                         changedFiles = listOf("foo.java"))
         )
         MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
-                setOf(p1, p2)
+                setOf(p1, p2, p3, p4, p5, p6, p7)
+        ))
+    }
+
+    @Test
+    fun changeInRootOnlyDependent() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.DEPENDENT_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = listOf("foo.java"))
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p1, p2, p3, p4, p5, p6, p7)
+        ))
+    }
+
+    @Test
+    fun changeInRootOnlyDirectlyAffected() {
+        val detector = AffectedModuleDetectorImpl(
+            rootProject = root,
+            logger = logger,
+            ignoreUnknownProjects = false,
+            projectSubset = ProjectSubset.CHANGED_PROJECTS,
+            injectedGitClient = MockGitClient(
+                lastMergeSha = "foo",
+                changedFiles = listOf("foo.java"))
+        )
+        MatcherAssert.assertThat(detector.affectedProjects, CoreMatchers.`is`(
+            setOf(p1, p2, p3, p4, p5, p6, p7)
         ))
     }
 
diff --git a/car/cluster/src/main/java/androidx/car/cluster/navigation/Step.java b/car/cluster/src/main/java/androidx/car/cluster/navigation/Step.java
index 95704ec..8fed212 100644
--- a/car/cluster/src/main/java/androidx/car/cluster/navigation/Step.java
+++ b/car/cluster/src/main/java/androidx/car/cluster/navigation/Step.java
@@ -81,8 +81,8 @@
         private RichText mCue;
 
         /**
-         * Sets the distance from the current position to the point where this navigation step
-         * should be executed, or null if this step doesn't involve a maneuver.
+         * Sets the maneuver to be performed on this step, or null if this step doesn't involve a
+         * maneuver.
          *
          * @return this object for chaining
          */
@@ -93,8 +93,8 @@
         }
 
         /**
-         * Sets the maneuver to be performed on this step, or null if distance to this step is not
-         * provided.
+         * Sets the distance from the current position to the point where this navigation step
+         * should be executed, or null if distance to this step is not provided.
          *
          * @return this object for chaining
          */
diff --git a/car/core/res/layout/car_paged_scrollbar_buttons.xml b/car/core/res/layout/car_paged_scrollbar_buttons.xml
index 36d1642..3a430e2 100644
--- a/car/core/res/layout/car_paged_scrollbar_buttons.xml
+++ b/car/core/res/layout/car_paged_scrollbar_buttons.xml
@@ -21,9 +21,9 @@
 
     <ImageButton
         android:id="@+id/page_up"
+        android:background="?attr/selectableItemBackgroundBorderless"
         android:layout_width="@dimen/car_scroll_bar_button_size"
         android:layout_height="@dimen/car_scroll_bar_button_size"
-        android:background="@drawable/car_button_ripple_background"
         android:contentDescription="@string/scroll_bar_page_up_button"
         android:focusable="false"
         android:hapticFeedbackEnabled="false"
@@ -51,9 +51,9 @@
 
     <ImageButton
         android:id="@+id/page_down"
+        android:background="?attr/selectableItemBackgroundBorderless"
         android:layout_width="@dimen/car_scroll_bar_button_size"
         android:layout_height="@dimen/car_scroll_bar_button_size"
-        android:background="@drawable/car_button_ripple_background"
         android:contentDescription="@string/scroll_bar_page_down_button"
         android:focusable="false"
         android:hapticFeedbackEnabled="false"
diff --git a/car/core/res/values/themes.xml b/car/core/res/values/themes.xml
index e0ec1c1..e960ba5 100644
--- a/car/core/res/values/themes.xml
+++ b/car/core/res/values/themes.xml
@@ -44,7 +44,8 @@
         <item name="android:progressBackgroundTint">@color/car_seekbar_track_background_dark</item>
         <item name="android:progressTint">?attr/android:colorControlActivated</item>
         <item name="android:secondaryProgressTint">@color/car_seekbar_track_secondary_progress</item>
-        <item name="android:selectableItemBackground">@drawable/car_card_ripple_background</item>
+        <item name="android:selectableItemBackground">@drawable/car_card_ripple_background_light</item>
+        <item name="android:selectableItemBackgroundBorderless">@drawable/car_button_ripple_background_light</item>
         <item name="android:seekBarStyle">@style/Widget.Car.SeekBar.Light</item>
         <item name="android:textColor">@color/car_body1_light</item>
         <item name="android:textColorPrimary">@color/car_display1_light</item>
@@ -53,7 +54,7 @@
         <item name="android:spinnerStyle">@style/Widget.Spinner.Car</item>
         <item name="android:spinnerItemStyle">@style/CarSpinnerItem</item>
         <item name="android:spinnerDropDownItemStyle">@style/CarSpinnerItem</item>
-        <item name="actionBarItemBackground">@drawable/car_card_ripple_background</item>
+        <item name="actionBarItemBackground">@drawable/car_card_ripple_background_light</item>
         <item name="actionBarSize">@dimen/car_app_bar_height</item>
         <item name="actionButtonStyle">@style/Widget.Car.ActionButton.Light</item>
         <item name="actionMenuTextAppearance">@style/TextAppearance.Car.ActionBar.Menu</item>
@@ -79,6 +80,8 @@
         <item name="pagedListViewStyle">@style/Widget.Car.List.Light</item>
         <item name="pagedScrollBarViewStyle">@style/Widget.Car.Scrollbar.Light</item>
         <item name="seekBarStyle">@style/Widget.Car.SeekBar.Light</item>
+        <item name="selectableItemBackground">@drawable/car_card_ripple_background_light</item>
+        <item name="selectableItemBackgroundBorderless">@drawable/car_button_ripple_background_light</item>
         <item name="switchStyle">@style/Widget.Car.Switch</item>
         <item name="tabStyle">@style/Widget.Car.TabLayout</item>
         <item name="checkboxStyle">@style/Widget.Car.CheckBox.Light</item>
@@ -115,6 +118,7 @@
         <item name="android:progressTint">?attr/android:colorControlActivated</item>
         <item name="android:secondaryProgressTint">@color/car_seekbar_track_secondary_progress</item>
         <item name="android:selectableItemBackground">@drawable/car_card_ripple_background</item>
+        <item name="android:selectableItemBackgroundBorderless">@drawable/car_button_ripple_background</item>
         <item name="android:spinnerStyle">@style/Widget.Spinner.Car</item>
         <item name="android:spinnerItemStyle">@style/CarSpinnerItem</item>
         <item name="android:spinnerDropDownItemStyle">@style/CarSpinnerItem</item>
@@ -149,6 +153,8 @@
         <item name="pagedListViewStyle">@style/Widget.Car.List</item>
         <item name="pagedScrollBarViewStyle">@style/Widget.Car.Scrollbar</item>
         <item name="seekBarStyle">@style/Widget.Car.SeekBar</item>
+        <item name="selectableItemBackground">@drawable/car_card_ripple_background</item>
+        <item name="selectableItemBackgroundBorderless">@drawable/car_button_ripple_background</item>
         <item name="switchStyle">@style/Widget.Car.Switch</item>
         <item name="checkboxStyle">@style/Widget.Car.CheckBox</item>
         <item name="toolbarNavigationButtonStyle">@style/Widget.Car.Toolbar.Button.Navigation</item>
diff --git a/core/res/values-as/strings.xml b/core/res/values-as/strings.xml
new file mode 100644
index 0000000..3039039
--- /dev/null
+++ b/core/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="status_bar_notification_info_overflow" msgid="8106346172024741305">"৯৯৯+"</string>
+</resources>
diff --git a/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java b/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java
index 833f0e9..7bbaf62 100644
--- a/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java
+++ b/drawerlayout/src/main/java/androidx/drawerlayout/widget/DrawerLayout.java
@@ -192,6 +192,10 @@
     private static final boolean SET_DRAWER_SHADOW_FROM_ELEVATION =
             Build.VERSION.SDK_INT >= 21;
 
+    /** Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage. */
+    private static final String ACCESSIBILITY_CLASS_NAME =
+            "androidx.drawerlayout.widget.DrawerLayout";
+
     private final ChildAccessibilityDelegate mChildAccessibilityDelegate =
             new ChildAccessibilityDelegate();
     private float mDrawerElevation;
@@ -2352,7 +2356,7 @@
                 addChildrenForAccessibility(info, (ViewGroup) host);
             }
 
-            info.setClassName(DrawerLayout.class.getName());
+            info.setClassName(ACCESSIBILITY_CLASS_NAME);
 
             // This view reports itself as focusable so that it can intercept
             // the back button, but we should prevent this view from reporting
@@ -2366,7 +2370,7 @@
         public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
             super.onInitializeAccessibilityEvent(host, event);
 
-            event.setClassName(DrawerLayout.class.getName());
+            event.setClassName(ACCESSIBILITY_CLASS_NAME);
         }
 
         @Override
diff --git a/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
index 7dadb5b..c8bdf798 100644
--- a/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
+++ b/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java
@@ -20,6 +20,8 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.location.Location;
+import android.media.MediaDataSource;
+import android.media.MediaMetadataRetriever;
 import android.os.Build;
 import android.system.Os;
 import android.system.OsConstants;
@@ -68,7 +70,7 @@
 /**
  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
  * <p>
- * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
+ * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF and HEIF.
  * <p>
  * Attribute mutation is supported for JPEG image files.
  */
@@ -2842,6 +2844,10 @@
      */
     public static final int REDUCED_RESOLUTION_IMAGE = 1;
 
+    // TODO: Replace the following constants once Q is released. (b/124409340)
+    private static final int METADATA_KEY_EXIF_OFFSET = 33;
+    private static final int METADATA_KEY_EXIF_LENGTH = 34;
+
     // Maximum size for checking file type signature (see image_type_recognition_lite.cc)
     private static final int SIGNATURE_CHECK_SIZE = 5000;
 
@@ -2851,6 +2857,10 @@
     private static final int RAF_INFO_SIZE = 160;
     private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
 
+    private static final byte[] HEIF_TYPE_FTYP = new byte[] {'f', 't', 'y', 'p'};
+    private static final byte[] HEIF_BRAND_MIF1 = new byte[] {'m', 'i', 'f', '1'};
+    private static final byte[] HEIF_BRAND_HEIC = new byte[] {'h', 'e', 'i', 'c'};
+
     // See http://fileformats.archiveteam.org/wiki/Olympus_ORF
     private static final short ORF_SIGNATURE_1 = 0x4f52;
     private static final short ORF_SIGNATURE_2 = 0x5352;
@@ -3678,6 +3688,7 @@
     private static final int IMAGE_TYPE_RAF = 9;
     private static final int IMAGE_TYPE_RW2 = 10;
     private static final int IMAGE_TYPE_SRW = 11;
+    private static final int IMAGE_TYPE_HEIF = 12;
 
     static {
         sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
@@ -3734,7 +3745,7 @@
      */
     public ExifInterface(@NonNull File file) throws IOException {
         if (file == null) {
-            throw new IllegalArgumentException("file cannot be null");
+            throw new NullPointerException("file cannot be null");
         }
         initForFilename(file.getAbsolutePath());
     }
@@ -3743,9 +3754,6 @@
      * Reads Exif tags from the specified image file.
      */
     public ExifInterface(@NonNull String filename) throws IOException {
-        if (filename == null) {
-            throw new IllegalArgumentException("filename cannot be null");
-        }
         initForFilename(filename);
     }
 
@@ -3756,7 +3764,7 @@
      */
     public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
         if (fileDescriptor == null) {
-            throw new IllegalArgumentException("fileDescriptor cannot be null");
+            throw new NullPointerException("fileDescriptor cannot be null");
         }
         mAssetInputStream = null;
         mFilename = null;
@@ -3790,7 +3798,7 @@
      */
     public ExifInterface(@NonNull InputStream inputStream) throws IOException {
         if (inputStream == null) {
-            throw new IllegalArgumentException("inputStream cannot be null");
+            throw new NullPointerException("inputStream cannot be null");
         }
         mFilename = null;
         if (inputStream instanceof AssetManager.AssetInputStream) {
@@ -4283,6 +4291,10 @@
                     getRafAttributes(inputStream);
                     break;
                 }
+                case IMAGE_TYPE_HEIF: {
+                    getHeifAttributes(inputStream);
+                    break;
+                }
                 case IMAGE_TYPE_ORF: {
                     getOrfAttributes(inputStream);
                     break;
@@ -4754,6 +4766,9 @@
     }
 
     private void initForFilename(String filename) throws IOException {
+        if (filename == null) {
+            throw new NullPointerException("filename cannot be null");
+        }
         FileInputStream in = null;
         mAssetInputStream = null;
         mFilename = filename;
@@ -4819,6 +4834,8 @@
             return IMAGE_TYPE_JPEG;
         } else if (isRafFormat(signatureCheckBytes)) {
             return IMAGE_TYPE_RAF;
+        } else if (isHeifFormat(signatureCheckBytes)) {
+            return IMAGE_TYPE_HEIF;
         } else if (isOrfFormat(signatureCheckBytes)) {
             return IMAGE_TYPE_ORF;
         } else if (isRw2Format(signatureCheckBytes)) {
@@ -4857,6 +4874,78 @@
         return true;
     }
 
+    private boolean isHeifFormat(byte[] signatureCheckBytes) throws IOException {
+        ByteOrderedDataInputStream signatureInputStream = null;
+        try {
+            signatureInputStream = new ByteOrderedDataInputStream(signatureCheckBytes);
+            signatureInputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
+
+            long chunkSize = signatureInputStream.readInt();
+            byte[] chunkType = new byte[4];
+            signatureInputStream.read(chunkType);
+
+            if (!Arrays.equals(chunkType, HEIF_TYPE_FTYP)) {
+                return false;
+            }
+
+            long chunkDataOffset = 8;
+            if (chunkSize == 1) {
+                // This indicates that the next 8 bytes represent the chunk size,
+                // and chunk data comes after that.
+                chunkSize = signatureInputStream.readLong();
+                if (chunkSize < 16) {
+                    // The smallest valid chunk is 16 bytes long in this case.
+                    return false;
+                }
+                chunkDataOffset += 8;
+            }
+
+            // only sniff up to signatureCheckBytes.length
+            if (chunkSize > signatureCheckBytes.length) {
+                chunkSize = signatureCheckBytes.length;
+            }
+
+            long chunkDataSize = chunkSize - chunkDataOffset;
+
+            // It should at least have major brand (4-byte) and minor version (4-byte).
+            // The rest of the chunk (if any) is a list of (4-byte) compatible brands.
+            if (chunkDataSize < 8) {
+                return false;
+            }
+
+            byte[] brand = new byte[4];
+            boolean isMif1 = false;
+            boolean isHeic = false;
+            for (long i = 0; i < chunkDataSize / 4;  ++i) {
+                if (signatureInputStream.read(brand) != brand.length) {
+                    return false;
+                }
+                if (i == 1) {
+                    // Skip this index, it refers to the minorVersion, not a brand.
+                    continue;
+                }
+                if (Arrays.equals(brand, HEIF_BRAND_MIF1)) {
+                    isMif1 = true;
+                } else if (Arrays.equals(brand, HEIF_BRAND_HEIC)) {
+                    isHeic = true;
+                }
+                if (isMif1 && isHeic) {
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            if (DEBUG) {
+                Log.d(TAG, "Exception parsing HEIF file type box.", e);
+            }
+        } finally {
+            if (signatureInputStream != null) {
+                signatureInputStream.close();
+                signatureInputStream = null;
+            }
+        }
+        return false;
+    }
+
     /**
      * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
      * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
@@ -5145,6 +5234,166 @@
         }
     }
 
+    private void getHeifAttributes(final ByteOrderedDataInputStream in) throws IOException {
+        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+        try {
+            if (Build.VERSION.SDK_INT >= 23) {
+                retriever.setDataSource(new MediaDataSource() {
+                    long mPosition;
+
+                    @Override
+                    public void close() throws IOException {}
+
+                    @Override
+                    public int readAt(long position, byte[] buffer, int offset, int size)
+                            throws IOException {
+                        if (size == 0) {
+                            return 0;
+                        }
+                        if (position < 0) {
+                            return -1;
+                        }
+                        try {
+                            if (mPosition != position) {
+                                // We don't allow seek to positions after the available bytes,
+                                // the input stream won't be able to seek back then.
+                                // However, if we hit an exception before (mPosition set to -1),
+                                // let it try the seek in hope it might recover.
+                                if (mPosition >= 0 && position >= mPosition + in.available()) {
+                                    return -1;
+                                }
+                                in.seek(position);
+                                mPosition = position;
+                            }
+
+                            // If the read will cause us to go over the available bytes,
+                            // reduce the size so that we stay in the available range.
+                            // Otherwise the input stream may not be able to seek back.
+                            if (size > in.available()) {
+                                size = in.available();
+                            }
+
+                            int bytesRead = in.read(buffer, offset, size);
+                            if (bytesRead >= 0) {
+                                mPosition += bytesRead;
+                                return bytesRead;
+                            }
+                        } catch (IOException e) {
+                            // do nothing
+                        }
+                        mPosition = -1; // need to seek on next read
+                        return -1;
+                    }
+
+                    @Override
+                    public long getSize() throws IOException {
+                        return -1;
+                    }
+                });
+            } else {
+                if (mSeekableFileDescriptor != null) {
+                    retriever.setDataSource(mSeekableFileDescriptor);
+                } else if (mFilename != null) {
+                    retriever.setDataSource(mFilename);
+                } else {
+                    return;
+                }
+            }
+
+            String exifOffsetStr = retriever.extractMetadata(
+                    METADATA_KEY_EXIF_OFFSET);
+            String exifLengthStr = retriever.extractMetadata(
+                    METADATA_KEY_EXIF_LENGTH);
+            String hasImage = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
+            String hasVideo = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO);
+
+            String width = null;
+            String height = null;
+            String rotation = null;
+            final String metadataValueYes = "yes";
+            // If the file has both image and video, prefer image info over video info.
+            // App querying ExifInterface is most likely using the bitmap path which
+            // picks the image first.
+            if (metadataValueYes.equals(hasImage)) {
+                width = retriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
+                height = retriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
+                rotation = retriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
+            } else if (metadataValueYes.equals(hasVideo)) {
+                width = retriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
+                height = retriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
+                rotation = retriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
+            }
+
+            if (width != null) {
+                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_WIDTH,
+                        ExifAttribute.createUShort(Integer.parseInt(width), mExifByteOrder));
+            }
+
+            if (height != null) {
+                mAttributes[IFD_TYPE_PRIMARY].put(TAG_IMAGE_LENGTH,
+                        ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
+            }
+
+            if (rotation != null) {
+                int orientation = ExifInterface.ORIENTATION_NORMAL;
+
+                // all rotation angles in CW
+                switch (Integer.parseInt(rotation)) {
+                    case 90:
+                        orientation = ExifInterface.ORIENTATION_ROTATE_90;
+                        break;
+                    case 180:
+                        orientation = ExifInterface.ORIENTATION_ROTATE_180;
+                        break;
+                    case 270:
+                        orientation = ExifInterface.ORIENTATION_ROTATE_270;
+                        break;
+                }
+
+                mAttributes[IFD_TYPE_PRIMARY].put(TAG_ORIENTATION,
+                        ExifAttribute.createUShort(orientation, mExifByteOrder));
+            }
+
+            if (exifOffsetStr != null && exifLengthStr != null) {
+                int offset = Integer.parseInt(exifOffsetStr);
+                int length = Integer.parseInt(exifLengthStr);
+                if (length <= 6) {
+                    throw new IOException("Invalid exif length");
+                }
+                in.seek(offset);
+                byte[] identifier = new byte[6];
+                if (in.read(identifier) != 6) {
+                    throw new IOException("Can't read identifier");
+                }
+                offset += 6;
+                length -= 6;
+                if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+                    throw new IOException("Invalid identifier");
+                }
+
+                byte[] bytes = new byte[length];
+                if (in.read(bytes) != length) {
+                    throw new IOException("Can't read exif");
+                }
+                readExifSegment(bytes, IFD_TYPE_PRIMARY);
+            }
+
+            if (DEBUG) {
+                Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
+            }
+        } finally {
+            retriever.release();
+        }
+    }
+
     /**
      * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
      * images. Both data takes the form of IFDs and can therefore be read with the
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt b/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt
index a76792b..c4ef2a8 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt
+++ b/fragment/src/androidTest/java/androidx/fragment/app/BackStackStateTest.kt
@@ -17,11 +17,16 @@
 package androidx.fragment.app
 
 import android.os.Parcel
+import androidx.fragment.app.FragmentTestUtil.shutdownFragmentController
+import androidx.fragment.app.FragmentTestUtil.startupFragmentController
 import androidx.fragment.app.test.EmptyFragmentTestActivity
+import androidx.lifecycle.ViewModelStore
+import androidx.test.annotation.UiThreadTest
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.rule.ActivityTestRule
 import com.google.common.truth.Truth.assertThat
+import junit.framework.Assert.fail
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -56,4 +61,168 @@
         assertThat(restoredBackStackState.mReorderingAllowed)
             .isEqualTo(backStackState.mReorderingAllowed)
     }
+
+    @Test
+    @UiThreadTest
+    fun testHideOnFragmentWithAManager() {
+        val viewModelStore1 = ViewModelStore()
+        val fc1 = startupFragmentController(activityRule.activity, null, viewModelStore1)
+        val fm1 = fc1.supportFragmentManager
+
+        val viewModelStore2 = ViewModelStore()
+        val fc2 = startupFragmentController(activityRule.activity, null, viewModelStore2)
+        val fm2 = fc2.supportFragmentManager
+
+        val fragment1 = Fragment()
+
+        fm1.beginTransaction().attach(fragment1).commitNow()
+        try {
+            fm2.beginTransaction().hide(fragment1).commitNow()
+            fail("Fragment associated with another" +
+                    " FragmentManager should throw IllegalStateException")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Cannot hide Fragment attached to " +
+                            "a different FragmentManager. Fragment " + fragment1.toString() +
+                            " is already attached to a FragmentManager."
+                )
+        }
+
+        // Bring the state back down to destroyed before we finish the test
+        shutdownFragmentController(fc1, viewModelStore1)
+        shutdownFragmentController(fc2, viewModelStore2)
+    }
+
+    @Test
+    @UiThreadTest
+    fun testShowOnFragmentWithAManager() {
+        val viewModelStore1 = ViewModelStore()
+        val fc1 = startupFragmentController(activityRule.activity, null, viewModelStore1)
+        val fm1 = fc1.supportFragmentManager
+
+        val viewModelStore2 = ViewModelStore()
+        val fc2 = startupFragmentController(activityRule.activity, null, viewModelStore2)
+        val fm2 = fc2.supportFragmentManager
+
+        val fragment1 = Fragment()
+
+        fm1.beginTransaction().attach(fragment1).commitNow()
+        try {
+            fm2.beginTransaction().show(fragment1).commitNow()
+            fail("Fragment associated with another" +
+                    " FragmentManager should throw IllegalStateException")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Cannot show Fragment attached to " +
+                            "a different FragmentManager. Fragment " + fragment1.toString() +
+                            " is already attached to a FragmentManager."
+                )
+        }
+
+        // Bring the state back down to destroyed before we finish the test
+        shutdownFragmentController(fc1, viewModelStore1)
+        shutdownFragmentController(fc2, viewModelStore2)
+    }
+
+    @Test
+    @UiThreadTest
+    fun testSetPrimaryNavigationFragmentOnFragmentWithAManager() {
+        val viewModelStore1 = ViewModelStore()
+        val fc1 = startupFragmentController(activityRule.activity, null, viewModelStore1)
+        val fm1 = fc1.supportFragmentManager
+
+        val viewModelStore2 = ViewModelStore()
+        val fc2 = startupFragmentController(activityRule.activity, null, viewModelStore2)
+        val fm2 = fc2.supportFragmentManager
+
+        val fragment1 = Fragment()
+
+        fm1.beginTransaction().attach(fragment1).commitNow()
+        try {
+            fm2.beginTransaction().setPrimaryNavigationFragment(fragment1).commitNow()
+            fail("Fragment associated with another" +
+                    " FragmentManager should throw IllegalStateException")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Cannot setPrimaryNavigation for Fragment attached to " +
+                            "a different FragmentManager. Fragment " + fragment1.toString() +
+                            " is already attached to a FragmentManager."
+                )
+        }
+
+        // Bring the state back down to destroyed before we finish the test
+        shutdownFragmentController(fc1, viewModelStore1)
+        shutdownFragmentController(fc2, viewModelStore2)
+    }
+
+    @Test
+    @UiThreadTest
+    fun testDetachFragmentWithManager() {
+        val viewModelStore1 = ViewModelStore()
+        val fc1 = startupFragmentController(activityRule.activity, null, viewModelStore1)
+        val fm1 = fc1.supportFragmentManager
+
+        val viewModelStore2 = ViewModelStore()
+        val fc2 = startupFragmentController(activityRule.activity, null, viewModelStore2)
+        val fm2 = fc2.supportFragmentManager
+
+        // Add the initial state
+        val fragment1 = StrictFragment()
+
+        fm1.beginTransaction().attach(fragment1).commitNow()
+
+        try {
+            fm2.beginTransaction().detach(fragment1).commitNow()
+            fail("Fragment associated with another" +
+                    " FragmentManager should throw IllegalStateException")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Cannot detach Fragment attached to " +
+                            "a different FragmentManager. Fragment " + fragment1.toString() +
+                            " is already attached to a FragmentManager."
+                )
+        }
+
+        // Bring the state back down to destroyed before we finish the test
+        shutdownFragmentController(fc1, viewModelStore1)
+        shutdownFragmentController(fc2, viewModelStore2)
+    }
+
+    @Test
+    @UiThreadTest
+    fun testRemoveFragmentWithManager() {
+        val viewModelStore1 = ViewModelStore()
+        val fc1 = startupFragmentController(activityRule.activity, null, viewModelStore1)
+        val fm1 = fc1.supportFragmentManager
+
+        val viewModelStore2 = ViewModelStore()
+        val fc2 = startupFragmentController(activityRule.activity, null, viewModelStore2)
+        val fm2 = fc2.supportFragmentManager
+
+        // Add the initial state
+        val fragment1 = StrictFragment()
+
+        fm1.beginTransaction().attach(fragment1).commitNow()
+
+        try {
+            fm2.beginTransaction().remove(fragment1).commitNow()
+            fail("Fragment associated with another" +
+                    " FragmentManager should throw IllegalStateException")
+        } catch (e: IllegalStateException) {
+            assertThat(e)
+                .hasMessageThat().contains(
+                    "Cannot remove Fragment attached to " +
+                            "a different FragmentManager. Fragment " + fragment1.toString() +
+                            " is already attached to a FragmentManager."
+                )
+        }
+
+        // Bring the state back down to destroyed before we finish the test
+        shutdownFragmentController(fc1, viewModelStore1)
+        shutdownFragmentController(fc2, viewModelStore2)
+    }
 }
diff --git a/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java b/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
index f4eb53c..cccbffb 100644
--- a/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
+++ b/fragment/src/main/java/androidx/fragment/app/BackStackRecord.java
@@ -304,7 +304,6 @@
         if (containerViewId == 0) {
             throw new IllegalArgumentException("Must use non-zero containerViewId");
         }
-
         doAddOp(containerViewId, fragment, tag, OP_REPLACE);
         return this;
     }
@@ -312,6 +311,11 @@
     @NonNull
     @Override
     public FragmentTransaction remove(@NonNull Fragment fragment) {
+        if (fragment.mFragmentManager != null && fragment.mFragmentManager != mManager) {
+            throw new IllegalStateException("Cannot remove Fragment attached to "
+                    + "a different FragmentManager. Fragment " + fragment.toString() + " is already"
+                    + " attached to a FragmentManager.");
+        }
         addOp(new Op(OP_REMOVE, fragment));
 
         return this;
@@ -320,6 +324,11 @@
     @NonNull
     @Override
     public FragmentTransaction hide(@NonNull Fragment fragment) {
+        if (fragment.mFragmentManager != null && fragment.mFragmentManager != mManager) {
+            throw new IllegalStateException("Cannot hide Fragment attached to "
+                    + "a different FragmentManager. Fragment " + fragment.toString() + " is already"
+                    + " attached to a FragmentManager.");
+        }
         addOp(new Op(OP_HIDE, fragment));
 
         return this;
@@ -328,6 +337,11 @@
     @NonNull
     @Override
     public FragmentTransaction show(@NonNull Fragment fragment) {
+        if (fragment.mFragmentManager != null && fragment.mFragmentManager != mManager) {
+            throw new IllegalStateException("Cannot show Fragment attached to "
+                    + "a different FragmentManager. Fragment " + fragment.toString() + " is already"
+                    + " attached to a FragmentManager.");
+        }
         addOp(new Op(OP_SHOW, fragment));
 
         return this;
@@ -336,8 +350,12 @@
     @NonNull
     @Override
     public FragmentTransaction detach(@NonNull Fragment fragment) {
+        if (fragment.mFragmentManager != null && fragment.mFragmentManager != mManager) {
+            throw new IllegalStateException("Cannot detach Fragment attached to "
+                    + "a different FragmentManager. Fragment " + fragment.toString() + " is already"
+                    + " attached to a FragmentManager.");
+        }
         addOp(new Op(OP_DETACH, fragment));
-
         return this;
     }
 
@@ -352,6 +370,12 @@
     @NonNull
     @Override
     public FragmentTransaction setPrimaryNavigationFragment(@Nullable Fragment fragment) {
+        if (fragment != null
+                && fragment.mFragmentManager != null && fragment.mFragmentManager != mManager) {
+            throw new IllegalStateException("Cannot setPrimaryNavigation for Fragment attached to "
+                    + "a different FragmentManager. Fragment " + fragment.toString() + " is already"
+                    + " attached to a FragmentManager.");
+        }
         addOp(new Op(OP_SET_PRIMARY_NAV, fragment));
 
         return this;
diff --git a/fragment/src/main/java/androidx/fragment/app/Fragment.java b/fragment/src/main/java/androidx/fragment/app/Fragment.java
index c081639..b94556d 100644
--- a/fragment/src/main/java/androidx/fragment/app/Fragment.java
+++ b/fragment/src/main/java/androidx/fragment/app/Fragment.java
@@ -1576,7 +1576,6 @@
     @CallSuper
     public void onCreate(@Nullable Bundle savedInstanceState) {
         mCalled = true;
-        mSavedStateRegistryController.performRestore(savedInstanceState);
         restoreChildFragmentState(savedInstanceState);
         if (mChildFragmentManager != null
                 && !mChildFragmentManager.isStateAtLeast(Fragment.CREATED)) {
@@ -2520,6 +2519,7 @@
         }
         mState = CREATED;
         mCalled = false;
+        mSavedStateRegistryController.performRestore(savedInstanceState);
         onCreate(savedInstanceState);
         mIsCreated = true;
         if (!mCalled) {
diff --git a/jetifier/jetifier/core/src/main/resources/default.config b/jetifier/jetifier/core/src/main/resources/default.config
index 8ac50f4..132e4cc 100644
--- a/jetifier/jetifier/core/src/main/resources/default.config
+++ b/jetifier/jetifier/core/src/main/resources/default.config
@@ -1644,11 +1644,11 @@
         },
         {
             "from": { "groupId": "com.android.support.constraint", "artifactId": "constraint-layout", "version": "1.1.0" },
-            "to": { "groupId": "androidx.constraintlayout", "artifactId": "constraintlayout", "version": "1.1.2" }
+            "to": { "groupId": "androidx.constraintlayout", "artifactId": "constraintlayout", "version": "1.1.3" }
         },
         {
             "from": { "groupId": "com.android.support.constraint", "artifactId": "constraint-layout-solver", "version": "1.1.0" },
-            "to": { "groupId": "androidx.constraintlayout", "artifactId": "constraintlayout-solver", "version": "1.1.2" }
+            "to": { "groupId": "androidx.constraintlayout", "artifactId": "constraintlayout-solver", "version": "1.1.3" }
         },
         {
             "from": { "groupId": "com.android.support.test", "artifactId": "orchestrator", "version": "1.0.2" },
diff --git a/jetifier/jetifier/core/src/main/resources/default.generated.config b/jetifier/jetifier/core/src/main/resources/default.generated.config
index aed4839..8319255 100644
--- a/jetifier/jetifier/core/src/main/resources/default.generated.config
+++ b/jetifier/jetifier/core/src/main/resources/default.generated.config
@@ -2235,7 +2235,7 @@
       "to": {
         "groupId": "androidx.constraintlayout",
         "artifactId": "constraintlayout",
-        "version": "1.1.2"
+        "version": "1.1.3"
       }
     },
     {
@@ -2247,7 +2247,7 @@
       "to": {
         "groupId": "androidx.constraintlayout",
         "artifactId": "constraintlayout-solver",
-        "version": "1.1.2"
+        "version": "1.1.3"
       }
     },
     {
diff --git a/jetifier/jetifier/migration.config b/jetifier/jetifier/migration.config
index 1acdde8..6e09ec1 100644
--- a/jetifier/jetifier/migration.config
+++ b/jetifier/jetifier/migration.config
@@ -56,10 +56,6 @@
       "to": "androidx/cardview/R{0}"
     },
     {
-      "from": "android/support/percent/R(.*)",
-      "to": "androidx/percentlayout/R{0}"
-    },
-    {
       "from": "android/support/coordinatorlayout/R(.*)",
       "to": "androidx/coordinatorlayout/R{0}"
     },
@@ -292,10 +288,6 @@
       "to": "androidx/leanback/{0}"
     },
     {
-      "from": "android/support/percent/(.*)",
-      "to": "androidx/percentlayout/widget/{0}"
-    },
-    {
       "from": "android/support/annotation/(.*)",
       "to": "androidx/annotation/{0}"
     },
@@ -722,6 +714,14 @@
       "to": "ignore"
     },
     {
+      "from": "androidx/percentlayout/R(.*)",
+      "to": "ignore"
+    },
+    {
+      "from": "androidx/percentlayout/widget/(.*)",
+      "to": "ignore"
+    },
+    {
       "from": "androidx/swiperefreshlayout/widget/CircleImageView(.*)",
       "to": "ignore"
     },
@@ -1012,7 +1012,7 @@
       "to": "androidx/viewpager"
     },
     {
-      "from": "android/support/percent",
+      "from": "androidx/percentlayout",
       "to": "androidx/percentlayout"
     },
     {
@@ -1399,9 +1399,9 @@
     },
     {
       "from": {
-        "groupId": "com.android.support",
-        "artifactId": "percent",
-        "version": "{oldSlVersion}"
+        "groupId": "androidx.percentlayout",
+        "artifactId": "percentlayout",
+        "version": "{newSlVersion}"
       },
       "to": {
         "groupId": "androidx.percentlayout",
@@ -2246,7 +2246,7 @@
       "to": {
         "groupId": "androidx.constraintlayout",
         "artifactId": "constraintlayout",
-        "version": "1.1.2"
+        "version": "1.1.3"
       }
     },
     {
@@ -2258,7 +2258,7 @@
       "to": {
         "groupId": "androidx.constraintlayout",
         "artifactId": "constraintlayout-solver",
-        "version": "1.1.2"
+        "version": "1.1.3"
       }
     },
     {
@@ -3239,10 +3239,6 @@
       "android/support/multidex/MultiDexApplication": "androidx/multidex/MultiDexApplication",
       "android/support/multidex/MultiDexExtractor": "androidx/multidex/MultiDexExtractor",
       "android/support/multidex/ZipUtil": "androidx/multidex/ZipUtil",
-      "android/support/percent/PercentFrameLayout": "androidx/percentlayout/widget/PercentFrameLayout",
-      "android/support/percent/PercentLayoutHelper": "androidx/percentlayout/widget/PercentLayoutHelper",
-      "android/support/percent/PercentRelativeLayout": "androidx/percentlayout/widget/PercentRelativeLayout",
-      "android/support/percent/R": "androidx/percentlayout/R",
       "android/support/test/InstrumentationRegistry": "androidx/test/InstrumentationRegistry",
       "android/support/test/annotation/Beta": "androidx/test/annotation/Beta",
       "android/support/test/annotation/UiThreadTest": "androidx/test/annotation/UiThreadTest",
diff --git a/leanback/src/main/java/androidx/leanback/widget/SearchEditText.java b/leanback/src/main/java/androidx/leanback/widget/SearchEditText.java
index a9b29f1..06367fb 100644
--- a/leanback/src/main/java/androidx/leanback/widget/SearchEditText.java
+++ b/leanback/src/main/java/androidx/leanback/widget/SearchEditText.java
@@ -18,7 +18,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
-import androidx.core.widget.TextViewCompat;
 import androidx.leanback.R;
 
 /**
@@ -58,8 +57,8 @@
             if (DEBUG) Log.v(TAG, "Keyboard being dismissed");
             if (mKeyboardDismissListener != null) {
                 mKeyboardDismissListener.onKeyboardDismiss();
+                return true;
             }
-            return false;
         }
         return super.onKeyPreIme(keyCode, event);
     }
diff --git a/leanback/src/main/java/androidx/leanback/widget/StreamingTextView.java b/leanback/src/main/java/androidx/leanback/widget/StreamingTextView.java
index b41d663..b5805eb 100644
--- a/leanback/src/main/java/androidx/leanback/widget/StreamingTextView.java
+++ b/leanback/src/main/java/androidx/leanback/widget/StreamingTextView.java
@@ -72,6 +72,10 @@
         }
     };
 
+    /** Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage. */
+    private static final String ACCESSIBILITY_CLASS_NAME =
+            "androidx.leanback.widget.StreamingTextView";
+
     final Random mRandom = new Random();
 
     Bitmap mOneDot;
@@ -230,7 +234,7 @@
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
-        info.setClassName(StreamingTextView.class.getCanonicalName());
+        info.setClassName(ACCESSIBILITY_CLASS_NAME);
     }
 
     private class DottySpan extends ReplacementSpan {
diff --git a/leanback/src/main/res/values-as/strings.xml b/leanback/src/main/res/values-as/strings.xml
index eb3b2b0..05b84d6 100644
--- a/leanback/src/main/res/values-as/strings.xml
+++ b/leanback/src/main/res/values-as/strings.xml
@@ -49,7 +49,7 @@
     <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"ছাব-টাইটেল অক্ষম কৰক"</string>
     <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"চিত্ৰৰ ভিতৰত চিত্ৰ ম\'ড আৰম্ভ কৰক"</string>
     <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
-    <string name="lb_playback_controls_shown" msgid="7794717158616536936">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ দেখুওৱা হ\'ল"</string>
+    <string name="lb_playback_controls_shown" msgid="7794717158616536936">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ দেখুওৱা হ’ল"</string>
     <string name="lb_playback_controls_hidden" msgid="619396299825306757">"মিডিয়াৰ নিয়ন্ত্ৰণসমূহ লুকুৱাই ৰখা হৈছে, দেখুওৱাবলৈ ডি-পেডত টিপক"</string>
     <string name="lb_guidedaction_finish_title" msgid="7747913934287176843">"সমাপ্ত"</string>
     <string name="lb_guidedaction_continue_title" msgid="1122271825827282965">"অব্যাহত ৰাখক"</string>
diff --git a/leanback/src/main/res/values-bs/strings.xml b/leanback/src/main/res/values-bs/strings.xml
index dbdfdea..6c429a0 100644
--- a/leanback/src/main/res/values-bs/strings.xml
+++ b/leanback/src/main/res/values-bs/strings.xml
@@ -55,5 +55,5 @@
     <string name="lb_guidedaction_continue_title" msgid="1122271825827282965">"Nastavi"</string>
     <string name="lb_media_player_error" msgid="8748646000835486516">"Kôd greške MediaPlayera %1$d dodatno %2$d"</string>
     <string name="lb_onboarding_get_started" msgid="7674487829030291492">"ZAPOČNITE"</string>
-    <string name="lb_onboarding_accessibility_next" msgid="4213611627196077555">"Dalje"</string>
+    <string name="lb_onboarding_accessibility_next" msgid="4213611627196077555">"Naprijed"</string>
 </resources>
diff --git a/leanback/src/main/res/values-ne/strings.xml b/leanback/src/main/res/values-ne/strings.xml
index 416fc65..5cf37cd 100644
--- a/leanback/src/main/res/values-ne/strings.xml
+++ b/leanback/src/main/res/values-ne/strings.xml
@@ -47,7 +47,7 @@
     <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"उच्च गुणस्तरलाई असक्षम पार्नुहोस्"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"उप शीर्षकहरू देखाउने सुविधालाई सक्षम पार्नुहोस्"</string>
     <string name="lb_playback_controls_closed_captioning_disable" msgid="5508271941331836786">"उप शीर्षकहरू देखाउने सुविधालाई असक्षम पार्नुहोस्"</string>
-    <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"तस्बिरभित्र तस्बिर नामक मोडमा प्रविष्ट गर्नुहोस्"</string>
+    <string name="lb_playback_controls_picture_in_picture" msgid="8800305194045609275">"तस्बिरभित्र तस्बिर नामक मोडमा प्रविष्टि गर्नुहोस्"</string>
     <string name="lb_playback_time_separator" msgid="6549544638083578695">"/"</string>
     <string name="lb_playback_controls_shown" msgid="7794717158616536936">"मिडियाका नियन्त्रणहरू देखाइएका छन्"</string>
     <string name="lb_playback_controls_hidden" msgid="619396299825306757">"मिडियाका नियन्त्रणहरूलाई लुकाइएको छ, देखाउनका लागि d-pad नामक बटन थिच्नुहोस्"</string>
diff --git a/leanback/src/main/res/values-ta/strings.xml b/leanback/src/main/res/values-ta/strings.xml
index 10c84ca..d0b2641 100644
--- a/leanback/src/main/res/values-ta/strings.xml
+++ b/leanback/src/main/res/values-ta/strings.xml
@@ -41,8 +41,8 @@
     <string name="lb_playback_controls_repeat_none" msgid="5812341701962930499">"எதையும் மீண்டும் இயக்காதே"</string>
     <string name="lb_playback_controls_repeat_all" msgid="5164826436271322261">"அனைத்தையும் மீண்டும் இயக்கு"</string>
     <string name="lb_playback_controls_repeat_one" msgid="7675097479246139440">"ஒன்றை மட்டும் மீண்டும் இயக்கு"</string>
-    <string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"கலைத்து இயக்கு"</string>
-    <string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"கலைக்காமல் இயக்கு"</string>
+    <string name="lb_playback_controls_shuffle_enable" msgid="7809089255981448519">"வரிசை மாற்றி இயக்கு"</string>
+    <string name="lb_playback_controls_shuffle_disable" msgid="8182435535948303910">"வரிசை மாற்றி இயக்குவதை நிறுத்து"</string>
     <string name="lb_playback_controls_high_quality_enable" msgid="1862669142355962638">"உயர்தரத்தை இயக்கு"</string>
     <string name="lb_playback_controls_high_quality_disable" msgid="3000046054608531995">"உயர்தரத்தை முடக்கு"</string>
     <string name="lb_playback_controls_closed_captioning_enable" msgid="3934392140182327163">"விரிவான வசனங்களை இயக்கு"</string>
diff --git a/media/api/1.1.0-alpha02.txt b/media/api/1.1.0-alpha02.txt
index 881051e..c4e14c0 100644
--- a/media/api/1.1.0-alpha02.txt
+++ b/media/api/1.1.0-alpha02.txt
@@ -285,7 +285,6 @@
     method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
     method public abstract void sendCustomAction(String!, android.os.Bundle!);
     method public abstract void setCaptioningEnabled(boolean);
-    method public void setPlaybackSpeed(float);
     method public abstract void setRating(android.support.v4.media.RatingCompat!);
     method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public abstract void setRepeatMode(int);
@@ -364,7 +363,6 @@
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetCaptioningEnabled(boolean);
-    method public void onSetPlaybackSpeed(float);
     method public void onSetRating(android.support.v4.media.RatingCompat!);
     method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public void onSetRepeatMode(int);
diff --git a/media/api/current.txt b/media/api/current.txt
index 881051e..c4e14c0 100644
--- a/media/api/current.txt
+++ b/media/api/current.txt
@@ -285,7 +285,6 @@
     method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
     method public abstract void sendCustomAction(String!, android.os.Bundle!);
     method public abstract void setCaptioningEnabled(boolean);
-    method public void setPlaybackSpeed(float);
     method public abstract void setRating(android.support.v4.media.RatingCompat!);
     method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public abstract void setRepeatMode(int);
@@ -364,7 +363,6 @@
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetCaptioningEnabled(boolean);
-    method public void onSetPlaybackSpeed(float);
     method public void onSetRating(android.support.v4.media.RatingCompat!);
     method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public void onSetRepeatMode(int);
diff --git a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index 49956a5..813f30f 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -1304,7 +1304,9 @@
          * Set the playback speed.
          *
          * @param speed The playback speed
+         * @hide
          */
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
         public void setPlaybackSpeed(float speed) {}
 
         /**
diff --git a/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index 95eff3c..cfe0e3b 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -1285,7 +1285,9 @@
          * @param speed the playback speed
          * @see #setPlaybackState(PlaybackStateCompat)
          * @see PlaybackStateCompat.Builder#setState(int, long, float)
+         * @hide
          */
+        @RestrictTo(LIBRARY_GROUP_PREFIX)
         public void onSetPlaybackSpeed(float speed) {
         }
 
diff --git a/media2-widget/api/1.0.0-alpha07.txt b/media2-widget/api/1.0.0-alpha07.txt
index cccfb1d..d73f1c3 100644
--- a/media2-widget/api/1.0.0-alpha07.txt
+++ b/media2-widget/api/1.0.0-alpha07.txt
@@ -6,7 +6,7 @@
     ctor public MediaControlView(android.content.Context, android.util.AttributeSet?);
     ctor public MediaControlView(android.content.Context, android.util.AttributeSet?, int);
     method public void requestPlayButtonFocus();
-    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener);
+    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener?);
     method public void setSessionToken(androidx.media2.SessionToken);
   }
 
diff --git a/media2-widget/api/current.txt b/media2-widget/api/current.txt
index cccfb1d..d73f1c3 100644
--- a/media2-widget/api/current.txt
+++ b/media2-widget/api/current.txt
@@ -6,7 +6,7 @@
     ctor public MediaControlView(android.content.Context, android.util.AttributeSet?);
     ctor public MediaControlView(android.content.Context, android.util.AttributeSet?, int);
     method public void requestPlayButtonFocus();
-    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener);
+    method public void setOnFullScreenListener(androidx.media2.widget.MediaControlView.OnFullScreenListener?);
     method public void setSessionToken(androidx.media2.SessionToken);
   }
 
diff --git a/media2-widget/src/androidTest/java/androidx/media2/widget/MediaControlViewTest.java b/media2-widget/src/androidTest/java/androidx/media2/widget/MediaControlViewTest.java
index 9b856c7..e6acd7e 100644
--- a/media2-widget/src/androidTest/java/androidx/media2/widget/MediaControlViewTest.java
+++ b/media2-widget/src/androidTest/java/androidx/media2/widget/MediaControlViewTest.java
@@ -20,10 +20,13 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withParent;
 
 import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -147,8 +150,7 @@
             }
         });
         assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        onView(allOf((withId(R.id.pause)),
-                withParent(withId(R.id.full_transport_controls)))).perform(click());
+        onView(allOf(withId(R.id.pause), isCompletelyDisplayed())).perform(click());
         assertTrue(latchForPlayingState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
@@ -181,8 +183,7 @@
             }
         });
         assertTrue(latchForPausedState.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        onView(allOf((withId(R.id.ffwd)),
-                withParent(withId(R.id.full_transport_controls)))).perform(click());
+        onView(allOf(withId(R.id.ffwd), isCompletelyDisplayed())).perform(click());
         assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
@@ -230,8 +231,7 @@
             }
         });
         assertTrue(latchForFfwd.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
-        onView(allOf((withId(R.id.rew)),
-                withParent(withId(R.id.full_transport_controls)))).perform(click());
+        onView(allOf(withId(R.id.rew), isCompletelyDisplayed())).perform(click());
         assertTrue(latchForRew.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
     }
 
@@ -358,6 +358,45 @@
         testCheckMediaItemIsFromNetwork(Uri.parse("file:///dummy.mp4"), false);
     }
 
+    @Test
+    public void testFullScreenListener() throws Throwable {
+        onView(withId(R.id.fullscreen)).check(matches(not(isDisplayed())));
+
+        final CountDownLatch latchOn = new CountDownLatch(1);
+        final CountDownLatch latchOff = new CountDownLatch(1);
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mVideoView.getMediaControlView().setOnFullScreenListener(
+                        new MediaControlView.OnFullScreenListener() {
+                            @Override
+                            public void onFullScreen(@NonNull View view, boolean fullScreen) {
+                                if (fullScreen) {
+                                    latchOn.countDown();
+                                } else {
+                                    latchOff.countDown();
+                                }
+                            }
+                        });
+            }
+        });
+        onView(withId(R.id.fullscreen)).check(matches(isDisplayed()));
+
+        onView(withId(R.id.fullscreen)).perform(click());
+        assertTrue(latchOn.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+        onView(withId(R.id.fullscreen)).perform(click());
+        assertTrue(latchOff.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mVideoView.getMediaControlView().setOnFullScreenListener(null);
+            }
+        });
+        onView(withId(R.id.fullscreen)).check(matches(not(isDisplayed())));
+    }
+
     private void testCheckMediaItemIsFromNetwork(Uri uri, boolean isNetwork) throws Throwable {
         final MediaItem mediaItem = createTestMediaItem2(uri);
         final CountDownLatch latch = new CountDownLatch(1);
diff --git a/media2-widget/src/main/java/androidx/media2/widget/MediaControlView.java b/media2-widget/src/main/java/androidx/media2/widget/MediaControlView.java
index 07ceb15..e59e4b4 100644
--- a/media2-widget/src/main/java/androidx/media2/widget/MediaControlView.java
+++ b/media2-widget/src/main/java/androidx/media2/widget/MediaControlView.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.AttributeSet;
@@ -293,13 +292,20 @@
     }
 
     /**
-     * Registers a callback to be invoked when the fullscreen mode should be changed.
-     * This needs to be implemented in order to display the fullscreen button.
-     * @param l The callback that will be run
+     * Sets a listener to be called when the fullscreen mode should be changed.
+     * A non-null listener needs to be set in order to display the fullscreen button.
+     *
+     * @param listener The listener to be called. A value of <code>null</code> removes any
+     * existing listener and hides the fullscreen button.
      */
-    public void setOnFullScreenListener(@NonNull OnFullScreenListener l) {
-        mOnFullScreenListener = l;
-        mFullScreenButton.setVisibility(View.VISIBLE);
+    public void setOnFullScreenListener(@Nullable OnFullScreenListener listener) {
+        if (listener == null) {
+            mOnFullScreenListener = null;
+            mFullScreenButton.setVisibility(View.GONE);
+        } else {
+            mOnFullScreenListener = listener;
+            mFullScreenButton.setVisibility(View.VISIBLE);
+        }
     }
 
     /**
@@ -325,7 +331,8 @@
 
     @Override
     public CharSequence getAccessibilityClassName() {
-        return MediaControlView.class.getName();
+        // Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage.
+        return "androidx.media2.widget.MediaControlView";
     }
 
     @Override
@@ -459,6 +466,8 @@
                 sizeType != SIZE_TYPE_MINIMAL ? View.VISIBLE : View.INVISIBLE);
         mMinimalFullScreenButton.setVisibility(
                 sizeType == SIZE_TYPE_MINIMAL ? View.VISIBLE : View.INVISIBLE);
+        mCenterView.setVisibility(
+                sizeType != SIZE_TYPE_FULL ? View.VISIBLE : View.INVISIBLE);
 
         final int childLeft = getPaddingLeft();
         final int childRight = childLeft + width;
@@ -642,17 +651,18 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 float alpha = (float) animation.getAnimatedValue();
-                Drawable thumb = mProgress.getThumb();
-                if (thumb != null) {
-                    thumb.setLevel((int) (MAX_SCALE_LEVEL * alpha));
-                }
+                int scaleLevel = mSizeType == SIZE_TYPE_MINIMAL ? 0 : MAX_SCALE_LEVEL;
+                mProgress.getThumb().setLevel((int) (scaleLevel * alpha));
 
                 mCenterView.setAlpha(alpha);
                 mMinimalFullScreenView.setAlpha(alpha);
-                if (alpha == 0.0f) {
-                    mCenterView.setVisibility(View.INVISIBLE);
-                    mMinimalFullScreenView.setVisibility(View.INVISIBLE);
-                }
+            }
+        });
+        fadeOutAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mCenterView.setVisibility(View.INVISIBLE);
+                mMinimalFullScreenView.setVisibility(View.INVISIBLE);
             }
         });
 
@@ -662,17 +672,20 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 float alpha = (float) animation.getAnimatedValue();
-                Drawable thumb = mProgress.getThumb();
-                if (thumb != null) {
-                    thumb.setLevel((int) (MAX_SCALE_LEVEL * alpha));
-                }
+                int scaleLevel = mSizeType == SIZE_TYPE_MINIMAL ? 0 : MAX_SCALE_LEVEL;
+                mProgress.getThumb().setLevel((int) (scaleLevel * alpha));
 
                 mCenterView.setAlpha(alpha);
                 mMinimalFullScreenView.setAlpha(alpha);
-                if (alpha == 0.0f) {
+            }
+        });
+        fadeInAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                if (mSizeType != SIZE_TYPE_FULL) {
                     mCenterView.setVisibility(View.VISIBLE);
-                    mMinimalFullScreenView.setVisibility(View.VISIBLE);
                 }
+                mMinimalFullScreenView.setVisibility(View.VISIBLE);
             }
         });
 
@@ -681,34 +694,32 @@
                 .with(AnimatorUtil.ofTranslationY(0, -titleBarHeight, mTitleBar))
                 .with(AnimatorUtil.ofTranslationYTogether(0, bottomBarHeight, bottomBarGroup));
         mHideMainBarsAnimator.setDuration(HIDE_TIME_MS);
-        mHideMainBarsAnimator.getChildAnimations().get(0).addListener(
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        mUxState = UX_STATE_ANIMATING;
-                    }
+        mHideMainBarsAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mUxState = UX_STATE_ANIMATING;
+            }
 
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mUxState = UX_STATE_ONLY_PROGRESS_VISIBLE;
-                    }
-                });
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mUxState = UX_STATE_ONLY_PROGRESS_VISIBLE;
+            }
+        });
 
         mHideProgressBarAnimator = AnimatorUtil.ofTranslationYTogether(
                 bottomBarHeight, bottomBarHeight + progressBarHeight, bottomBarGroup);
         mHideProgressBarAnimator.setDuration(HIDE_TIME_MS);
-        mHideProgressBarAnimator.getChildAnimations().get(0).addListener(
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        mUxState = UX_STATE_ANIMATING;
-                    }
+        mHideProgressBarAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mUxState = UX_STATE_ANIMATING;
+            }
 
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mUxState = UX_STATE_NONE_VISIBLE;
-                    }
-                });
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mUxState = UX_STATE_NONE_VISIBLE;
+            }
+        });
 
         mHideAllBarsAnimator = new AnimatorSet();
         mHideAllBarsAnimator.play(fadeOutAnimator)
@@ -716,7 +727,7 @@
                 .with(AnimatorUtil.ofTranslationYTogether(
                         0, bottomBarHeight + progressBarHeight, bottomBarGroup));
         mHideAllBarsAnimator.setDuration(HIDE_TIME_MS);
-        mHideAllBarsAnimator.getChildAnimations().get(0).addListener(new AnimatorListenerAdapter() {
+        mHideAllBarsAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mUxState = UX_STATE_ANIMATING;
@@ -733,18 +744,17 @@
                 .with(AnimatorUtil.ofTranslationY(-titleBarHeight, 0, mTitleBar))
                 .with(AnimatorUtil.ofTranslationYTogether(bottomBarHeight, 0, bottomBarGroup));
         mShowMainBarsAnimator.setDuration(SHOW_TIME_MS);
-        mShowMainBarsAnimator.getChildAnimations().get(0).addListener(
-                new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        mUxState = UX_STATE_ANIMATING;
-                    }
+        mShowMainBarsAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mUxState = UX_STATE_ANIMATING;
+            }
 
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        mUxState = UX_STATE_ALL_VISIBLE;
-                    }
-                });
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mUxState = UX_STATE_ALL_VISIBLE;
+            }
+        });
 
         mShowAllBarsAnimator = new AnimatorSet();
         mShowAllBarsAnimator.play(fadeInAnimator)
@@ -752,7 +762,7 @@
                 .with(AnimatorUtil.ofTranslationYTogether(
                         bottomBarHeight + progressBarHeight, 0, bottomBarGroup));
         mShowAllBarsAnimator.setDuration(SHOW_TIME_MS);
-        mShowAllBarsAnimator.getChildAnimations().get(0).addListener(new AnimatorListenerAdapter() {
+        mShowAllBarsAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 mUxState = UX_STATE_ANIMATING;
@@ -1346,12 +1356,11 @@
             case SIZE_TYPE_FULL:
             case SIZE_TYPE_EMBEDDED:
                 // Relating to Progress Bar
-                mProgress.setThumb(mResources.getDrawable(R.drawable.custom_progress_thumb));
-                mProgress.setThumbOffset(0);
+                mProgress.getThumb().setLevel(MAX_SCALE_LEVEL);
                 break;
             case SIZE_TYPE_MINIMAL:
                 // Relating to Progress Bar
-                mProgress.setThumb(null);
+                mProgress.getThumb().setLevel(0);
                 break;
         }
 
diff --git a/media2-widget/src/main/java/androidx/media2/widget/RoutePlayer.java b/media2-widget/src/main/java/androidx/media2/widget/RoutePlayer.java
index 011da20..c392f85 100644
--- a/media2-widget/src/main/java/androidx/media2/widget/RoutePlayer.java
+++ b/media2-widget/src/main/java/androidx/media2/widget/RoutePlayer.java
@@ -19,7 +19,7 @@
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_BAD_VALUE;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_INVALID_STATE;
-import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_SUCCESS;
 
 import android.content.Context;
@@ -233,7 +233,7 @@
                             });
                         }
                     } else {
-                        result.set(new PlayerResult(RESULT_ERROR_UNKNOWN_ERROR,
+                        result.set(new PlayerResult(RESULT_ERROR_UNKNOWN,
                                 getCurrentMediaItem()));
                     }
                 }
diff --git a/media2-widget/src/main/java/androidx/media2/widget/SubtitleAnchorView.java b/media2-widget/src/main/java/androidx/media2/widget/SubtitleAnchorView.java
index 1ab96ae..7c36f80 100644
--- a/media2-widget/src/main/java/androidx/media2/widget/SubtitleAnchorView.java
+++ b/media2-widget/src/main/java/androidx/media2/widget/SubtitleAnchorView.java
@@ -133,6 +133,7 @@
 
     @Override
     public CharSequence getAccessibilityClassName() {
-        return SubtitleAnchorView.class.getName();
+        // Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage.
+        return "androidx.media2.widget.SubtitleAnchorView";
     }
 }
diff --git a/media2-widget/src/main/java/androidx/media2/widget/VideoView.java b/media2-widget/src/main/java/androidx/media2/widget/VideoView.java
index 87bf21c..d219597 100644
--- a/media2-widget/src/main/java/androidx/media2/widget/VideoView.java
+++ b/media2-widget/src/main/java/androidx/media2/widget/VideoView.java
@@ -509,12 +509,15 @@
     }
 
     /**
-     * Registers a callback to be invoked when a view type change is done.
-     * {@see #setViewType(int)}
-     * @param l The callback that will be run
+     * Sets a listener to be called when a view type change is done.
+     *
+     * @see #setViewType(int)
+     *
+     * @param listener The listener to be called. A value of <code>null</code> removes any existing
+     * listener.
      */
-    public void setOnViewTypeChangedListener(@Nullable OnViewTypeChangedListener l) {
-        mViewTypeChangedListener = l;
+    public void setOnViewTypeChangedListener(@Nullable OnViewTypeChangedListener listener) {
+        mViewTypeChangedListener = listener;
     }
 
     @Override
@@ -581,7 +584,8 @@
 
     @Override
     public CharSequence getAccessibilityClassName() {
-        return VideoView.class.getName();
+        // Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage.
+        return "androidx.media2.widget.VideoView";
     }
 
     @Override
diff --git a/media2-widget/src/main/res/drawable/title_bar_gradient.xml b/media2-widget/src/main/res/drawable/title_bar_gradient.xml
index 1e3bb82..5aef9b8d 100644
--- a/media2-widget/src/main/res/drawable/title_bar_gradient.xml
+++ b/media2-widget/src/main/res/drawable/title_bar_gradient.xml
@@ -23,5 +23,5 @@
         android:layout_width="match_parent"
         android:startColor="@color/title_bar_gradient_start"
         android:endColor="@color/title_bar_gradient_end"
-        android:angle="-270" />
+        android:angle="270" />
 </shape>
\ No newline at end of file
diff --git a/media2-widget/src/main/res/layout/media_controller.xml b/media2-widget/src/main/res/layout/media_controller.xml
index 259ed4f..a61317d 100644
--- a/media2-widget/src/main/res/layout/media_controller.xml
+++ b/media2-widget/src/main/res/layout/media_controller.xml
@@ -15,6 +15,27 @@
 -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <FrameLayout
+        android:id="@+id/center_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/center_view_background"
+        android:layoutDirection="ltr">
+
+        <include
+            android:id="@+id/embedded_transport_controls"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            layout="@layout/embedded_transport_controls" />
+
+        <include
+            android:id="@+id/minimal_transport_controls"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            layout="@layout/minimal_transport_controls" />
+    </FrameLayout>
 
     <LinearLayout
         android:id="@+id/title_bar"
@@ -83,27 +104,6 @@
         </LinearLayout>
     </LinearLayout>
 
-    <FrameLayout
-        android:id="@+id/center_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layoutDirection="ltr">
-
-        <include
-            android:id="@+id/embedded_transport_controls"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            layout="@layout/embedded_transport_controls" />
-
-        <include
-            android:id="@+id/minimal_transport_controls"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            layout="@layout/minimal_transport_controls" />
-    </FrameLayout>
-
     <LinearLayout
         android:id="@+id/minimal_fullscreen_view"
         android:layout_width="match_parent"
diff --git a/media2-widget/src/main/res/values/colors.xml b/media2-widget/src/main/res/values/colors.xml
index 3aff9da..56cda35 100644
--- a/media2-widget/src/main/res/values/colors.xml
+++ b/media2-widget/src/main/res/values/colors.xml
@@ -24,7 +24,8 @@
     <color name="white">#ffffff</color>
     <color name="white_opacity_70">#B3ffffff</color>
     <color name="black_opacity_70">#B3000000</color>
-    <color name="title_bar_gradient_start">#50000000</color>
+    <color name="title_bar_gradient_start">#d0000000</color>
     <color name="title_bar_gradient_end">#00000000</color>
-    <color name="bottom_bar_background">#40202020</color>
-</resources>
\ No newline at end of file
+    <color name="center_view_background">#90000000</color>
+    <color name="bottom_bar_background">#b0000000</color>
+</resources>
diff --git a/media2/api/1.0.0-alpha05.txt b/media2/api/1.0.0-alpha05.txt
index fbf4c3f..331fa20 100644
--- a/media2/api/1.0.0-alpha05.txt
+++ b/media2/api/1.0.0-alpha05.txt
@@ -5,9 +5,12 @@
     method public androidx.media2.DataSourceCallback getDataSourceCallback();
   }
 
-  public static final class CallbackMediaItem.Builder {
+  public static final class CallbackMediaItem.Builder extends androidx.media2.MediaItem.Builder {
     ctor public CallbackMediaItem.Builder(androidx.media2.DataSourceCallback);
     method public androidx.media2.CallbackMediaItem build();
+    method public androidx.media2.CallbackMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.CallbackMediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.CallbackMediaItem.Builder setStartPosition(long);
   }
 
   public abstract class DataSourceCallback implements java.io.Closeable {
@@ -23,10 +26,13 @@
     field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
   }
 
-  public static final class FileMediaItem.Builder {
+  public static final class FileMediaItem.Builder extends androidx.media2.MediaItem.Builder {
     ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor);
     ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor, long, long);
     method public androidx.media2.FileMediaItem build();
+    method public androidx.media2.FileMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.FileMediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.FileMediaItem.Builder setStartPosition(long);
   }
 
   public final class HeartRating implements androidx.media2.Rating {
@@ -47,7 +53,7 @@
     method public int getResultCode();
     field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
     field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
-    field public static final int RESULT_ERROR_IO_ERROR = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
     field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
     field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
@@ -58,7 +64,7 @@
     field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
     field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
     field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
     field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
@@ -170,7 +176,10 @@
 
   public static class MediaItem.Builder {
     ctor public MediaItem.Builder();
-    method public androidx.media2.MediaItem! build();
+    method public androidx.media2.MediaItem build();
+    method public androidx.media2.MediaItem.Builder setEndPosition(long);
+    method public androidx.media2.MediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.MediaItem.Builder setStartPosition(long);
   }
 
   public abstract class MediaLibraryService extends androidx.media2.MediaSessionService {
@@ -570,15 +579,15 @@
     method public abstract int getBufferingState();
     method protected final java.util.List<androidx.core.util.Pair<androidx.media2.SessionPlayer.PlayerCallback,java.util.concurrent.Executor>> getCallbacks();
     method public abstract androidx.media2.MediaItem? getCurrentMediaItem();
-    method public abstract int getCurrentMediaItemIndex();
+    method @IntRange(from=androidx.media2.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getCurrentMediaItemIndex();
     method public abstract long getCurrentPosition();
     method public abstract long getDuration();
-    method public abstract int getNextMediaItemIndex();
+    method @IntRange(from=androidx.media2.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getNextMediaItemIndex();
     method public abstract float getPlaybackSpeed();
     method public abstract int getPlayerState();
     method public abstract java.util.List<androidx.media2.MediaItem>? getPlaylist();
     method public abstract androidx.media2.MediaMetadata? getPlaylistMetadata();
-    method public abstract int getPreviousMediaItemIndex();
+    method @IntRange(from=androidx.media2.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getPreviousMediaItemIndex();
     method public abstract int getRepeatMode();
     method public abstract int getShuffleMode();
     method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.SessionPlayer.PlayerResult> pause();
@@ -640,10 +649,10 @@
     method public int getResultCode();
     field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
     field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
-    field public static final int RESULT_ERROR_IO_ERROR = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
     field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
     field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
@@ -656,7 +665,7 @@
     method public int getResultCode();
     field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
     field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
-    field public static final int RESULT_ERROR_IO_ERROR = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
     field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
     field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
@@ -667,7 +676,7 @@
     field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
     field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
     field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
     field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
@@ -720,10 +729,13 @@
     method public java.util.Map<java.lang.String,java.lang.String>? getUriHeaders();
   }
 
-  public static final class UriMediaItem.Builder {
+  public static final class UriMediaItem.Builder extends androidx.media2.MediaItem.Builder {
     ctor public UriMediaItem.Builder(android.content.Context, android.net.Uri);
     ctor public UriMediaItem.Builder(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>?, java.util.List<java.net.HttpCookie>?);
     method public androidx.media2.UriMediaItem build();
+    method public androidx.media2.UriMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.UriMediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.UriMediaItem.Builder setStartPosition(long);
   }
 
   public final class VideoSize {
diff --git a/media2/api/current.txt b/media2/api/current.txt
index fbf4c3f..331fa20 100644
--- a/media2/api/current.txt
+++ b/media2/api/current.txt
@@ -5,9 +5,12 @@
     method public androidx.media2.DataSourceCallback getDataSourceCallback();
   }
 
-  public static final class CallbackMediaItem.Builder {
+  public static final class CallbackMediaItem.Builder extends androidx.media2.MediaItem.Builder {
     ctor public CallbackMediaItem.Builder(androidx.media2.DataSourceCallback);
     method public androidx.media2.CallbackMediaItem build();
+    method public androidx.media2.CallbackMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.CallbackMediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.CallbackMediaItem.Builder setStartPosition(long);
   }
 
   public abstract class DataSourceCallback implements java.io.Closeable {
@@ -23,10 +26,13 @@
     field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL
   }
 
-  public static final class FileMediaItem.Builder {
+  public static final class FileMediaItem.Builder extends androidx.media2.MediaItem.Builder {
     ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor);
     ctor public FileMediaItem.Builder(android.os.ParcelFileDescriptor, long, long);
     method public androidx.media2.FileMediaItem build();
+    method public androidx.media2.FileMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.FileMediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.FileMediaItem.Builder setStartPosition(long);
   }
 
   public final class HeartRating implements androidx.media2.Rating {
@@ -47,7 +53,7 @@
     method public int getResultCode();
     field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
     field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
-    field public static final int RESULT_ERROR_IO_ERROR = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
     field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
     field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
@@ -58,7 +64,7 @@
     field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
     field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
     field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
     field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
@@ -170,7 +176,10 @@
 
   public static class MediaItem.Builder {
     ctor public MediaItem.Builder();
-    method public androidx.media2.MediaItem! build();
+    method public androidx.media2.MediaItem build();
+    method public androidx.media2.MediaItem.Builder setEndPosition(long);
+    method public androidx.media2.MediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.MediaItem.Builder setStartPosition(long);
   }
 
   public abstract class MediaLibraryService extends androidx.media2.MediaSessionService {
@@ -570,15 +579,15 @@
     method public abstract int getBufferingState();
     method protected final java.util.List<androidx.core.util.Pair<androidx.media2.SessionPlayer.PlayerCallback,java.util.concurrent.Executor>> getCallbacks();
     method public abstract androidx.media2.MediaItem? getCurrentMediaItem();
-    method public abstract int getCurrentMediaItemIndex();
+    method @IntRange(from=androidx.media2.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getCurrentMediaItemIndex();
     method public abstract long getCurrentPosition();
     method public abstract long getDuration();
-    method public abstract int getNextMediaItemIndex();
+    method @IntRange(from=androidx.media2.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getNextMediaItemIndex();
     method public abstract float getPlaybackSpeed();
     method public abstract int getPlayerState();
     method public abstract java.util.List<androidx.media2.MediaItem>? getPlaylist();
     method public abstract androidx.media2.MediaMetadata? getPlaylistMetadata();
-    method public abstract int getPreviousMediaItemIndex();
+    method @IntRange(from=androidx.media2.SessionPlayer.INVALID_ITEM_INDEX) public abstract int getPreviousMediaItemIndex();
     method public abstract int getRepeatMode();
     method public abstract int getShuffleMode();
     method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.media2.SessionPlayer.PlayerResult> pause();
@@ -640,10 +649,10 @@
     method public int getResultCode();
     field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
     field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
-    field public static final int RESULT_ERROR_IO_ERROR = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
     field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
     field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
@@ -656,7 +665,7 @@
     method public int getResultCode();
     field public static final int RESULT_ERROR_BAD_VALUE = -3; // 0xfffffffd
     field public static final int RESULT_ERROR_INVALID_STATE = -2; // 0xfffffffe
-    field public static final int RESULT_ERROR_IO_ERROR = -5; // 0xfffffffb
+    field public static final int RESULT_ERROR_IO = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_NOT_SUPPORTED = -6; // 0xfffffffa
     field public static final int RESULT_ERROR_PERMISSION_DENIED = -4; // 0xfffffffc
     field public static final int RESULT_ERROR_SESSION_AUTHENTICATION_EXPIRED = -102; // 0xffffff9a
@@ -667,7 +676,7 @@
     field public static final int RESULT_ERROR_SESSION_PREMIUM_ACCOUNT_REQUIRED = -103; // 0xffffff99
     field public static final int RESULT_ERROR_SESSION_SETUP_REQUIRED = -108; // 0xffffff94
     field public static final int RESULT_ERROR_SESSION_SKIP_LIMIT_REACHED = -107; // 0xffffff95
-    field public static final int RESULT_ERROR_UNKNOWN_ERROR = -1; // 0xffffffff
+    field public static final int RESULT_ERROR_UNKNOWN = -1; // 0xffffffff
     field public static final int RESULT_INFO_SKIPPED = 1; // 0x1
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
@@ -720,10 +729,13 @@
     method public java.util.Map<java.lang.String,java.lang.String>? getUriHeaders();
   }
 
-  public static final class UriMediaItem.Builder {
+  public static final class UriMediaItem.Builder extends androidx.media2.MediaItem.Builder {
     ctor public UriMediaItem.Builder(android.content.Context, android.net.Uri);
     ctor public UriMediaItem.Builder(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>?, java.util.List<java.net.HttpCookie>?);
     method public androidx.media2.UriMediaItem build();
+    method public androidx.media2.UriMediaItem.Builder setEndPosition(long);
+    method public androidx.media2.UriMediaItem.Builder setMetadata(androidx.media2.MediaMetadata?);
+    method public androidx.media2.UriMediaItem.Builder setStartPosition(long);
   }
 
   public final class VideoSize {
diff --git a/media2/src/androidTest/java/androidx/media2/MediaPlayer2StateTest.java b/media2/src/androidTest/java/androidx/media2/MediaPlayer2StateTest.java
index e35c92c..d18f681 100644
--- a/media2/src/androidTest/java/androidx/media2/MediaPlayer2StateTest.java
+++ b/media2/src/androidTest/java/androidx/media2/MediaPlayer2StateTest.java
@@ -489,7 +489,10 @@
     private static final PlayerOperation sGetMetricsOperation = new PlayerOperation() {
         @Override
         public void doOperation(MediaPlayer2 player) {
-            player.getMetrics();
+            // Validate media metrics from API 21 where PersistableBundle was added.
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                player.getMetrics();
+            }
         }
 
         @Override
diff --git a/media2/src/main/java/androidx/media2/BaseResult.java b/media2/src/main/java/androidx/media2/BaseResult.java
index d203f32..58c37b1 100644
--- a/media2/src/main/java/androidx/media2/BaseResult.java
+++ b/media2/src/main/java/androidx/media2/BaseResult.java
@@ -40,7 +40,7 @@
     /**
      * Result code represents that call is ended with an unknown error.
      */
-    int RESULT_ERROR_UNKNOWN_ERROR = -1;
+    int RESULT_ERROR_UNKNOWN = -1;
 
     /**
      * Result code representing that the command cannot be completed because the current state is
@@ -61,7 +61,7 @@
     /**
      * Result code representing a file or network related command error.
      */
-    int RESULT_ERROR_IO_ERROR = -5;
+    int RESULT_ERROR_IO = -5;
 
     /**
      * Result code representing that the command is not supported nor implemented.
diff --git a/media2/src/main/java/androidx/media2/CallbackMediaItem.java b/media2/src/main/java/androidx/media2/CallbackMediaItem.java
index 5ccbc97..0c9acdb 100644
--- a/media2/src/main/java/androidx/media2/CallbackMediaItem.java
+++ b/media2/src/main/java/androidx/media2/CallbackMediaItem.java
@@ -17,6 +17,7 @@
 package androidx.media2;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.core.util.Preconditions;
 import androidx.versionedparcelable.NonParcelField;
 import androidx.versionedparcelable.ParcelUtils;
@@ -36,7 +37,7 @@
 public class CallbackMediaItem extends MediaItem {
     @NonParcelField
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-            DataSourceCallback mDataSourceCallback;
+    DataSourceCallback mDataSourceCallback;
 
     /**
      * Used for VersionedParcelable
@@ -64,7 +65,7 @@
     /**
      * This Builder class simplifies the creation of a {@link CallbackMediaItem} object.
      */
-    public static final class Builder extends BuilderBase<Builder> {
+    public static final class Builder extends MediaItem.Builder {
 
         @SuppressWarnings("WeakerAccess") /* synthetic access */
                 DataSourceCallback mDataSourceCallback;
@@ -78,11 +79,33 @@
             mDataSourceCallback = dsc2;
         }
 
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setMetadata(@Nullable MediaMetadata metadata) {
+            return (Builder) super.setMetadata(metadata);
+        }
+
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setStartPosition(long position) {
+            return (Builder) super.setStartPosition(position);
+        }
+
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setEndPosition(long position) {
+            return (Builder) super.setEndPosition(position);
+        }
+
         /**
          * @return A new CallbackMediaItem with values supplied by the Builder.
          */
+        @NonNull
         @Override
-        public @NonNull CallbackMediaItem build() {
+        public CallbackMediaItem build() {
             return new CallbackMediaItem(this);
         }
     }
diff --git a/media2/src/main/java/androidx/media2/FileMediaItem.java b/media2/src/main/java/androidx/media2/FileMediaItem.java
index 9793cef..fcb36ab 100644
--- a/media2/src/main/java/androidx/media2/FileMediaItem.java
+++ b/media2/src/main/java/androidx/media2/FileMediaItem.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.util.Preconditions;
@@ -171,7 +172,7 @@
     /**
      * This Builder class simplifies the creation of a {@link FileMediaItem} object.
      */
-    public static final class Builder extends BuilderBase<Builder> {
+    public static final class Builder extends MediaItem.Builder {
 
         @SuppressWarnings("WeakerAccess") /* synthetic access */
         ParcelFileDescriptor mPFD;
@@ -223,11 +224,33 @@
             mFDLength = length;
         }
 
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setMetadata(@Nullable MediaMetadata metadata) {
+            return (Builder) super.setMetadata(metadata);
+        }
+
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setStartPosition(long position) {
+            return (Builder) super.setStartPosition(position);
+        }
+
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setEndPosition(long position) {
+            return (Builder) super.setEndPosition(position);
+        }
+
         /**
          * @return A new FileMediaItem with values supplied by the Builder.
          */
+        @NonNull
         @Override
-        public @NonNull FileMediaItem build() {
+        public FileMediaItem build() {
             return new FileMediaItem(this);
         }
     }
diff --git a/media2/src/main/java/androidx/media2/LibraryResult.java b/media2/src/main/java/androidx/media2/LibraryResult.java
index b03891f..ebf745f 100644
--- a/media2/src/main/java/androidx/media2/LibraryResult.java
+++ b/media2/src/main/java/androidx/media2/LibraryResult.java
@@ -49,11 +49,11 @@
      */
     @IntDef(flag = false, /*prefix = "RESULT_CODE",*/ value = {
             RESULT_SUCCESS,
-            RESULT_ERROR_UNKNOWN_ERROR,
+            RESULT_ERROR_UNKNOWN,
             RESULT_ERROR_INVALID_STATE,
             RESULT_ERROR_BAD_VALUE,
             RESULT_ERROR_PERMISSION_DENIED,
-            RESULT_ERROR_IO_ERROR,
+            RESULT_ERROR_IO,
             RESULT_INFO_SKIPPED,
             RESULT_ERROR_SESSION_DISCONNECTED,
             RESULT_ERROR_NOT_SUPPORTED,
@@ -142,11 +142,11 @@
      *
      * @return result code
      * @see #RESULT_SUCCESS
-     * @see #RESULT_ERROR_UNKNOWN_ERROR
+     * @see #RESULT_ERROR_UNKNOWN
      * @see #RESULT_ERROR_INVALID_STATE
      * @see #RESULT_ERROR_BAD_VALUE
      * @see #RESULT_ERROR_PERMISSION_DENIED
-     * @see #RESULT_ERROR_IO_ERROR
+     * @see #RESULT_ERROR_IO
      * @see #RESULT_INFO_SKIPPED
      * @see #RESULT_ERROR_SESSION_DISCONNECTED
      * @see #RESULT_ERROR_NOT_SUPPORTED
diff --git a/media2/src/main/java/androidx/media2/MediaBrowser.java b/media2/src/main/java/androidx/media2/MediaBrowser.java
index 51832aa..8360a0a 100644
--- a/media2/src/main/java/androidx/media2/MediaBrowser.java
+++ b/media2/src/main/java/androidx/media2/MediaBrowser.java
@@ -223,7 +223,7 @@
      * item.
      *
      * @param mediaId non-empty media id for specifying the item
-     * @see LibraryResult#getMediaItems()
+     * @see LibraryResult#getMediaItem()
      */
     public @NonNull ListenableFuture<LibraryResult> getItem(@NonNull final String mediaId) {
         if (TextUtils.isEmpty(mediaId)) {
diff --git a/media2/src/main/java/androidx/media2/MediaBrowserImplLegacy.java b/media2/src/main/java/androidx/media2/MediaBrowserImplLegacy.java
index ef52760..fb14397 100644
--- a/media2/src/main/java/androidx/media2/MediaBrowserImplLegacy.java
+++ b/media2/src/main/java/androidx/media2/MediaBrowserImplLegacy.java
@@ -18,7 +18,7 @@
 
 import static androidx.media2.LibraryResult.RESULT_ERROR_BAD_VALUE;
 import static androidx.media2.LibraryResult.RESULT_ERROR_SESSION_DISCONNECTED;
-import static androidx.media2.LibraryResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.LibraryResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.LibraryResult.RESULT_SUCCESS;
 import static androidx.media2.MediaMetadata.BROWSABLE_TYPE_MIXED;
 import static androidx.media2.MediaMetadata.METADATA_KEY_BROWSABLE;
@@ -200,7 +200,7 @@
                 getCallbackExecutor().execute(new Runnable() {
                     @Override
                     public void run() {
-                        result.set(new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR));
+                        result.set(new LibraryResult(RESULT_ERROR_UNKNOWN));
                     }
                 });
             }
@@ -283,7 +283,7 @@
                 getCallbackExecutor().execute(new Runnable() {
                     @Override
                     public void run() {
-                        future.set(new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR));
+                        future.set(new LibraryResult(RESULT_ERROR_UNKNOWN));
                     }
                 });
             }
@@ -341,7 +341,7 @@
             }
             if (browserCompat == null) {
                 // Shouldn't be happen. Internal error?
-                mResult.set(new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR));
+                mResult.set(new LibraryResult(RESULT_ERROR_UNKNOWN));
             } else {
                 mResult.set(new LibraryResult(RESULT_SUCCESS,
                         createRootMediaItem(browserCompat),
@@ -426,12 +426,12 @@
 
         @Override
         public void onError(String parentId) {
-            mFuture.set(new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR));
+            mFuture.set(new LibraryResult(RESULT_ERROR_UNKNOWN));
         }
 
         @Override
         public void onError(String parentId, Bundle options) {
-            mFuture.set(new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR));
+            mFuture.set(new LibraryResult(RESULT_ERROR_UNKNOWN));
         }
 
         @Override
@@ -456,7 +456,7 @@
             final List<MediaItem> items = new ArrayList<>();
             if (children == null) {
                 // list are non-Null, so it must be internal error.
-                mFuture.set(new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR));
+                mFuture.set(new LibraryResult(RESULT_ERROR_UNKNOWN));
             } else {
                 for (int i = 0; i < children.size(); i++) {
                     items.add(MediaUtils.convertToMediaItem(children.get(i)));
diff --git a/media2/src/main/java/androidx/media2/MediaController.java b/media2/src/main/java/androidx/media2/MediaController.java
index 839bd4f..a1c4039 100644
--- a/media2/src/main/java/androidx/media2/MediaController.java
+++ b/media2/src/main/java/androidx/media2/MediaController.java
@@ -749,15 +749,16 @@
     }
 
     /**
-     * Returns the cached playlist from {@link ControllerCallback#onPlaylistChanged}.
+     * Returns the cached playlist from {@link ControllerCallback#onPlaylistChanged}. Can be
+     * {@code null} if the playlist hasn't been set or it's reset by {@link #setMediaItem}.
      * <p>
      * This list may differ with the list that was specified with
      * {@link #setPlaylist(List, MediaMetadata)} depending on the {@link SessionPlayer}
      * implementation. Use media items returned here for other playlist agent APIs such as
-     * {@link SessionPlayer#skipToPlaylistItem(MediaItem)}.
+     * {@link SessionPlayer#skipToPlaylistItem}.
      *
-     * @return playlist, or {@code null} if the playlist hasn't set, controller isn't connected,
-     *         or it doesn't have enough permission
+     * @return playlist, or {@code null} if the playlist hasn't been set, controller isn't
+     *         connected, or it doesn't have enough permission.
      * @see SessionCommand#COMMAND_CODE_PLAYER_GET_PLAYLIST
      */
     @Nullable
@@ -766,13 +767,22 @@
     }
 
     /**
-     * Sets the playlist with the list of media IDs. All media IDs in the list shouldn't be empty.
+     * Sets the playlist with the list of media IDs. Use this or {@link #setMediaItem} to specify
+     * which items to play.
+     * <p>
+     * All media IDs in the list shouldn't be an empty string.
+     * <p>
+     * The {@link ControllerCallback#onPlaylistChanged} and
+     * {@link ControllerCallback#onCurrentMediaItemChanged} would be called when it's completed.
+     * The current item would be the first item in the playlist.
      *
      * @param list list of media id. Shouldn't contain an empty id.
      * @param metadata metadata of the playlist
-     * @see #getPlaylist()
+     * @see #setMediaItem
+     * @see ControllerCallback#onCurrentMediaItemChanged
      * @see ControllerCallback#onPlaylistChanged
      * @see MediaMetadata#METADATA_KEY_MEDIA_ID
+     * @throws IllegalArgumentException if the list is {@code null} or contains any empty string.
      */
     @NonNull
     public ListenableFuture<SessionResult> setPlaylist(@NonNull List<String> list,
@@ -792,9 +802,19 @@
     }
 
     /**
-     * Sets a {@link MediaItem} for playback.
+     * Sets a {@link MediaItem} for playback with the media ID. Use this or {@link #setPlaylist}
+     * to specify which items to play. If you want to change current item in the playlist, use one
+     * of {@link #skipToPlaylistItem}, {@link #skipToNextPlaylistItem}, or
+     * {@link #skipToPreviousPlaylistItem} instead of this method.
+     * <p>
+     * The {@link ControllerCallback#onPlaylistChanged} and
+     * {@link ControllerCallback#onCurrentMediaItemChanged} would be called when it's completed.
+     * The current item would be the item given here.
      *
      * @param mediaId The non-empty media id of the item to play
+     * @see #setPlaylist
+     * @see ControllerCallback#onCurrentMediaItemChanged
+     * @see ControllerCallback#onPlaylistChanged
      * @see MediaMetadata#METADATA_KEY_MEDIA_ID
      */
     @NonNull
@@ -909,6 +929,8 @@
      * {@link ControllerCallback#onCurrentMediaItemChanged(MediaController, MediaItem)}.
      *
      * @return the currently playing item, or null if unknown or not connected
+     * @see #setMediaItem
+     * @see #setPlaylist
      */
     @Nullable
     public MediaItem getCurrentMediaItem() {
@@ -1235,7 +1257,7 @@
         /**
          * Called when the session sent a custom command. Returns a {@link SessionResult} for
          * session to get notification back. If the {@code null} is returned,
-         * {@link SessionResult#RESULT_ERROR_UNKNOWN_ERROR} will be returned.
+         * {@link SessionResult#RESULT_ERROR_UNKNOWN} will be returned.
          * <p>
          * Default implementation returns {@link SessionResult#RESULT_ERROR_NOT_SUPPORTED}.
          *
@@ -1289,25 +1311,31 @@
         public void onSeekCompleted(@NonNull MediaController controller, long position) {}
 
         /**
-         * Called when the player's currently playing item is changed
+         * Called when the player's current item is changed. It's also called after
+         * {@link #setPlaylist} or {@link #setMediaItem}.
          * <p>
          * When it's called, you should invalidate previous playback information and wait for later
          * callbacks. Also, current, previous, and next media item indices may need to be updated.
          *
          * @param controller the controller for this event
-         * @param item new item
+         * @param item new current media item
+         * @see #getPlaylist()
+         * @see #getPlaylistMetadata()
          */
         public void onCurrentMediaItemChanged(@NonNull MediaController controller,
                 @Nullable MediaItem item) {}
 
         /**
-         * Called when a playlist is changed.
+         * Called when a playlist is changed. It's also called after {@link #setPlaylist} or
+         * {@link #setMediaItem}.
          * <p>
          * When it's called, current, previous, and next media item indices may need to be updated.
          *
          * @param controller the controller for this event
          * @param list new playlist
          * @param metadata new metadata
+         * @see #getPlaylist()
+         * @see #getPlaylistMetadata()
          */
         public void onPlaylistChanged(@NonNull MediaController controller,
                 @Nullable List<MediaItem> list, @Nullable MediaMetadata metadata) {}
diff --git a/media2/src/main/java/androidx/media2/MediaControllerImplBase.java b/media2/src/main/java/androidx/media2/MediaControllerImplBase.java
index 0b90962..a552820 100644
--- a/media2/src/main/java/androidx/media2/MediaControllerImplBase.java
+++ b/media2/src/main/java/androidx/media2/MediaControllerImplBase.java
@@ -51,7 +51,7 @@
 import static androidx.media2.SessionPlayer.UNKNOWN_TIME;
 import static androidx.media2.SessionResult.RESULT_ERROR_PERMISSION_DENIED;
 import static androidx.media2.SessionResult.RESULT_ERROR_SESSION_DISCONNECTED;
-import static androidx.media2.SessionResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.SessionResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.SessionResult.RESULT_INFO_SKIPPED;
 import static androidx.media2.SessionToken.TYPE_SESSION;
 
@@ -1174,7 +1174,7 @@
                         throw new RuntimeException("ControllerCallback#onCustomCommand() has"
                                 + " returned null, command=" + command.getCustomCommand());
                     } else {
-                        result = new SessionResult(RESULT_ERROR_UNKNOWN_ERROR);
+                        result = new SessionResult(RESULT_ERROR_UNKNOWN);
                     }
                 }
                 sendControllerResult(seq, result);
diff --git a/media2/src/main/java/androidx/media2/MediaItem.java b/media2/src/main/java/androidx/media2/MediaItem.java
index 4bb748f..c2745b0 100644
--- a/media2/src/main/java/androidx/media2/MediaItem.java
+++ b/media2/src/main/java/androidx/media2/MediaItem.java
@@ -18,7 +18,6 @@
 
 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.annotation.SuppressLint;
 import android.text.TextUtils;
@@ -104,7 +103,7 @@
     // Note: Needs to be protected when we want to allow 3rd party player to define customized
     //       MediaItem.
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    MediaItem(BuilderBase builder) {
+    MediaItem(Builder builder) {
         this(builder.mMetadata, builder.mStartPositionMs, builder.mEndPositionMs);
     }
 
@@ -241,13 +240,9 @@
     }
 
     /**
-     * Base builder for {@link MediaItem} and its subclass.
-     *
-     * @param <T> builder class
-     * @hide
+     * Builder for {@link MediaItem}.
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
-    public static class BuilderBase<T extends BuilderBase> {
+    public static class Builder {
         @SuppressWarnings("WeakerAccess") /* synthetic access */
                 MediaMetadata mMetadata;
         @SuppressWarnings("WeakerAccess") /* synthetic access */
@@ -256,14 +251,21 @@
         long mEndPositionMs = POSITION_UNKNOWN;
 
         /**
+         * Default constructor
+         */
+        public Builder() {
+        }
+
+        /**
          * Set the metadata of this instance. {@code null} for unset.
          *
          * @param metadata metadata
          * @return this instance for chaining
          */
-        public @NonNull T setMetadata(@Nullable MediaMetadata metadata) {
+        @NonNull
+        public Builder setMetadata(@Nullable MediaMetadata metadata) {
             mMetadata = metadata;
-            return (T) this;
+            return this;
         }
 
         /**
@@ -271,14 +273,15 @@
          * Any negative number is treated as 0.
          *
          * @param position the start position in milliseconds at which the playback will start
-         * @return the same Builder instance.
+         * @return this instance for chaining
          */
-        public @NonNull T setStartPosition(long position) {
+        @NonNull
+        public Builder setStartPosition(long position) {
             if (position < 0) {
                 position = 0;
             }
             mStartPositionMs = position;
-            return (T) this;
+            return this;
         }
 
         /**
@@ -286,14 +289,15 @@
          * Any negative number is treated as maximum length of the media item.
          *
          * @param position the end position in milliseconds at which the playback will end
-         * @return the same Builder instance.
+         * @return this instance for chaining
          */
-        public @NonNull T setEndPosition(long position) {
+        @NonNull
+        public Builder setEndPosition(long position) {
             if (position < 0) {
                 position = POSITION_UNKNOWN;
             }
             mEndPositionMs = position;
-            return (T) this;
+            return this;
         }
 
         /**
@@ -301,23 +305,12 @@
          *
          * @return a new {@link MediaItem}.
          */
-        public @NonNull MediaItem build() {
+        @NonNull
+        public MediaItem build() {
             return new MediaItem(this);
         }
     }
 
-    /**
-     * Builder for {@link MediaItem}.
-     */
-    public static class Builder extends BuilderBase<Builder> {
-        /**
-         * Default constructor
-         */
-        public Builder() {
-            super();
-        }
-    }
-
     interface OnMetadataChangedListener {
         void onMetadataChanged(MediaItem item);
     }
@@ -331,8 +324,7 @@
     @SuppressLint("RestrictedApi")
     public void onPreParceling(boolean isStream) {
         if (getClass() != MediaItem.class) {
-            throw new RuntimeException("MediaItem's subclasses shouldn't be parcelized."
-                    + " Use instead");
+            throw new RuntimeException("MediaItem's subclasses shouldn't be parcelized.");
         }
         super.onPreParceling(isStream);
     }
diff --git a/media2/src/main/java/androidx/media2/MediaLibrarySessionImplBase.java b/media2/src/main/java/androidx/media2/MediaLibrarySessionImplBase.java
index 176ff99..ba62ce5 100644
--- a/media2/src/main/java/androidx/media2/MediaLibrarySessionImplBase.java
+++ b/media2/src/main/java/androidx/media2/MediaLibrarySessionImplBase.java
@@ -16,7 +16,7 @@
 
 package androidx.media2;
 
-import static androidx.media2.LibraryResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.LibraryResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.LibraryResult.RESULT_SUCCESS;
 
 import android.annotation.SuppressLint;
@@ -162,7 +162,7 @@
             }
             for (MediaItem item : items) {
                 if (!isValidItem(item)) {
-                    return new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR);
+                    return new LibraryResult(RESULT_ERROR_UNKNOWN);
                 }
             }
         }
@@ -173,7 +173,7 @@
         returnedResult = ensureNonNullResult(returnedResult);
         if (returnedResult.getResultCode() == RESULT_SUCCESS) {
             if (!isValidItem(returnedResult.getMediaItem())) {
-                return new LibraryResult(RESULT_ERROR_UNKNOWN_ERROR);
+                return new LibraryResult(RESULT_ERROR_UNKNOWN);
             }
         }
         return returnedResult;
diff --git a/media2/src/main/java/androidx/media2/MediaPlayer.java b/media2/src/main/java/androidx/media2/MediaPlayer.java
index 861be42..5b2c128 100644
--- a/media2/src/main/java/androidx/media2/MediaPlayer.java
+++ b/media2/src/main/java/androidx/media2/MediaPlayer.java
@@ -19,9 +19,9 @@
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_BAD_VALUE;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_INVALID_STATE;
-import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_IO_ERROR;
+import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_IO;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_PERMISSION_DENIED;
-import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.SessionPlayer.PlayerResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_INFO_SKIPPED;
 import static androidx.media2.SessionPlayer.PlayerResult.RESULT_SUCCESS;
 
@@ -443,13 +443,13 @@
     static {
         sResultCodeMap = new ArrayMap<>();
         sResultCodeMap.put(MediaPlayer2.CALL_STATUS_NO_ERROR, RESULT_SUCCESS);
-        sResultCodeMap.put(MediaPlayer2.CALL_STATUS_ERROR_UNKNOWN, RESULT_ERROR_UNKNOWN_ERROR);
+        sResultCodeMap.put(MediaPlayer2.CALL_STATUS_ERROR_UNKNOWN, RESULT_ERROR_UNKNOWN);
         sResultCodeMap.put(
                 MediaPlayer2.CALL_STATUS_INVALID_OPERATION, RESULT_ERROR_INVALID_STATE);
         sResultCodeMap.put(MediaPlayer2.CALL_STATUS_BAD_VALUE, RESULT_ERROR_BAD_VALUE);
         sResultCodeMap.put(
                 MediaPlayer2.CALL_STATUS_PERMISSION_DENIED, RESULT_ERROR_PERMISSION_DENIED);
-        sResultCodeMap.put(MediaPlayer2.CALL_STATUS_ERROR_IO, RESULT_ERROR_IO_ERROR);
+        sResultCodeMap.put(MediaPlayer2.CALL_STATUS_ERROR_IO, RESULT_ERROR_IO);
         sResultCodeMap.put(MediaPlayer2.CALL_STATUS_SKIPPED, RESULT_INFO_SKIPPED);
 
         sErrorCodeMap = new ArrayMap<>();
@@ -711,7 +711,7 @@
                         addPendingCommandLocked(MediaPlayer2.CALL_COMPLETED_PLAY, future, token);
                     }
                 } else {
-                    future = createFutureForResultCode(RESULT_ERROR_UNKNOWN_ERROR);
+                    future = createFutureForResultCode(RESULT_ERROR_UNKNOWN);
                 }
                 futures.add(future);
                 return futures;
@@ -2515,7 +2515,7 @@
         }
         if (what != MediaPlayer2.CALL_COMPLETED_PREPARE_DRM) {
             Integer resultCode = sResultCodeMap.containsKey(status)
-                    ? sResultCodeMap.get(status) : RESULT_ERROR_UNKNOWN_ERROR;
+                    ? sResultCodeMap.get(status) : RESULT_ERROR_UNKNOWN;
             expected.mFuture.set(new PlayerResult(resultCode, item));
         } else {
             Integer resultCode = sPrepareDrmStatusMap.containsKey(status)
diff --git a/media2/src/main/java/androidx/media2/MediaSessionImplBase.java b/media2/src/main/java/androidx/media2/MediaSessionImplBase.java
index 4eea71d..5d4eea5 100644
--- a/media2/src/main/java/androidx/media2/MediaSessionImplBase.java
+++ b/media2/src/main/java/androidx/media2/MediaSessionImplBase.java
@@ -28,7 +28,7 @@
 import static androidx.media2.SessionPlayer.UNKNOWN_TIME;
 import static androidx.media2.SessionResult.RESULT_ERROR_INVALID_STATE;
 import static androidx.media2.SessionResult.RESULT_ERROR_SESSION_DISCONNECTED;
-import static androidx.media2.SessionResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.SessionResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.SessionResult.RESULT_INFO_SKIPPED;
 import static androidx.media2.SessionResult.RESULT_SUCCESS;
 import static androidx.media2.SessionToken.TYPE_SESSION;
@@ -1098,7 +1098,7 @@
             //   - DeadSystemException means that errors around it can be ignored.
             Log.w(TAG, "Exception in " + controller.toString(), e);
         }
-        return SessionResult.createFutureWithResult(RESULT_ERROR_UNKNOWN_ERROR);
+        return SessionResult.createFutureWithResult(RESULT_ERROR_UNKNOWN);
     }
 
     /**
diff --git a/media2/src/main/java/androidx/media2/MediaSessionService.java b/media2/src/main/java/androidx/media2/MediaSessionService.java
index 6a913f6..92e8f22 100644
--- a/media2/src/main/java/androidx/media2/MediaSessionService.java
+++ b/media2/src/main/java/androidx/media2/MediaSessionService.java
@@ -105,6 +105,7 @@
  * connecting any session in this service. In that case, {@link #onGetPrimarySession()} will be
  * called to know which session to handle incoming connection request. Pick the best session among
  * added sessions, or create new one and return from the {@link #onGetPrimarySession()}.
+ * </div>
  */
 public abstract class MediaSessionService extends Service {
     /**
diff --git a/media2/src/main/java/androidx/media2/MediaSessionStub.java b/media2/src/main/java/androidx/media2/MediaSessionStub.java
index 6e02ae5..1de652f 100644
--- a/media2/src/main/java/androidx/media2/MediaSessionStub.java
+++ b/media2/src/main/java/androidx/media2/MediaSessionStub.java
@@ -16,7 +16,7 @@
 
 package androidx.media2;
 
-import static androidx.media2.BaseResult.RESULT_ERROR_UNKNOWN_ERROR;
+import static androidx.media2.BaseResult.RESULT_ERROR_UNKNOWN;
 import static androidx.media2.MediaUtils.DIRECT_EXECUTOR;
 import static androidx.media2.SessionCommand.COMMAND_CODE_CUSTOM;
 import static androidx.media2.SessionCommand.COMMAND_VERSION_CURRENT;
@@ -288,13 +288,13 @@
                         if (task instanceof PlayerTask) {
                             sendPlayerResult(controller, seq,
                                     new PlayerResult(
-                                            PlayerResult.RESULT_ERROR_UNKNOWN_ERROR, null));
+                                            PlayerResult.RESULT_ERROR_UNKNOWN, null));
                         } else if (task instanceof SessionCallbackTask) {
                             sendSessionResult(controller, seq,
-                                    SessionResult.RESULT_ERROR_UNKNOWN_ERROR);
+                                    SessionResult.RESULT_ERROR_UNKNOWN);
                         } else if (task instanceof LibrarySessionCallbackTask) {
                             sendLibraryResult(controller, seq,
-                                    LibraryResult.RESULT_ERROR_UNKNOWN_ERROR);
+                                    LibraryResult.RESULT_ERROR_UNKNOWN);
                         }
                     }
                 }
@@ -644,7 +644,7 @@
                         throw new RuntimeException("SessionCallback#onCustomCommand has returned"
                                 + " null, command=" + sessionCommand);
                     } else {
-                        result = new SessionResult(RESULT_ERROR_UNKNOWN_ERROR);
+                        result = new SessionResult(RESULT_ERROR_UNKNOWN);
                     }
                 }
                 return result;
@@ -1230,7 +1230,7 @@
         @Override
         void onSessionResult(int seq, @Nullable SessionResult result) throws RemoteException {
             if (result == null) {
-                result = new SessionResult(RESULT_ERROR_UNKNOWN_ERROR, null);
+                result = new SessionResult(RESULT_ERROR_UNKNOWN, null);
             }
             mIControllerCallback.onSessionResult(seq, MediaUtils.toParcelable(result));
         }
@@ -1238,7 +1238,7 @@
         @Override
         void onLibraryResult(int seq, LibraryResult result) throws RemoteException {
             if (result == null) {
-                result = new LibraryResult(LibraryResult.RESULT_ERROR_UNKNOWN_ERROR);
+                result = new LibraryResult(LibraryResult.RESULT_ERROR_UNKNOWN);
             }
             mIControllerCallback.onLibraryResult(seq, MediaUtils.toParcelable(result));
         }
diff --git a/media2/src/main/java/androidx/media2/PlaybackParams.java b/media2/src/main/java/androidx/media2/PlaybackParams.java
index 0749492..2b93cd6 100644
--- a/media2/src/main/java/androidx/media2/PlaybackParams.java
+++ b/media2/src/main/java/androidx/media2/PlaybackParams.java
@@ -168,6 +168,9 @@
         private Float mSpeed;
         private android.media.PlaybackParams mPlaybackParams;
 
+        /**
+         * Default constructor
+         */
         public Builder() {
             if (Build.VERSION.SDK_INT >= 23) {
                 mPlaybackParams = new android.media.PlaybackParams();
@@ -181,6 +184,11 @@
             mPlaybackParams = playbackParams;
         }
 
+        /**
+         * Constructs a new PlaybackParams builder using data from {@code playbackParams}.
+         *
+         * @param playbackParams the non-null instance to initialize from.
+         */
         public Builder(@NonNull PlaybackParams playbackParams) {
             if (Build.VERSION.SDK_INT >= 23) {
                 mPlaybackParams = playbackParams.getPlaybackParams();
@@ -239,6 +247,7 @@
 
         /**
          * Takes the values of the Builder object and creates a PlaybackParams object.
+         *
          * @return PlaybackParams object with values from the Builder.
          */
         public @NonNull PlaybackParams build() {
diff --git a/media2/src/main/java/androidx/media2/SessionPlayer.java b/media2/src/main/java/androidx/media2/SessionPlayer.java
index bf298f7..329278f 100644
--- a/media2/src/main/java/androidx/media2/SessionPlayer.java
+++ b/media2/src/main/java/androidx/media2/SessionPlayer.java
@@ -383,9 +383,11 @@
     public abstract float getPlaybackSpeed();
 
     /**
-     * Sets a list of {@link MediaItem} with metadata. Ensure uniqueness of each {@link MediaItem}
-     * in the playlist so the session can uniquely identity individual items. All
-     * {@link MediaItem}s shouldn't be {@code null} as well.
+     * Sets a list of {@link MediaItem} with metadata. Use this or {@link #setMediaItem} to specify
+     * which items to play.
+     * <p>
+     * Ensure uniqueness of each {@link MediaItem} in the playlist so the session can uniquely
+     * identity individual items. All {@link MediaItem}s shouldn't be {@code null} as well.
      * <p>
      * It's recommended to fill {@link MediaMetadata} in each {@link MediaItem} especially for the
      * duration information with the key {@link MediaMetadata#METADATA_KEY_DURATION}. Without the
@@ -393,8 +395,8 @@
      * it to the controller.
      * <p>
      * The implementation must notify registered callbacks with
-     * {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
-     * completed.
+     * {@link PlayerCallback#onPlaylistChanged} and {@link PlayerCallback#onCurrentMediaItemChanged}
+     * when it's completed. The current media item would be the first item in the playlist.
      * <p>
      * The implementation must close the {@link ParcelFileDescriptor} in the {@link FileMediaItem}
      * when a media item in the playlist is a {@link FileMediaItem}.
@@ -403,7 +405,9 @@
      * @throws IllegalArgumentException if the given list is {@code null} or empty, or has
      *         duplicated media items.
      * @return a {@link ListenableFuture} which represents the pending completion of the command.
-     * @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
+     * @see #setMediaItem
+     * @see PlayerCallback#onPlaylistChanged
+     * @see PlayerCallback#onCurrentMediaItemChanged
      */
     public abstract @NonNull ListenableFuture<PlayerResult> setPlaylist(
             @NonNull List<MediaItem> list, @Nullable MediaMetadata metadata);
@@ -414,22 +418,28 @@
     public abstract @Nullable AudioAttributesCompat getAudioAttributes();
 
     /**
-     * Sets a {@link MediaItem} for playback.
+     * Sets a {@link MediaItem} for playback. Use this or {@link #setPlaylist} to specify which
+     * items to play. If you want to change current item in the playlist, use one of
+     * {@link #skipToPlaylistItem}, {@link #skipToNextPlaylistItem}, or
+     * {@link #skipToPreviousPlaylistItem} instead of this method.
      * <p>
-     * It's recommended to fill {@link MediaMetadata} in each {@link MediaItem} especially for the
+     * It's recommended to fill {@link MediaMetadata} in {@link MediaItem} especially for the
      * duration information with the key {@link MediaMetadata#METADATA_KEY_DURATION}. Without the
      * duration information in the metadata, session will do extra work to get the duration and send
      * it to the controller.
      * <p>
      * The implementation must notify registered callbacks with
-     * {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
-     * completed.
+     * {@link PlayerCallback#onPlaylistChanged} and {@link PlayerCallback#onCurrentMediaItemChanged}
+     * when it's completed. The current item would be the item given here.
      * <p>
      * The implementation must close the {@link ParcelFileDescriptor} in the {@link FileMediaItem}
      * if the given media item is a {@link FileMediaItem}.
      *
      * @param item the descriptor of media item you want to play
      * @return a {@link ListenableFuture} which represents the pending completion of the command.
+     * @see #setPlaylist
+     * @see PlayerCallback#onPlaylistChanged
+     * @see PlayerCallback#onCurrentMediaItemChanged
      * @throws IllegalArgumentException if the given item is {@code null}.
      */
     public abstract @NonNull ListenableFuture<PlayerResult> setMediaItem(
@@ -571,9 +581,10 @@
             @ShuffleMode int shuffleMode);
 
     /**
-     * Gets the playlist.
+     * Gets the playlist. Can be {@code null} if the playlist hasn't been set or it's reset by
+     * {@link #setMediaItem}.
      *
-     * @return playlist, or null if none is set.
+     * @return playlist, or {@code null}
      * @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
      */
     public abstract @Nullable List<MediaItem> getPlaylist();
@@ -611,13 +622,16 @@
     public abstract @ShuffleMode int getShuffleMode();
 
     /**
-     * Gets the current media item. This value may be updated when
+     * Gets the current media item, which is currently playing or would be played with later
+     * {@link #play}. This value may be updated when
      * {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
      * {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is
      * called.
      *
      * @return the current media item. Can be {@code null} only when media item or playlist hasn't
      *         been set.
+     * @see #setMediaItem
+     * @see #setPlaylist
      */
     public abstract @Nullable MediaItem getCurrentMediaItem();
 
@@ -629,7 +643,7 @@
      * @return the index of current media item. Can be {@link #INVALID_ITEM_INDEX} when current
      *         media item is null or not in the playlist, and when the playlist hasn't been set.
      */
-    public abstract int getCurrentMediaItemIndex();
+    public abstract @IntRange(from = INVALID_ITEM_INDEX) int getCurrentMediaItemIndex();
 
     /**
      * Gets the previous item index in the playlist. The returned value can be outdated after
@@ -639,7 +653,7 @@
      * @return the index of previous media item. Can be {@link #INVALID_ITEM_INDEX} only when
      *         previous media item does not exist or playlist hasn't been set.
      */
-    public abstract int getPreviousMediaItemIndex();
+    public abstract @IntRange(from = INVALID_ITEM_INDEX) int getPreviousMediaItemIndex();
 
     /**
      * Gets the next item index in the playlist. The returned value can be outdated after
@@ -649,7 +663,7 @@
      * @return the index of next media item. Can be {@link #INVALID_ITEM_INDEX} only when next media
      *         item does not exist or playlist hasn't been set.
      */
-    public abstract int getNextMediaItemIndex();
+    public abstract @IntRange(from = INVALID_ITEM_INDEX) int getNextMediaItemIndex();
 
     // Listeners / Callback related
     // Intentionally final not to allow developers to change the behavior
@@ -763,7 +777,8 @@
         }
 
         /**
-         * Called when a playlist is changed.
+         * Called when a playlist is changed. It's also called after {@link #setPlaylist} or
+         * {@link #setMediaItem}.
          *
          * @param player the player that has changed the playlist and playlist metadata.
          * @param list new playlist
@@ -824,7 +839,8 @@
         }
 
         /**
-         * Called when the player's current media item has changed.
+         * Called when the player's current media item has changed. It's also called after
+         * {@link #setPlaylist} or {@link #setMediaItem}.
          *
          * @param player the player whose media item changed.
          * @param item the new current media item.
@@ -877,11 +893,11 @@
          */
         @IntDef(flag = false, /*prefix = "RESULT_CODE",*/ value = {
                 RESULT_SUCCESS,
-                RESULT_ERROR_UNKNOWN_ERROR,
+                RESULT_ERROR_UNKNOWN,
                 RESULT_ERROR_INVALID_STATE,
                 RESULT_ERROR_BAD_VALUE,
                 RESULT_ERROR_PERMISSION_DENIED,
-                RESULT_ERROR_IO_ERROR,
+                RESULT_ERROR_IO,
                 RESULT_INFO_SKIPPED})
         @Retention(RetentionPolicy.SOURCE)
         @RestrictTo(LIBRARY_GROUP_PREFIX)
@@ -922,11 +938,11 @@
          * codes defined here. Check the documentation of the class that you're interested in.
          *
          * @return result code.
-         * @see #RESULT_ERROR_UNKNOWN_ERROR
+         * @see #RESULT_ERROR_UNKNOWN
          * @see #RESULT_ERROR_INVALID_STATE
          * @see #RESULT_ERROR_BAD_VALUE
          * @see #RESULT_ERROR_PERMISSION_DENIED
-         * @see #RESULT_ERROR_IO_ERROR
+         * @see #RESULT_ERROR_IO
          * @see #RESULT_INFO_SKIPPED
          */
         @Override
diff --git a/media2/src/main/java/androidx/media2/SessionResult.java b/media2/src/main/java/androidx/media2/SessionResult.java
index 9f4d535..dac076d 100644
--- a/media2/src/main/java/androidx/media2/SessionResult.java
+++ b/media2/src/main/java/androidx/media2/SessionResult.java
@@ -57,11 +57,11 @@
      */
     @IntDef(flag = false, /*prefix = "RESULT_CODE",*/ value = {
             RESULT_SUCCESS,
-            RESULT_ERROR_UNKNOWN_ERROR,
+            RESULT_ERROR_UNKNOWN,
             RESULT_ERROR_INVALID_STATE,
             RESULT_ERROR_BAD_VALUE,
             RESULT_ERROR_PERMISSION_DENIED,
-            RESULT_ERROR_IO_ERROR,
+            RESULT_ERROR_IO,
             RESULT_INFO_SKIPPED,
             RESULT_ERROR_SESSION_DISCONNECTED,
             RESULT_ERROR_NOT_SUPPORTED,
@@ -137,11 +137,11 @@
      *
      * @return result code
      * @see #RESULT_SUCCESS
-     * @see #RESULT_ERROR_UNKNOWN_ERROR
+     * @see #RESULT_ERROR_UNKNOWN
      * @see #RESULT_ERROR_INVALID_STATE
      * @see #RESULT_ERROR_BAD_VALUE
      * @see #RESULT_ERROR_PERMISSION_DENIED
-     * @see #RESULT_ERROR_IO_ERROR
+     * @see #RESULT_ERROR_IO
      * @see #RESULT_INFO_SKIPPED
      * @see #RESULT_ERROR_SESSION_DISCONNECTED
      * @see #RESULT_ERROR_NOT_SUPPORTED
diff --git a/media2/src/main/java/androidx/media2/UriMediaItem.java b/media2/src/main/java/androidx/media2/UriMediaItem.java
index 5d70552..a867f40 100644
--- a/media2/src/main/java/androidx/media2/UriMediaItem.java
+++ b/media2/src/main/java/androidx/media2/UriMediaItem.java
@@ -115,7 +115,7 @@
     /**
      * This Builder class simplifies the creation of a {@link UriMediaItem} object.
      */
-    public static final class Builder extends BuilderBase<Builder> {
+    public static final class Builder extends MediaItem.Builder {
 
         @SuppressWarnings("WeakerAccess") /* synthetic access */
         Uri mUri;
@@ -180,11 +180,33 @@
             mUriContext = context;
         }
 
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setMetadata(@Nullable MediaMetadata metadata) {
+            return (Builder) super.setMetadata(metadata);
+        }
+
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setStartPosition(long position) {
+            return (Builder) super.setStartPosition(position);
+        }
+
+        // Override just to change return type.
+        @NonNull
+        @Override
+        public Builder setEndPosition(long position) {
+            return (Builder) super.setEndPosition(position);
+        }
+
         /**
          * @return A new UriMediaItem with values supplied by the Builder.
          */
+        @NonNull
         @Override
-        public @NonNull UriMediaItem build() {
+        public UriMediaItem build() {
             return new UriMediaItem(this);
         }
     }
diff --git a/media2/src/main/res/values-as/strings.xml b/media2/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..73e9775
--- /dev/null
+++ b/media2/src/main/res/values-as/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="default_notification_channel_name" msgid="3980628489251198480">"এতিয়া প্লে’ হৈ আছে"</string>
+    <string name="play_button_content_description" msgid="5898492519310326971">"প্লে\'"</string>
+    <string name="pause_button_content_description" msgid="7844788760550939526">"পজ"</string>
+    <string name="skip_to_previous_item_button_content_description" msgid="250823119142607886">"পূৰ্বৱৰ্তী বস্তুটোলৈ যাওক"</string>
+    <string name="skip_to_next_item_button_content_description" msgid="3508534122676007656">"পৰৱৰ্তী বস্তুটোলৈ যাওক"</string>
+</resources>
diff --git a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaItemTest.java b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaItemTest.java
index ddcdb32..8a44064 100644
--- a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaItemTest.java
+++ b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaItemTest.java
@@ -17,21 +17,26 @@
 package androidx.media2.test.service.tests;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.content.res.AssetFileDescriptor;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
 
+import androidx.annotation.NonNull;
+import androidx.media2.CallbackMediaItem;
+import androidx.media2.DataSourceCallback;
+import androidx.media2.FileMediaItem;
 import androidx.media2.MediaItem;
 import androidx.media2.MediaMetadata;
 import androidx.media2.MediaUtils;
 import androidx.media2.UriMediaItem;
 import androidx.media2.test.service.MediaTestUtils;
+import androidx.media2.test.service.test.R;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SdkSuppress;
 import androidx.test.filters.SmallTest;
 import androidx.versionedparcelable.ParcelImpl;
@@ -40,75 +45,181 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
 
 /**
- * Tests {@link MediaItem}.
+ * Tests {@link MediaItem} and its subclasses.
  */
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
 @SmallTest
 public class MediaItemTest {
+    private final MediaItemFactory mItemFactory;
+    private final Class mItemBuilderClass;
     private Context mContext;
+    private MediaItem mTestItem;
+
+    private static final MediaItemFactory sMediaItemFactory = new MediaItemFactory() {
+        @Override
+        public MediaItem create(Context context) {
+            final MediaMetadata testMetadata = new MediaMetadata.Builder()
+                    .putLong("MediaItemTest", 1).build();
+            return new MediaItem.Builder()
+                    .setMetadata(testMetadata)
+                    .setStartPosition(1)
+                    .setEndPosition(10)
+                    .build();
+        }
+    };
+
+    private static final MediaItemFactory sUriMediaItemFactory = new MediaItemFactory() {
+        @Override
+        public MediaItem create(Context context) {
+            final MediaMetadata testMetadata = new MediaMetadata.Builder()
+                    .putString("MediaItemTest", "MediaItemTest").build();
+            return new UriMediaItem.Builder(context, Uri.parse("test://test"))
+                    .setMetadata(testMetadata)
+                    .setStartPosition(1)
+                    .setEndPosition(1000)
+                    .build();
+        }
+    };
+
+    private static final MediaItemFactory sCallbackMediaItemFactory = new MediaItemFactory() {
+        @Override
+        public MediaItem create(Context context) {
+            final MediaMetadata testMetadata = new MediaMetadata.Builder()
+                    .putText("MediaItemTest", "testtest").build();
+            final DataSourceCallback callback = new DataSourceCallback() {
+                @Override
+                public int readAt(long position, @NonNull byte[] buffer, int offset, int size)
+                        throws IOException {
+                    return 0;
+                }
+
+                @Override
+                public long getSize() throws IOException {
+                    return 0;
+                }
+
+                @Override
+                public void close() throws IOException {
+                    // no-op
+                }
+            };
+            return new CallbackMediaItem.Builder(callback)
+                    .setMetadata(testMetadata)
+                    .setStartPosition(0)
+                    .setEndPosition(0)
+                    .build();
+        }
+    };
+
+    private static final MediaItemFactory sFileMediaItemFactory = new MediaItemFactory() {
+        @Override
+        public MediaItem create(Context context) {
+            int resId = R.raw.midi8sec;
+            try (AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId)) {
+                return new FileMediaItem.Builder(
+                        ParcelFileDescriptor.dup(afd.getFileDescriptor())).build();
+            } catch (Exception e) {
+                return null;
+            }
+        }
+    };
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {sMediaItemFactory, MediaItem.Builder.class},
+                {sUriMediaItemFactory, UriMediaItem.Builder.class},
+                {sCallbackMediaItemFactory, CallbackMediaItem.Builder.class},
+                {sFileMediaItemFactory, FileMediaItem.Builder.class}});
+    }
+
+    public MediaItemTest(MediaItemFactory factory, Class builderClass) {
+        mItemFactory = factory;
+        mItemBuilderClass = builderClass;
+    }
 
     @Before
     public void setUp() {
         mContext = ApplicationProvider.getApplicationContext();
+        mTestItem = mItemFactory.create(mContext);
     }
 
     @Test
     public void testSubclass_sameProcess() {
-        final UriMediaItem testUriItem = createUriMediaItem();
-        final ParcelImpl parcel = MediaUtils.toParcelable(testUriItem);
+        final ParcelImpl parcel = MediaUtils.toParcelable(mTestItem);
 
         final MediaItem testRemoteItem = MediaUtils.fromParcelable(parcel);
-        assertEquals(testUriItem, testRemoteItem);
+        assertEquals(mTestItem, testRemoteItem);
     }
 
     @Test
     public void testSubclass_acrossProcessWithMediaUtils() {
-        final UriMediaItem testUriItem = createUriMediaItem();
-
         // Mocks the binder call across the processes by using writeParcelable/readParcelable
         // which only happens between processes. Code snippets are copied from
         // VersionedParcelIntegTest#parcelCopy.
         final Parcel p = Parcel.obtain();
-        p.writeParcelable(MediaUtils.toParcelable(testUriItem), 0);
+        p.writeParcelable(MediaUtils.toParcelable(mTestItem), 0);
         p.setDataPosition(0);
         final MediaItem testRemoteItem = MediaUtils.fromParcelable(
                 (ParcelImpl) p.readParcelable(MediaItem.class.getClassLoader()));
 
-        assertFalse(testRemoteItem instanceof UriMediaItem);
-        assertEquals(testUriItem.getStartPosition(), testRemoteItem.getStartPosition());
-        assertEquals(testUriItem.getEndPosition(), testRemoteItem.getEndPosition());
+        assertEquals(MediaItem.class, testRemoteItem.getClass());
+        assertEquals(mTestItem.getStartPosition(), testRemoteItem.getStartPosition());
+        assertEquals(mTestItem.getEndPosition(), testRemoteItem.getEndPosition());
         MediaTestUtils.assertMediaMetadataEquals(
-                testUriItem.getMetadata(), testRemoteItem.getMetadata());
+                mTestItem.getMetadata(), testRemoteItem.getMetadata());
     }
 
     @Test
     public void testSubclass_acrossProcessWithParcelUtils() {
-        final UriMediaItem testUriItem = createUriMediaItem();
-
-        // Mocks the binder call across the processes by using writeParcelable/readParcelable
-        // which only happens between processes. Code snippets are copied from
-        // VersionedParcelIntegTest#parcelCopy.
+        if (mTestItem.getClass() == MediaItem.class) {
+            return;
+        }
         try {
+            // Mocks the binder call across the processes by using writeParcelable/readParcelable
+            // which only happens between processes. Code snippets are copied from
+            // VersionedParcelIntegTest#parcelCopy.
             final Parcel p = Parcel.obtain();
-            p.writeParcelable(ParcelUtils.toParcelable(testUriItem), 0);
+            p.writeParcelable(ParcelUtils.toParcelable(mTestItem), 0);
             p.setDataPosition(0);
             final MediaItem testRemoteItem = ParcelUtils.fromParcelable(
                     (ParcelImpl) p.readParcelable(MediaItem.class.getClassLoader()));
-            fail("Write to parcel should fail for subclass of MediaItem");
+
+            assertTrue("Write to parcel should fail for subclass of MediaItem",
+                    mTestItem.getClass() == MediaItem.class);
         } catch (Exception e) {
         }
     }
 
-    private UriMediaItem createUriMediaItem() {
-        final MediaMetadata testMetadata = new MediaMetadata.Builder()
-                .putString("MediaItemTest", "MediaItemTest").build();
-        return new UriMediaItem.Builder(mContext, Uri.parse("test://test"))
-                        .setMetadata(testMetadata)
-                        .setStartPosition(1)
-                        .setEndPosition(1000)
-                        .build();
+    /**
+     * Tests whether the methods in MediaItem.Builder have been hidden in subclasses by overriding
+     * them all.
+     */
+    @Test
+    public void testSubclass_overriddenAllMethods() throws Exception {
+        Method[] mediaItemBuilderMethods = MediaItem.Builder.class.getDeclaredMethods();
+        for (int i = 0; i < mediaItemBuilderMethods.length; i++) {
+            Method mediaItemBuilderMethod = mediaItemBuilderMethods[i];
+            if (!Modifier.isPublic(mediaItemBuilderMethod.getModifiers())) {
+                continue;
+            }
+            Method subclassMethod = mItemBuilderClass.getMethod(
+                    mediaItemBuilderMethod.getName(), mediaItemBuilderMethod.getParameterTypes());
+            assertEquals(subclassMethod.getDeclaringClass(), mItemBuilderClass);
+        }
+    }
+
+    interface MediaItemFactory {
+        MediaItem create(Context context);
     }
 }
diff --git a/media2/version-compat-tests/current/service/src/androidTest/res/raw/midi8sec.mid b/media2/version-compat-tests/current/service/src/androidTest/res/raw/midi8sec.mid
new file mode 100644
index 0000000..746aca1
--- /dev/null
+++ b/media2/version-compat-tests/current/service/src/androidTest/res/raw/midi8sec.mid
Binary files differ
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteActionProvider.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteActionProvider.java
index 191f9e0..6dd4e27 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteActionProvider.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteActionProvider.java
@@ -199,6 +199,13 @@
 
     /**
      * Enables dynamic group feature.
+     * With this enabled, a different set of {@link MediaRouteChooserDialog} and
+     * {@link MediaRouteControllerDialog} is shown when the button is clicked.
+     * If a {@link androidx.mediarouter.media.MediaRouteProvider media route provider}
+     * supports dynamic group, the users can use that feature with the dialogs.
+     *
+     * @see MediaRouteButton#enableDynamicGroup()
+     * @see androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController
      */
     public void enableDynamicGroup() {
         mUseDynamicGroup = true;
@@ -208,8 +215,9 @@
     }
 
     /**
-     * Sets weather {@link MediaRouteButton} is visible when no routes are available.
+     * Sets whether {@link MediaRouteButton} is visible when no routes are available.
      * When true, the button is visible even if there are no routes to connect.
+     * The default is false.
      *
      * @param alwaysVisible true to show MediaRouteButton always.
      *
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
index 61e7717..2ff24e0 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
@@ -263,6 +263,12 @@
 
     /**
      * Enables dynamic group feature.
+     * With this enabled, a different set of {@link MediaRouteChooserDialog} and
+     * {@link MediaRouteControllerDialog} is shown when the button is clicked.
+     * If a {@link androidx.mediarouter.media.MediaRouteProvider media route provider}
+     * supports dynamic group, the users can use that feature with the dialogs.
+     *
+     * @see androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController
      */
     public void enableDynamicGroup() {
         mUseDynamicGroup = true;
@@ -407,10 +413,11 @@
     }
 
     /**
-     * Sets weather the button is visible when no routes are available.
+     * Sets whether the button is visible when no routes are available.
      * When true, the button is visible even if there are no routes to connect.
      * You may want to override {@link View#performClick()} to change the behavior
      * when the button is clicked.
+     * The default is false.
      * It doesn't overrides the {@link View#getVisibility visibility} status of the button.
      *
      * @param alwaysVisible true to show button always.
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteCastDialog.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteCastDialog.java
index 6560d8f..ac3269e 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteCastDialog.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteCastDialog.java
@@ -99,7 +99,8 @@
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
 public class MediaRouteCastDialog extends AppCompatDialog {
-    static final String TAG = "MediaRouteCastDialog";
+    private static final String TAG = "MediaRouteCastDialog";
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     // Do not update the route list immediately to avoid unnatural dialog change.
@@ -107,28 +108,35 @@
     private static final int CONNECTION_TIMEOUT_MS = 30000;
     private static final int UPDATE_VOLUME_DELAY_MS = 500;
 
-    static final int MSG_UPDATE_ROUTES_VIEW = 1;
-    static final int MSG_UPDATE_ROUTE_VOLUME_BY_USER = 2;
+    private static final int MSG_UPDATE_ROUTES_VIEW = 1;
+    private static final int MSG_UPDATE_ROUTE_VOLUME_BY_USER = 2;
 
     // TODO (b/111731099): Remove this once dark theme is implemented inside MediaRouterThemeHelper.
-    static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE;
+    private static final int COLOR_WHITE_ON_DARK_BACKGROUND = Color.WHITE;
 
-    static final int MUTED_VOLUME = 0;
-    static final int MIN_UNMUTED_VOLUME = 1;
+    private static final int MUTED_VOLUME = 0;
+    private static final int MIN_UNMUTED_VOLUME = 1;
 
     private static final int BLUR_RADIUS = 10;
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final MediaRouter mRouter;
     private final MediaRouterCallback mCallback;
     private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     MediaRouter.RouteInfo mSelectedRoute;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final List<MediaRouter.RouteInfo> mMemberRoutes = new ArrayList<>();
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final List<MediaRouter.RouteInfo> mGroupableRoutes = new ArrayList<>();
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final List<MediaRouter.RouteInfo> mTransferableRoutes = new ArrayList<>();
 
     // List of routes that were previously groupable but temporarily ungroupable.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final List<MediaRouter.RouteInfo> mUngroupableRoutes = new ArrayList<>();
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     Context mContext;
     private boolean mCreated;
     private boolean mAttachedToWindow;
@@ -156,15 +164,15 @@
     RecyclerView mRecyclerView;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     RecyclerAdapter mAdapter;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     VolumeChangeListener mVolumeChangeListener;
-    int mVolumeSliderColor;
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     Map<String, MediaRouteVolumeSliderHolder> mVolumeSliderHolderMap;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     MediaRouter.RouteInfo mRouteForVolumeUpdatingByUser;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    Map<String, Integer> mBeforeMuteVolumeMap;
+    Map<String, Integer> mUnmutedVolumeMap;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     boolean mIsSelectingRoute;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
@@ -178,20 +186,30 @@
 
     private ImageView mMetadataBackground;
     private View mMetadataBlackScrim;
-    private ImageView mArtView;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    ImageView mArtView;
     private TextView mTitleView;
     private TextView mSubtitleView;
     private String mTitlePlaceholder;
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     MediaControllerCompat mMediaController;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     MediaControllerCallback mControllerCallback;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     MediaDescriptionCompat mDescription;
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     FetchArtTask mFetchArtTask;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     Bitmap mArtIconBitmap;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     Uri mArtIconUri;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     boolean mArtIconIsLoaded;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     Bitmap mArtIconLoadedBitmap;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     int mArtIconBackgroundColor;
 
     public MediaRouteCastDialog(Context context) {
@@ -239,7 +257,7 @@
         MediaMetadataCompat metadata = mMediaController == null ? null
                 : mMediaController.getMetadata();
         mDescription = metadata == null ? null : metadata.getDescription();
-        updateArtIconIfNeeded();
+        reloadIconIfNeeded();
         updateMetadataViews();
     }
 
@@ -348,9 +366,8 @@
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
         mVolumeChangeListener = new VolumeChangeListener();
-        mVolumeSliderColor = MediaRouterThemeHelper.getControllerColor(mContext, 0);
         mVolumeSliderHolderMap = new HashMap<>();
-        mBeforeMuteVolumeMap = new HashMap<>();
+        mUnmutedVolumeMap = new HashMap<>();
 
         mMetadataBackground = findViewById(R.id.mr_cast_meta_background);
         mMetadataBlackScrim = findViewById(R.id.mr_cast_meta_black_scrim);
@@ -376,7 +393,7 @@
 
         mArtIconBitmap = null;
         mArtIconUri = null;
-        updateArtIconIfNeeded();
+        reloadIconIfNeeded();
         updateMetadataViews();
         updateRoutesView();
     }
@@ -407,12 +424,14 @@
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
-    int getDesiredArtHeight(int originalWidth, int originalHeight) {
-        return mArtView.getHeight();
-    }
+    void reloadIconIfNeeded() {
+        Bitmap newBitmap = mDescription == null ? null : mDescription.getIconBitmap();
+        Uri newUri = mDescription == null ? null : mDescription.getIconUri();
+        Bitmap oldBitmap = mFetchArtTask == null ? mArtIconBitmap : mFetchArtTask.getIconBitmap();
+        Uri oldUri = mFetchArtTask == null ? mArtIconUri : mFetchArtTask.getIconUri();
 
-    void updateArtIconIfNeeded() {
-        if (!isIconChanged()) {
+        if (oldBitmap == newBitmap
+                && (oldBitmap != null || ObjectsCompat.equals(oldUri, newUri))) {
             return;
         }
         if (mFetchArtTask != null) {
@@ -426,6 +445,7 @@
      * Clear the bitmap loaded by FetchArtTask. Will be called after the loaded bitmaps are applied
      * to artwork, or no longer valid.
      */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     void clearLoadedBitmap() {
         mArtIconIsLoaded = false;
         mArtIconLoadedBitmap = null;
@@ -433,24 +453,6 @@
     }
 
     /**
-     * Returns whether a new art image is different from an original art image. Compares
-     * Bitmap objects first, and then compares URIs only if bitmap is unchanged with
-     * a null value.
-     */
-    private boolean isIconChanged() {
-        Bitmap newBitmap = mDescription == null ? null : mDescription.getIconBitmap();
-        Uri newUri = mDescription == null ? null : mDescription.getIconUri();
-        Bitmap oldBitmap = mFetchArtTask == null ? mArtIconBitmap : mFetchArtTask.getIconBitmap();
-        Uri oldUri = mFetchArtTask == null ? mArtIconUri : mFetchArtTask.getIconUri();
-        if (oldBitmap != newBitmap) {
-            return true;
-        } else if (oldBitmap == null && ObjectsCompat.equals(oldUri, newUri)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
      * Returns whether updateMetadataViews and updateRoutesView should defer updating views.
      */
     private boolean shouldDeferUpdateViews() {
@@ -463,10 +465,7 @@
             return true;
         }
         // Defer updating views if corresponding views aren't created yet.
-        if (!mCreated) {
-            return true;
-        }
-        return false;
+        return !mCreated;
     }
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
@@ -537,6 +536,7 @@
         }
     }
 
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     static void setLayoutHeight(View view, int height) {
         ViewGroup.LayoutParams lp = view.getLayoutParams();
         lp.height = height;
@@ -579,21 +579,26 @@
     }
 
     /**
-     * Returns a list of groupable routes of selected route.
-     * If selected route is not dynamic group, returns empty list.
+     * Returns a list of currently groupable routes of the selected route.
+     * If the selected route is not dynamic group, returns empty list.
      */
-    List<MediaRouter.RouteInfo> getGroupableRoutes() {
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    List<MediaRouter.RouteInfo> getCurrentGroupableRoutes() {
         List<MediaRouter.RouteInfo> groupableRoutes = new ArrayList<>();
         if (mSelectedRoute.isDynamicRoute()) {
             for (MediaRouter.RouteInfo route : mSelectedRoute.getProvider().getRoutes()) {
-                if (route.isGroupable()) groupableRoutes.add(route);
+                if (route.isGroupable()) {
+                    groupableRoutes.add(route);
+                }
+
             }
         }
         return groupableRoutes;
     }
 
     /**
-     * Updates the routes view that are shown in the cast dialog.
+     * Updates the visible status(groupable/unselectable status and volume) of routes.
+     * The position of the routes is not changed and no routes are added/removed.
      */
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     void updateRoutesView() {
@@ -618,6 +623,12 @@
         }
     }
 
+    /**
+     * Updates routes and items of the adapter.
+     * It introduces new routes or hides removed routes.
+     * Calling this method would result in sudden UI changes due to change of the adapter.
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     void updateRoutes() {
         mMemberRoutes.clear();
         mGroupableRoutes.clear();
@@ -644,7 +655,7 @@
         Collections.sort(mGroupableRoutes, RouteComparator.sInstance);
         Collections.sort(mTransferableRoutes, RouteComparator.sInstance);
 
-        mAdapter.setItems();
+        mAdapter.updateItems();
     }
 
     @RequiresApi(17)
@@ -735,14 +746,14 @@
             if (mute) {
                 // Save current progress, who is the progress just before muted, so that the volume
                 // can be restored to that value when user unmutes it.
-                mBeforeMuteVolumeMap.put(mRoute.getId(), mVolumeSlider.getProgress());
+                mUnmutedVolumeMap.put(mRoute.getId(), mVolumeSlider.getProgress());
             } else {
-                mBeforeMuteVolumeMap.remove(mRoute.getId());
+                mUnmutedVolumeMap.remove(mRoute.getId());
             }
         }
 
         int getUnmutedVolume() {
-            Integer beforeMuteVolume = mBeforeMuteVolumeMap.get(mRoute.getId());
+            Integer beforeMuteVolume = mUnmutedVolumeMap.get(mRoute.getId());
 
             return (beforeMuteVolume == null)
                     ? MIN_UNMUTED_VOLUME : Math.max(MIN_UNMUTED_VOLUME, beforeMuteVolume);
@@ -778,7 +789,7 @@
                     R.integer.mr_cast_volume_slider_layout_animation_duration_ms);
             mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
 
-            setItems();
+            updateItems();
         }
 
         boolean isGroupVolumeNeeded() {
@@ -834,6 +845,7 @@
             }
 
             boolean wasShown = isGroupVolumeNeeded();
+            // Group volume is shown when two or more members are in the selected route.
             boolean shouldShow = memberCount >= 2;
 
             if (wasShown != shouldShow) {
@@ -849,7 +861,7 @@
         }
 
         // Create a list of items with mMemberRoutes and add them to mItems
-        void setItems() {
+        void updateItems() {
             mItems.clear();
 
             mGroupVolumeItem = new Item(mSelectedRoute, ITEM_TYPE_GROUP_VOLUME);
@@ -912,12 +924,12 @@
             // routes at groupable routes section.
             mUngroupableRoutes.clear();
             mUngroupableRoutes.addAll(MediaRouteDialogHelper.getItemsRemoved(mGroupableRoutes,
-                    getGroupableRoutes()));
+                    getCurrentGroupableRoutes()));
             notifyDataSetChanged();
         }
 
         @Override
-        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
             View view;
 
             switch (viewType) {
@@ -940,7 +952,7 @@
         }
 
         @Override
-        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
             int viewType = getItemViewType(position);
             Item item = getItem(position);
 
@@ -975,7 +987,7 @@
         }
 
         @Override
-        public void onViewRecycled(RecyclerView.ViewHolder holder) {
+        public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
             super.onViewRecycled(holder);
             mVolumeSliderHolderMap.values().remove(holder);
         }
@@ -1070,7 +1082,7 @@
                 mExpandedHeight = (int) value.getDimension(metrics);
             }
 
-            public void bindGroupVolumeViewHolder(Item item) {
+            void bindGroupVolumeViewHolder(Item item) {
                 setLayoutHeight(itemView, isGroupVolumeNeeded() ? mExpandedHeight : 0);
 
                 MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.getData();
@@ -1079,7 +1091,7 @@
                 mTextView.setText(route.getName());
             }
 
-            public int getExpandedHeight() {
+            int getExpandedHeight() {
                 return mExpandedHeight;
             }
         }
@@ -1092,7 +1104,7 @@
                 mTextView = itemView.findViewById(R.id.mr_cast_header_name);
             }
 
-            public void bindHeaderViewHolder(Item item) {
+            void bindHeaderViewHolder(Item item) {
                 String headerName = item.getData().toString();
 
                 mTextView.setText(headerName);
@@ -1179,6 +1191,10 @@
                 if (mUngroupableRoutes.contains(route)) {
                     return false;
                 }
+                // The last member route can not be removed.
+                if (isSelected(route) && mSelectedRoute.getMemberRoutes().size() < 2) {
+                    return false;
+                }
                 // Selected route that can't be unselected has to be disabled.
                 if (isSelected(route) && mSelectedRoute.isDynamicRoute()) {
                     return route.isUnselectable();
@@ -1186,7 +1202,7 @@
                 return true;
             }
 
-            public void bindRouteViewHolder(Item item) {
+            void bindRouteViewHolder(Item item) {
                 MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.getData();
 
                 // This is required to sync volume and the name of the route
@@ -1286,7 +1302,7 @@
                 return true;
             }
 
-            public void bindGroupViewHolder(Item item) {
+            void bindGroupViewHolder(Item item) {
                 final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.getData();
                 mRoute = route;
                 mImageView.setVisibility(View.VISIBLE);
@@ -1309,42 +1325,17 @@
         }
     }
 
-    /*
-     * The following comment is to improve readability. It explains about the sequence of callback
-     * triggering when selecting a route whose provider supports dynamic group.
-     *
-     * When selecting a route, the sequence of callback triggered depends on whether the provider
-     * supports dynamic group or not. If the provider supports dynamic group, the sequence becomes
-     * more complex, which is shown below with parameters of callbacks.
-     * 1) onRouteUnselected
-     *    Previously selected route is passed as a parameter.
-     * 2) onRouteSelected
-     *    Dynamic group route of selecting route isn't published at this point, which means passed
-     *    route is not dynamic group route yet.
-     * 3) onRouteAdded
-     *    Published dynamic group route is passed as a parameter, but its member/groupable/
-     *    transferable routes are not initialized yet.
-     * 4) onRouteChanged
-     *    member/groupable/transferable routes of dynamic group route is initialized and
-     *    corresponding dynamic group route is passed as a parameter.
-     */
+    // When a new route is selected, member/groupable/transferable routes are not updated
+    // immediately in onRouteSelected(). Instead, onRouteChanged() is called after a while.
+    // So we should refresh items in onRouteChanged().
+    // But onRouteChanged() is also called when a member is added/removed so we refresh
+    // items only when a new route is found, which happens right after a new member is selected.
     private final class MediaRouterCallback extends MediaRouter.Callback {
         MediaRouterCallback() {
         }
 
-        // This method is to check if selecting a route is in progress and the provider of selecting
-        // route supports dynamic group or not.
-        private boolean isSelectingDynamicRoute() {
-            // Because MediaRouteCastDialog shows routes with same provider, we can check if the
-            // provider of selecting route supports dynamic group or not by checking instance of
-            // mSelectedRoute.
-            return mIsSelectingRoute && mSelectedRoute.isDynamicRoute();
-        }
-
         @Override
         public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            // Defer updating because member/groupable/transferable routes of selecting route isn't
-            // initialized yet.
             updateRoutesView();
         }
 
@@ -1377,6 +1368,7 @@
                     if (mSelectedRoute.getMemberRoutes().contains(memberRoute)) {
                         continue;
                     }
+                    // Refresh items only when a new groupable route is found.
                     if (memberRoute.isGroupable() && !mGroupableRoutes.contains(memberRoute)) {
                         shouldRefreshRoute = true;
                         break;
@@ -1385,6 +1377,7 @@
             }
             if (shouldRefreshRoute) {
                 updateViewsIfNeeded();
+                // Calls updateRoutes to show new routes.
                 updateRoutes();
             } else {
                 updateRoutesView();
@@ -1421,7 +1414,7 @@
         @Override
         public void onMetadataChanged(MediaMetadataCompat metadata) {
             mDescription = metadata == null ? null : metadata.getDescription();
-            updateArtIconIfNeeded();
+            reloadIconIfNeeded();
             updateMetadataViews();
         }
     }
@@ -1441,11 +1434,11 @@
             mIconUri = mDescription == null ? null : mDescription.getIconUri();
         }
 
-        public Bitmap getIconBitmap() {
+        Bitmap getIconBitmap() {
             return mIconBitmap;
         }
 
-        public Uri getIconUri() {
+        Uri getIconUri() {
             return mIconUri;
         }
 
@@ -1486,7 +1479,7 @@
                     }
                     // Calculate required size to decode the art and possibly resize it.
                     options.inJustDecodeBounds = false;
-                    int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight);
+                    int reqHeight = mArtView.getHeight();
                     int ratio = options.outHeight / reqHeight;
                     options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio));
                     if (isCancelled()) {
@@ -1551,7 +1544,7 @@
     }
 
     static final class RouteComparator implements Comparator<MediaRouter.RouteInfo> {
-        public static final RouteComparator sInstance = new RouteComparator();
+        static final RouteComparator sInstance = new RouteComparator();
 
         @Override
         public int compare(MediaRouter.RouteInfo lhs, MediaRouter.RouteInfo rhs) {
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDevicePickerDialog.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDevicePickerDialog.java
index b16d038..94ff389 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDevicePickerDialog.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteDevicePickerDialog.java
@@ -69,7 +69,7 @@
     private static final int ITEM_TYPE_ROUTE = 2;
 
     // Do not update the route list immediately to avoid unnatural dialog change.
-    static final int MSG_UPDATE_ROUTES = 1;
+    private static final int MSG_UPDATE_ROUTES = 1;
 
     final MediaRouter mRouter;
     private final MediaRouteDevicePickerDialog.MediaRouterCallback mCallback;
@@ -251,7 +251,7 @@
         mLastUpdateTime = SystemClock.uptimeMillis();
         mRoutes.clear();
         mRoutes.addAll(routes);
-        mAdapter.setItems();
+        mAdapter.rebuildItems();
     }
 
     private final class MediaRouterCallback extends MediaRouter.Callback {
@@ -310,11 +310,11 @@
             mTvIcon = MediaRouterThemeHelper.getTvDrawableIcon(mContext);
             mSpeakerIcon = MediaRouterThemeHelper.getSpeakerDrawableIcon(mContext);
             mSpeakerGroupIcon = MediaRouterThemeHelper.getSpeakerGroupDrawableIcon(mContext);
-            setItems();
+            rebuildItems();
         }
 
         // Create a list of items with mMemberRoutes and add them to mItems
-        void setItems() {
+        void rebuildItems() {
             mItems.clear();
 
             mItems.add(new Item(mContext.getString(R.string.mr_chooser_title)));
@@ -349,7 +349,7 @@
 
             switch (viewType) {
                 case ITEM_TYPE_HEADER:
-                    ((HeaderViewHolder) holder).binHeaderView(item);
+                    ((HeaderViewHolder) holder).bindHeaderView(item);
                     break;
                 case ITEM_TYPE_ROUTE:
                     ((RouteViewHolder) holder).bindRouteView(item);
@@ -446,7 +446,7 @@
                 mTextView = itemView.findViewById(R.id.mr_picker_header_name);
             }
 
-            public void binHeaderView(Item item) {
+            public void bindHeaderView(Item item) {
                 String headerName = item.getData().toString();
 
                 mTextView.setText(headerName);
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java
index b9b168f..2beba1a 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java
@@ -56,6 +56,12 @@
  * by implementing {@link #onCreateRouteController} to return a {@link RouteController}
  * for a particular route.
  * </p><p>
+ * A media route provider can support
+ * {@link MediaRouteProviderDescriptor#supportsDynamicGroupRoute dynamic group} that
+ * allows the user to add or remove a route from the selected route dynamically.
+ * To control dynamic group, {@link DynamicGroupRouteController} returned by
+ * {@link #onCreateDynamicGroupRouteController} is used instead of {@link RouteController}.
+ * </p><p>
  * A media route provider may be used privately within the scope of a single
  * application process by calling {@link MediaRouter#addProvider MediaRouter.addProvider}
  * to add it to the local {@link MediaRouter}.  A media route provider may also be made
@@ -447,18 +453,13 @@
 
     /**
      * Provides control over a dynamic group route.
+     * A dynamic group route is a group of routes such that a route can be added or removed
+     * from the group by the user dynamically.
      */
     public abstract static class DynamicGroupRouteController extends RouteController {
         /**
-         * Gets the ID of the dynamic group route. Note that the route may have not been
-         * published yet by the time the {@link DynamicGroupRouteController} is created.
-         */
-
-        // TODO add @link annotation in front of MediaRouteCastDialog.
-        /**
-         * Gets the title of the groupable routes section on the UX such as
-         * androidx.mediarouter.app.MediaRouteCastDialog, which is proposed by
-         * {@link MediaRouteProvider}.
+         * Gets the title of the groupable routes section which will be shown to the user.
+         * It is provided by {@link MediaRouteProvider}.
          * e.g. "Add a device."
          */
         @Nullable
@@ -466,10 +467,9 @@
             return null;
         }
 
-        // TODO add @link annotation in front of MediaRouteCastDialog.
         /**
-         * Gets the title of the transferable routes section on the UX such as
-         * androidx.mediarouter.app.MediaRouteCastDialog, which is proposed by
+         * Gets the title of the transferable routes section which will be shown to the user.
+         * It is provided by {@link MediaRouteProvider}.
          * {@link MediaRouteProvider}.
          * e.g. "Play on group."
          */
@@ -517,6 +517,9 @@
          */
         public interface OnDynamicRoutesChangedListener {
             /**
+             * The provider should call this method when routes' properties change.
+             * (e.g. when a route becomes ungroupable)
+             *
              * @param controller the {@link DynamicGroupRouteController} which keeps this listener.
              * @param routes the collection of routes contains selected routes
              *               (can be unselectable or not)
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderDescriptor.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderDescriptor.java
index a39195fc..0ffb1e9 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderDescriptor.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProviderDescriptor.java
@@ -79,6 +79,8 @@
 
     /**
      * Indicates whether a {@link MediaRouteProvider} supports dynamic group route.
+     *
+     * @see androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController
      */
     public boolean supportsDynamicGroupRoute() {
         return mSupportsDynamicGroupRoute;
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index 3e6d598..5aba0e9 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -2292,6 +2292,11 @@
                         + "dynamic group route.");
             }
             if (!mSelectedRoute.getMemberRoutes().contains(route) || !route.isUnselectable()) {
+                Log.w(TAG, "Ignoring attempt to remove not unselectable member route : " + route);
+                return;
+            }
+            if (mSelectedRoute.getMemberRoutes().size() <= 1) {
+                Log.w(TAG, "Ignoring attempt to remove the last member route.");
                 return;
             }
             ((DynamicGroupRouteController) mSelectedRouteController)
@@ -2797,14 +2802,10 @@
                     MediaRouteProvider.DynamicGroupRouteController controller =
                             route.getProviderInstance().onCreateDynamicGroupRouteController(
                                     route.mDescriptorId);
-                    // Note: Controller doesn't have a valid route id yet.
-                    // It will be informed with updated provider's route descriptors.
                     controller.setOnDynamicRoutesChangedListener(
                             ContextCompat.getMainExecutor(mApplicationContext),
                             mDynamicRoutesListener);
                     mSelectedRouteController = controller;
-                    // Select the initial member route for now. It is replaced with dynamic group
-                    // route once MRP publishes corresponding route descriptor.
                     mSelectedRoute = route;
                 } else {
                     mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
diff --git a/mediarouter/src/main/res/values-pa/strings.xml b/mediarouter/src/main/res/values-pa/strings.xml
index 71224a6..46a04e3 100644
--- a/mediarouter/src/main/res/values-pa/strings.xml
+++ b/mediarouter/src/main/res/values-pa/strings.xml
@@ -22,7 +22,7 @@
     <string name="mr_cast_button_disconnected" msgid="5501231066847739632">"\'ਕਾਸਟ ਕਰੋ\' ਬਟਨ। ਡਿਸਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="mr_cast_button_connecting" msgid="8959304318293841992">"\'ਕਾਸਟ ਕਰੋ\' ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="mr_cast_button_connected" msgid="1350095112462806159">"\'ਕਾਸਟ ਕਰੋ\' ਬਟਨ। ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
-    <string name="mr_chooser_title" msgid="7548226170787476564">"ਏਥੇ ਕਾਸਟ ਕਰੋ"</string>
+    <string name="mr_chooser_title" msgid="7548226170787476564">"ਇਸਦੇ ਨਾਲ ਕਾਸਟ ਕਰੋ"</string>
     <string name="mr_chooser_searching" msgid="5504553798429329689">"ਡੀਵਾਈਸ ਲੱਭੇ ਜਾ ਰਹੇ ਹਨ"</string>
     <string name="mr_controller_disconnect" msgid="1370654436555555647">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
     <string name="mr_controller_stop_casting" msgid="7617024847862349259">"ਕਾਸਟ ਕਰਨਾ ਬੰਦ ਕਰੋ"</string>
diff --git a/navigation/ui/src/main/res/values-bs/strings.xml b/navigation/ui/src/main/res/values-bs/strings.xml
index 39602a7..f4b93f0 100644
--- a/navigation/ui/src/main/res/values-bs/strings.xml
+++ b/navigation/ui/src/main/res/values-bs/strings.xml
@@ -17,6 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="nav_app_bar_open_drawer_description" msgid="7456070600745802113">"Otvaranje panela za navigaciju"</string>
+    <string name="nav_app_bar_open_drawer_description" msgid="7456070600745802113">"Otvaranje ladice za navigaciju"</string>
     <string name="nav_app_bar_navigate_up_description" msgid="6301633601645100427">"Idi gore"</string>
 </resources>
diff --git a/preference/api/res-1.1.0-alpha04.txt b/preference/api/res-1.1.0-alpha04.txt
index f55c31a..3afd07c 100644
--- a/preference/api/res-1.1.0-alpha04.txt
+++ b/preference/api/res-1.1.0-alpha04.txt
@@ -42,22 +42,15 @@
 attr orderingFromXml
 attr persistent
 attr positiveButtonText
-attr preferenceActivityStyle
 attr preferenceCategoryStyle
 attr preferenceCategoryTitleTextAppearance
 attr preferenceFragmentCompatStyle
 attr preferenceFragmentListStyle
-attr preferenceFragmentPaddingSide
 attr preferenceFragmentStyle
-attr preferenceHeaderPanelStyle
 attr preferenceInformationStyle
-attr preferenceLayoutChild
-attr preferenceListStyle
-attr preferencePanelStyle
 attr preferenceScreenStyle
 attr preferenceStyle
 attr preferenceTheme
-attr ringtonePreferenceStyle
 attr seekBarIncrement
 attr seekBarPreferenceStyle
 attr selectable
@@ -74,4 +67,3 @@
 attr switchTextOn
 attr title
 attr widgetLayout
-attr yesNoPreferenceStyle
diff --git a/preference/api/restricted_1.1.0-alpha04.ignore b/preference/api/restricted_1.1.0-alpha04.ignore
index b965d59..9a6d5b8 100644
--- a/preference/api/restricted_1.1.0-alpha04.ignore
+++ b/preference/api/restricted_1.1.0-alpha04.ignore
@@ -1,5 +1,23 @@
 // Baseline format: 1.0
+// See tools/metalava/API-LINT.md for how to update this file.
+
 RemovedClass: androidx.preference.internal.AbstractMultiSelectListPreference:
     Removed class androidx.preference.internal.AbstractMultiSelectListPreference
 
 
+RemovedField: androidx.preference.AndroidResources#ANDROID_R_EDITTEXT_PREFERENCE_STYLE:
+    Removed field androidx.preference.AndroidResources.ANDROID_R_EDITTEXT_PREFERENCE_STYLE
+RemovedField: androidx.preference.AndroidResources#ANDROID_R_LIST_CONTAINER:
+    Removed field androidx.preference.AndroidResources.ANDROID_R_LIST_CONTAINER
+RemovedField: androidx.preference.AndroidResources#ANDROID_R_PREFERENCE_FRAGMENT_STYLE:
+    Removed field androidx.preference.AndroidResources.ANDROID_R_PREFERENCE_FRAGMENT_STYLE
+RemovedField: androidx.preference.AndroidResources#ANDROID_R_SWITCH_WIDGET:
+    Removed field androidx.preference.AndroidResources.ANDROID_R_SWITCH_WIDGET
+
+
+RemovedMethod: androidx.preference.Preference#clearWasDetached():
+    Removed method androidx.preference.Preference.clearWasDetached()
+RemovedMethod: androidx.preference.Preference#wasDetached():
+    Removed method androidx.preference.Preference.wasDetached()
+
+
diff --git a/preference/api/restricted_1.1.0-alpha04.txt b/preference/api/restricted_1.1.0-alpha04.txt
index 51b04fa..43e5913 100644
--- a/preference/api/restricted_1.1.0-alpha04.txt
+++ b/preference/api/restricted_1.1.0-alpha04.txt
@@ -1,16 +1,7 @@
 // Signature format: 3.0
 package androidx.preference {
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AndroidResources {
-    field public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE = 16842898; // 0x1010092
     field public static final int ANDROID_R_ICON_FRAME = 16908350; // 0x102003e
-    field public static final int ANDROID_R_LIST_CONTAINER = 16908351; // 0x102003f
-    field public static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE = 16844038; // 0x1010506
-    field public static final int ANDROID_R_SWITCH_WIDGET = 16908352; // 0x1020040
-  }
-
-  public class DropDownPreference extends androidx.preference.ListPreference {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int findSpinnerIndexOfValue(String!);
   }
 
   public class Preference implements java.lang.Comparable<androidx.preference.Preference> {
@@ -20,17 +11,12 @@
   }
 
   @Deprecated public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected boolean needInputMethod();
   }
 
   public abstract class PreferenceDialogFragmentCompat extends androidx.fragment.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected boolean needInputMethod();
   }
 
   @Deprecated public abstract class PreferenceFragment extends android.app.Fragment implements androidx.preference.DialogPreference.TargetFragment androidx.preference.PreferenceManager.OnDisplayPreferenceDialogListener androidx.preference.PreferenceManager.OnNavigateToScreenListener androidx.preference.PreferenceManager.OnPreferenceTreeClickListener {
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.app.Fragment! getCallbackFragment();
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void onBindPreferences();
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void onUnbindPreferences();
   }
 
   public abstract class PreferenceFragmentCompat extends androidx.fragment.app.Fragment implements androidx.preference.DialogPreference.TargetFragment androidx.preference.PreferenceManager.OnDisplayPreferenceDialogListener androidx.preference.PreferenceManager.OnNavigateToScreenListener androidx.preference.PreferenceManager.OnPreferenceTreeClickListener {
@@ -41,7 +27,6 @@
 
   public abstract class PreferenceGroup extends androidx.preference.Preference {
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.preference.PreferenceGroup.OnExpandButtonClickListener? getOnExpandButtonClickListener();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isAttached();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setOnExpandButtonClickListener(androidx.preference.PreferenceGroup.OnExpandButtonClickListener?);
   }
 
@@ -80,7 +65,6 @@
   }
 
   public abstract class TwoStatePreference extends androidx.preference.Preference {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void syncSummaryView(android.view.View!);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class UnPressableLinearLayout extends android.widget.LinearLayout {
@@ -90,7 +74,6 @@
 
 }
 
-package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) androidx.preference.internal {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class PreferenceImageView extends android.widget.ImageView {
     ctor public PreferenceImageView(android.content.Context!);
diff --git a/preference/api/restricted_current.txt b/preference/api/restricted_current.txt
index 51b04fa..43e5913 100644
--- a/preference/api/restricted_current.txt
+++ b/preference/api/restricted_current.txt
@@ -1,16 +1,7 @@
 // Signature format: 3.0
 package androidx.preference {
 
-  @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class AndroidResources {
-    field public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE = 16842898; // 0x1010092
     field public static final int ANDROID_R_ICON_FRAME = 16908350; // 0x102003e
-    field public static final int ANDROID_R_LIST_CONTAINER = 16908351; // 0x102003f
-    field public static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE = 16844038; // 0x1010506
-    field public static final int ANDROID_R_SWITCH_WIDGET = 16908352; // 0x1020040
-  }
-
-  public class DropDownPreference extends androidx.preference.ListPreference {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public int findSpinnerIndexOfValue(String!);
   }
 
   public class Preference implements java.lang.Comparable<androidx.preference.Preference> {
@@ -20,17 +11,12 @@
   }
 
   @Deprecated public abstract class PreferenceDialogFragment extends android.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected boolean needInputMethod();
   }
 
   public abstract class PreferenceDialogFragmentCompat extends androidx.fragment.app.DialogFragment implements android.content.DialogInterface.OnClickListener {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected boolean needInputMethod();
   }
 
   @Deprecated public abstract class PreferenceFragment extends android.app.Fragment implements androidx.preference.DialogPreference.TargetFragment androidx.preference.PreferenceManager.OnDisplayPreferenceDialogListener androidx.preference.PreferenceManager.OnNavigateToScreenListener androidx.preference.PreferenceManager.OnPreferenceTreeClickListener {
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public android.app.Fragment! getCallbackFragment();
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void onBindPreferences();
-    method @Deprecated @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void onUnbindPreferences();
   }
 
   public abstract class PreferenceFragmentCompat extends androidx.fragment.app.Fragment implements androidx.preference.DialogPreference.TargetFragment androidx.preference.PreferenceManager.OnDisplayPreferenceDialogListener androidx.preference.PreferenceManager.OnNavigateToScreenListener androidx.preference.PreferenceManager.OnPreferenceTreeClickListener {
@@ -41,7 +27,6 @@
 
   public abstract class PreferenceGroup extends androidx.preference.Preference {
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public androidx.preference.PreferenceGroup.OnExpandButtonClickListener? getOnExpandButtonClickListener();
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public boolean isAttached();
     method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public void setOnExpandButtonClickListener(androidx.preference.PreferenceGroup.OnExpandButtonClickListener?);
   }
 
@@ -80,7 +65,6 @@
   }
 
   public abstract class TwoStatePreference extends androidx.preference.Preference {
-    method @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) protected void syncSummaryView(android.view.View!);
   }
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class UnPressableLinearLayout extends android.widget.LinearLayout {
@@ -90,7 +74,6 @@
 
 }
 
-package @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) androidx.preference.internal {
 
   @RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX) public class PreferenceImageView extends android.widget.ImageView {
     ctor public PreferenceImageView(android.content.Context!);
diff --git a/preference/build.gradle b/preference/build.gradle
index 3eed07a..902a866 100644
--- a/preference/build.gradle
+++ b/preference/build.gradle
@@ -20,11 +20,14 @@
 
 plugins {
     id("SupportAndroidLibraryPlugin")
+    id("kotlin-android")
 }
 
 dependencies {
-    api(project(":annotation"))
+    // TODO: change to alpha02 after release
+    implementation(project(":annotation"))
     api("androidx.appcompat:appcompat:1.1.0-alpha02")
+    // TODO: change to alpha05 after release
     api(project(":core"))
     implementation("androidx.collection:collection:1.0.0")
     api("androidx.fragment:fragment:1.1.0-alpha04")
@@ -37,6 +40,7 @@
     androidTestImplementation(ESPRESSO_CORE, libs.exclude_for_espresso)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy) // DexMaker has it"s own MockMaker
+    androidTestImplementation(KOTLIN_STDLIB)
 }
 
 android {
diff --git a/preference/res-public/values/public_attrs.xml b/preference/res-public/values/public_attrs.xml
index a12c6cc..ed1d2f9 100644
--- a/preference/res-public/values/public_attrs.xml
+++ b/preference/res-public/values/public_attrs.xml
@@ -47,22 +47,15 @@
     <public type="attr" name="orderingFromXml" />
     <public type="attr" name="persistent" />
     <public type="attr" name="positiveButtonText" />
-    <public type="attr" name="preferenceActivityStyle" />
     <public type="attr" name="preferenceCategoryStyle" />
     <public type="attr" name="preferenceCategoryTitleTextAppearance" />
     <public type="attr" name="preferenceFragmentCompatStyle" />
     <public type="attr" name="preferenceFragmentListStyle" />
-    <public type="attr" name="preferenceFragmentPaddingSide" />
     <public type="attr" name="preferenceFragmentStyle" />
-    <public type="attr" name="preferenceHeaderPanelStyle" />
     <public type="attr" name="preferenceInformationStyle" />
-    <public type="attr" name="preferenceLayoutChild" />
-    <public type="attr" name="preferenceListStyle" />
-    <public type="attr" name="preferencePanelStyle" />
     <public type="attr" name="preferenceScreenStyle" />
     <public type="attr" name="preferenceStyle" />
     <public type="attr" name="preferenceTheme" />
-    <public type="attr" name="ringtonePreferenceStyle" />
     <public type="attr" name="seekBarIncrement" />
     <public type="attr" name="seekBarPreferenceStyle" />
     <public type="attr" name="selectable" />
@@ -79,5 +72,4 @@
     <public type="attr" name="switchTextOn" />
     <public type="attr" name="title" />
     <public type="attr" name="widgetLayout" />
-    <public type="attr" name="yesNoPreferenceStyle" />
 </resources>
diff --git a/preference/res/layout/preference_widget_seekbar_material.xml b/preference/res/layout/preference_widget_seekbar_material.xml
index 47a48f6..25872c1 100644
--- a/preference/res/layout/preference_widget_seekbar_material.xml
+++ b/preference/res/layout/preference_widget_seekbar_material.xml
@@ -86,7 +86,10 @@
             increased touch area so you do not need to exactly tap the thumb to move it. However,
             setting the Seekbar height directly causes the thumb and seekbar to be misaligned on
             API 22 and 23 - so instead we just set 15dp padding above and below, to account for the
-            2dp height of the track, and the 8dp margin above and below the whole Preference. -->
+            18dp default height of the Seekbar thumb for a total of 48dp.
+            Note: we set 0dp padding at the start and end of this seekbar to allow it to properly
+            fit into the layout, but this means that there's no leeway on either side for touch
+            input - this might be something we should reconsider down the line. -->
             <SeekBar
                 android:id="@+id/seekbar"
                 android:layout_width="0dp"
@@ -98,8 +101,6 @@
                 android:paddingEnd="@dimen/preference_seekbar_padding_horizontal"
                 android:paddingTop="@dimen/preference_seekbar_padding_vertical"
                 android:paddingBottom="@dimen/preference_seekbar_padding_vertical"
-                android:focusable="false"
-                android:clickable="false"
                 android:background="@null"/>
 
             <!-- If the value is shown, we reserve a minimum width of 36dp to allow for consistent
diff --git a/preference/res/values/attrs.xml b/preference/res/values/attrs.xml
index 50aac79..63540d5 100644
--- a/preference/res/values/attrs.xml
+++ b/preference/res/values/attrs.xml
@@ -33,8 +33,6 @@
 
         <!-- Default style for PreferenceScreen. -->
         <attr name="preferenceScreenStyle" format="reference" />
-        <!-- Default style for the PreferenceActivity. -->
-        <attr name="preferenceActivityStyle" format="reference" />
         <!-- Default style for Headers pane in PreferenceActivity. -->
         <attr name="preferenceFragmentStyle" format="reference" />
         <!-- Default style for Headers pane in PreferenceActivity. -->
@@ -47,28 +45,14 @@
         <attr name="preferenceInformationStyle" format="reference" />
         <!-- Default style for CheckBoxPreference. -->
         <attr name="checkBoxPreferenceStyle" format="reference" />
-        <!-- Default style for YesNoPreference. -->
-        <attr name="yesNoPreferenceStyle" format="reference" />
         <!-- Default style for DialogPreference. -->
         <attr name="dialogPreferenceStyle" format="reference" />
         <!-- Default style for EditTextPreference. -->
         <attr name="editTextPreferenceStyle" format="reference" />
-        <!-- Default style for RingtonePreference. -->
-        <attr name="ringtonePreferenceStyle" format="reference" />
         <!-- Default style for DropDownPreference. -->
         <attr name="dropdownPreferenceStyle" format="reference" />
-        <!-- The preference layout that has the child/tabbed effect. -->
-        <attr name="preferenceLayoutChild" format="reference" />
-        <!-- Preference panel style -->
-        <attr name="preferencePanelStyle" format="reference" />
-        <!-- Preference headers panel style -->
-        <attr name="preferenceHeaderPanelStyle" format="reference" />
-        <!-- Preference list style -->
-        <attr name="preferenceListStyle" format="reference" />
         <!-- Preference fragment list style -->
         <attr name="preferenceFragmentListStyle" format="reference" />
-        <!-- Preference fragment padding side -->
-        <attr name="preferenceFragmentPaddingSide" format="dimension" />
         <!-- Default style for switch preferences. -->
         <attr name="switchPreferenceStyle" format="reference" />
         <!-- Default style for switch compat preferences. -->
diff --git a/preference/src/androidTest/java/androidx/preference/tests/SeekBarPreferenceTest.kt b/preference/src/androidTest/java/androidx/preference/tests/SeekBarPreferenceTest.kt
new file mode 100644
index 0000000..1e230d3
--- /dev/null
+++ b/preference/src/androidTest/java/androidx/preference/tests/SeekBarPreferenceTest.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2019 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 androidx.preference.tests
+
+import android.util.DisplayMetrics
+import android.view.View
+import android.widget.SeekBar
+import androidx.preference.SeekBarPreference
+import androidx.preference.test.R
+import androidx.preference.tests.helpers.PreferenceTestHelperActivity
+import androidx.test.annotation.UiThreadTest
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.ViewAction
+import androidx.test.espresso.action.CoordinatesProvider
+import androidx.test.espresso.action.GeneralSwipeAction
+import androidx.test.espresso.action.Press
+import androidx.test.espresso.action.Swipe
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.BoundedMatcher
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.rule.ActivityTestRule
+import org.hamcrest.Description
+import org.hamcrest.Matchers.allOf
+import org.hamcrest.Matchers.not
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test for [androidx.preference.SeekBarPreference].
+ */
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class SeekBarPreferenceTest {
+
+    @get:Rule
+    val activityRule = ActivityTestRule(PreferenceTestHelperActivity::class.java)
+
+    private lateinit var seekBarPreference: SeekBarPreference
+
+    @Before
+    @UiThreadTest
+    fun setUp() {
+        val fragment = activityRule.activity.setupPreferenceHierarchy(
+            R.xml.test_seekbar
+        )
+        seekBarPreference = fragment.preferenceScreen.getPreference(0) as SeekBarPreference
+        seekBarPreference.min = 0
+        seekBarPreference.max = 5
+    }
+
+    @Test
+    fun testSetValue() {
+        activityRule.runOnUiThread {
+            // When a value of 3 is set
+            seekBarPreference.value = 3
+            // The internal value should be set to 3
+            assertEquals(3, seekBarPreference.value)
+        }
+        // The seekbar's progress should be set to 3
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(3)))
+    }
+
+    @Test
+    fun testSetValue_belowMinValue() {
+        activityRule.runOnUiThread {
+            // Given a non-zero min value
+            seekBarPreference.min = 3
+            // When a value lower than the min value is set
+            seekBarPreference.value = 0
+            // The actual value set should be equal to the min value
+            assertEquals(3, seekBarPreference.value)
+        }
+        // The seekbar's progress should be 0, as we are currently at the min value
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
+    }
+
+    @Test
+    fun testSetValue_aboveMaxValue() {
+        activityRule.runOnUiThread {
+            // Given a max value
+            seekBarPreference.max = 7
+            // When a value higher than the max value is set
+            seekBarPreference.value = 10
+            // The actual value set should be equal to the max value
+            assertEquals(7, seekBarPreference.value)
+        }
+        // The seekbar's progress should be equal to the max value
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(7)))
+    }
+
+    @Test
+    fun testSeekBarLabel_updatesWhenValueIsChanged() {
+        // By default only the seekbar, not the label should be visible
+        onView(withId(R.id.seekbar)).check(matches(isDisplayed()))
+        onView(withId(R.id.seekbar_value)).check(matches(not(isDisplayed())))
+
+        // When we enable showing the value
+        activityRule.runOnUiThread { seekBarPreference.showSeekBarValue = true }
+
+        // Both the seekbar and the label should be visible
+        onView(withId(R.id.seekbar)).check(matches(isDisplayed()))
+        onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
+
+        // When we change the value
+        activityRule.runOnUiThread { seekBarPreference.value = 5 }
+
+        // The label should update to show the new value
+        onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
+    }
+
+    @Test
+    fun testSeekBarLabel_updatesWhenSeekbarProgressChanges() {
+        activityRule.runOnUiThread { seekBarPreference.showSeekBarValue = true }
+        onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
+
+        // When the seekbar's progress changes
+        activityRule.activity.findViewById<SeekBar>(R.id.seekbar).progress = 5
+
+        // The label should update to show the new value
+        onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
+    }
+
+    @Test
+    fun testSeekBarLabel_shouldCorrectlyHandlesMinValue() {
+        activityRule.runOnUiThread {
+            // Set the minimum and the saved value to 3
+            seekBarPreference.min = 3
+            seekBarPreference.value = 3
+            seekBarPreference.showSeekBarValue = true
+        }
+
+        // The label should display the saved value
+        onView(allOf(withId(R.id.seekbar_value), withText("3"))).check(matches(isDisplayed()))
+
+        // Since the value is also the minimum, the seekbar should have no progress
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
+
+        // Change the value to 5
+        activityRule.runOnUiThread { seekBarPreference.value = 5 }
+
+        // The label should display the saved value
+        onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
+
+        // However since the minimum is 3, the seekbar's progress should be offset by 3, and should
+        // only have progressed to 2 (the current value - the minimum)
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(2)))
+    }
+
+    @Test
+    fun testSeekBarPreferenceChangeListener() {
+        // How many times the change listener has been called
+        var updateCount = 0
+        activityRule.runOnUiThread {
+            seekBarPreference.value = 0
+            seekBarPreference.showSeekBarValue = true
+            seekBarPreference.updatesContinuously = false
+            seekBarPreference.setOnPreferenceChangeListener { _, _ ->
+                updateCount++
+                true
+            }
+        }
+
+        onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
+
+        // Fully drag the seekbar from left to right
+        onView(withId(R.id.seekbar)).perform(dragSeekBar())
+
+        // The current value should be 5
+        onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(5)))
+
+        activityRule.runOnUiThread {
+            // SeekBarPreference should only attempt to update once, when the drag has stopped
+            assertEquals(1, updateCount)
+            assertEquals(5, seekBarPreference.value)
+        }
+    }
+
+    @Test
+    fun testSeekBarPreferenceChangeListenerWithContinuousUpdates() {
+        // How many times the change listener has been called
+        var updateCount = 0
+        activityRule.runOnUiThread {
+            seekBarPreference.value = 0
+            seekBarPreference.max = 5
+            seekBarPreference.showSeekBarValue = true
+            seekBarPreference.updatesContinuously = true
+            seekBarPreference.setOnPreferenceChangeListener { _, _ ->
+                updateCount++
+                true
+            }
+        }
+
+        onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
+
+        // Fully drag the seekbar from left to right
+        onView(withId(R.id.seekbar)).perform(dragSeekBar())
+
+        // The current value should be 5
+        onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
+        onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(5)))
+
+        activityRule.runOnUiThread {
+            // SeekBarPreference should attempt to update every time the seekbar's progress changed.
+            // From 0-5, it should update 5 times.
+            assertEquals(5, updateCount)
+            assertEquals(5, seekBarPreference.value)
+        }
+    }
+
+    /**
+     * A [ViewAction] that drags a [SeekBar] from its left edge to the right edge of the screen
+     */
+    private fun dragSeekBar(): ViewAction {
+        return GeneralSwipeAction(Swipe.FAST,
+            CoordinatesProvider { view ->
+                val location = IntArray(2)
+                view.getLocationOnScreen(location)
+                val posX = location[0]
+                val posY = location[1]
+                // Start at the beginning of the seekbar
+                floatArrayOf(posX.toFloat(), posY.toFloat())
+            }, CoordinatesProvider { view ->
+                val location = IntArray(2)
+                view.getLocationOnScreen(location)
+                // We want to swipe all the way to the right edge of the screen to avoid
+                // flakiness due to sometimes not reaching the end of the seekbar
+                val metrics = DisplayMetrics()
+                activityRule.activity.windowManager.defaultDisplay.getMetrics(metrics)
+                val posX = metrics.widthPixels
+                val posY = location[1]
+                floatArrayOf(posX.toFloat(), posY.toFloat())
+            }, Press.PINPOINT
+        )
+    }
+
+    /**
+     * A matcher to assert the current progress of a [SeekBar]
+     *
+     * @param progress The expected progress of the [SeekBar]
+     */
+    private fun withSeekBarProgress(progress: Int): BoundedMatcher<View, SeekBar> {
+        return object : BoundedMatcher<View, SeekBar>(SeekBar::class.java) {
+            override fun matchesSafely(item: SeekBar?): Boolean {
+                return item!!.progress == progress
+            }
+
+            override fun describeTo(description: Description?) {
+                description?.appendText("with SeekBar progress:")
+                    ?.appendValue(progress)
+            }
+        }
+    }
+}
diff --git a/preference/src/androidTest/res/xml/test_seekbar.xml b/preference/src/androidTest/res/xml/test_seekbar.xml
new file mode 100644
index 0000000..9fa7050
--- /dev/null
+++ b/preference/src/androidTest/res/xml/test_seekbar.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 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.
+  -->
+<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
+    <SeekBarPreference app:title="SeekBarPreference"/>
+</PreferenceScreen>
diff --git a/preference/src/main/java/androidx/preference/AndroidResources.java b/preference/src/main/java/androidx/preference/AndroidResources.java
index 8e12be3..18f3003 100644
--- a/preference/src/main/java/androidx/preference/AndroidResources.java
+++ b/preference/src/main/java/androidx/preference/AndroidResources.java
@@ -16,23 +16,24 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.annotation.SuppressLint;
 
 import androidx.annotation.RestrictTo;
 
 /**
+ * Utility class for attributes unavailable on older APIs
  * @hide
  */
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(LIBRARY)
+@SuppressLint("InlinedApi")
 public class AndroidResources {
 
     public static final int ANDROID_R_ICON_FRAME = android.R.id.icon_frame;
-    public static final int ANDROID_R_LIST_CONTAINER = android.R.id.list_container;
-    public static final int ANDROID_R_SWITCH_WIDGET = android.R.id.switch_widget;
-    public static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE
-            = android.R.attr.preferenceFragmentStyle;
-    public static final int ANDROID_R_EDITTEXT_PREFERENCE_STYLE
-            = android.R.attr.editTextPreferenceStyle;
+    static final int ANDROID_R_LIST_CONTAINER = android.R.id.list_container;
+    static final int ANDROID_R_SWITCH_WIDGET = android.R.id.switch_widget;
+    static final int ANDROID_R_PREFERENCE_FRAGMENT_STYLE = android.R.attr.preferenceFragmentStyle;
 
     private AndroidResources() {}
 }
diff --git a/preference/src/main/java/androidx/preference/CheckBoxPreference.java b/preference/src/main/java/androidx/preference/CheckBoxPreference.java
index bfd13c4..11168ec 100644
--- a/preference/src/main/java/androidx/preference/CheckBoxPreference.java
+++ b/preference/src/main/java/androidx/preference/CheckBoxPreference.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -84,7 +84,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Override
     protected void performClick(View view) {
         super.performClick(view);
diff --git a/preference/src/main/java/androidx/preference/DropDownPreference.java b/preference/src/main/java/androidx/preference/DropDownPreference.java
index ad9fbf5..45d0357 100644
--- a/preference/src/main/java/androidx/preference/DropDownPreference.java
+++ b/preference/src/main/java/androidx/preference/DropDownPreference.java
@@ -16,8 +16,6 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
-
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -27,7 +25,6 @@
 import android.widget.Spinner;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
 
 /**
  * A {@link ListPreference} that presents the options in a drop down menu rather than a dialog.
@@ -116,22 +113,6 @@
         setValue(getEntryValues()[index].toString());
     }
 
-    /**
-     * @hide
-     */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
-    public int findSpinnerIndexOfValue(String value) {
-        CharSequence[] entryValues = getEntryValues();
-        if (value != null && entryValues != null) {
-            for (int i = entryValues.length - 1; i >= 0; i--) {
-                if (entryValues[i].equals(value)) {
-                    return i;
-                }
-            }
-        }
-        return Spinner.INVALID_POSITION;
-    }
-
     @Override
     protected void notifyChanged() {
         super.notifyChanged();
@@ -150,5 +131,17 @@
         mSpinner.setSelection(findSpinnerIndexOfValue(getValue()));
         super.onBindViewHolder(view);
     }
+
+    private int findSpinnerIndexOfValue(String value) {
+        CharSequence[] entryValues = getEntryValues();
+        if (value != null && entryValues != null) {
+            for (int i = entryValues.length - 1; i >= 0; i--) {
+                if (entryValues[i].equals(value)) {
+                    return i;
+                }
+            }
+        }
+        return Spinner.INVALID_POSITION;
+    }
 }
 
diff --git a/preference/src/main/java/androidx/preference/EditTextPreference.java b/preference/src/main/java/androidx/preference/EditTextPreference.java
index 7836c4b..b597f0c 100644
--- a/preference/src/main/java/androidx/preference/EditTextPreference.java
+++ b/preference/src/main/java/androidx/preference/EditTextPreference.java
@@ -60,7 +60,7 @@
 
     public EditTextPreference(Context context, AttributeSet attrs) {
         this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.editTextPreferenceStyle,
-                AndroidResources.ANDROID_R_EDITTEXT_PREFERENCE_STYLE));
+                android.R.attr.editTextPreferenceStyle));
     }
 
     public EditTextPreference(Context context) {
diff --git a/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragment.java b/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragment.java
index 8deaabb..016e361 100644
--- a/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragment.java
+++ b/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragment.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.os.Bundle;
 import android.view.View;
@@ -94,7 +94,7 @@
     }
 
     /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Override
     protected boolean needInputMethod() {
         // We want the input method to show, if possible, when dialog is displayed
diff --git a/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragmentCompat.java b/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragmentCompat.java
index 6e197f3..b926000 100644
--- a/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragmentCompat.java
+++ b/preference/src/main/java/androidx/preference/EditTextPreferenceDialogFragmentCompat.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.os.Bundle;
 import android.view.View;
@@ -83,7 +83,7 @@
     }
 
     /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Override
     protected boolean needInputMethod() {
         // We want the input method to show, if possible, when dialog is displayed
diff --git a/preference/src/main/java/androidx/preference/ExpandButton.java b/preference/src/main/java/androidx/preference/ExpandButton.java
index 1e185a7..b2a6b57 100644
--- a/preference/src/main/java/androidx/preference/ExpandButton.java
+++ b/preference/src/main/java/androidx/preference/ExpandButton.java
@@ -16,13 +16,9 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-
 import android.content.Context;
 import android.text.TextUtils;
 
-import androidx.annotation.RestrictTo;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -32,7 +28,6 @@
  *
  * @hide
  */
-@RestrictTo(LIBRARY)
 final class ExpandButton extends Preference {
     private long mId;
 
diff --git a/preference/src/main/java/androidx/preference/Preference.java b/preference/src/main/java/androidx/preference/Preference.java
index 8ac775d..9530747 100644
--- a/preference/src/main/java/androidx/preference/Preference.java
+++ b/preference/src/main/java/androidx/preference/Preference.java
@@ -16,7 +16,6 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.content.ClipData;
@@ -1141,6 +1140,7 @@
     }
 
     /**
+     * Used by Settings.
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
@@ -1151,6 +1151,7 @@
     /**
      * Called when a click should be performed.
      *
+     * Used by Settings.
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
@@ -1295,6 +1296,8 @@
     /**
      * Called from {@link PreferenceGroup} to pass in an ID for reuse.
      *
+     * Used by Settings.
+     *
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
@@ -1349,21 +1352,15 @@
     /**
      * Returns true if {@link #onDetached()} was called. Used for handling the case when a
      * preference was removed, modified, and re-added to a {@link PreferenceGroup}.
-     *
-     * @hide
      */
-    @RestrictTo(LIBRARY)
-    public final boolean wasDetached() {
+    final boolean wasDetached() {
         return mWasDetached;
     }
 
     /**
      * Clears the {@link #wasDetached()} status.
-     *
-     * @hide
      */
-    @RestrictTo(LIBRARY)
-    public final void clearWasDetached() {
+    final void clearWasDetached() {
         mWasDetached = false;
     }
 
diff --git a/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java b/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
index ca73bd2..d5ce88f 100644
--- a/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
+++ b/preference/src/main/java/androidx/preference/PreferenceDialogFragment.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -217,7 +217,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     protected boolean needInputMethod() {
         return false;
     }
diff --git a/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java b/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
index c5ceb93..8877f1b 100644
--- a/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
+++ b/preference/src/main/java/androidx/preference/PreferenceDialogFragmentCompat.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.app.Dialog;
 import android.content.Context;
@@ -198,7 +198,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     protected boolean needInputMethod() {
         return false;
     }
diff --git a/preference/src/main/java/androidx/preference/PreferenceFragment.java b/preference/src/main/java/androidx/preference/PreferenceFragment.java
index 871bde9..50ab41d 100644
--- a/preference/src/main/java/androidx/preference/PreferenceFragment.java
+++ b/preference/src/main/java/androidx/preference/PreferenceFragment.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.app.DialogFragment;
 import android.app.Fragment;
@@ -514,11 +514,11 @@
     }
 
     /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     protected void onBindPreferences() {}
 
     /** @hide */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     protected void onUnbindPreferences() {}
 
     /**
@@ -645,7 +645,7 @@
      * @return The {@link Fragment} to possibly use as a callback
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public Fragment getCallbackFragment() {
         return null;
     }
diff --git a/preference/src/main/java/androidx/preference/PreferenceFragmentCompat.java b/preference/src/main/java/androidx/preference/PreferenceFragmentCompat.java
index cd12ba6..f490c55 100644
--- a/preference/src/main/java/androidx/preference/PreferenceFragmentCompat.java
+++ b/preference/src/main/java/androidx/preference/PreferenceFragmentCompat.java
@@ -495,11 +495,17 @@
         onUnbindPreferences();
     }
 
-    /** @hide */
+    /**
+     * Used by Settings.
+     * @hide
+     */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     protected void onBindPreferences() {}
 
-    /** @hide */
+    /**
+     * Used by Settings.
+     * @hide
+     */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
     protected void onUnbindPreferences() {}
 
diff --git a/preference/src/main/java/androidx/preference/PreferenceGroup.java b/preference/src/main/java/androidx/preference/PreferenceGroup.java
index 491c52b..90850b5 100644
--- a/preference/src/main/java/androidx/preference/PreferenceGroup.java
+++ b/preference/src/main/java/androidx/preference/PreferenceGroup.java
@@ -16,6 +16,7 @@
 
 package androidx.preference;
 
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
 
 import android.content.Context;
@@ -404,7 +405,7 @@
      *
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     public boolean isAttached() {
         return mAttachedToHierarchy;
     }
@@ -412,6 +413,8 @@
     /**
      * Sets the callback to be invoked when the expand button is clicked.
      *
+     * Used by Settings.
+     *
      * @param onExpandButtonClickListener The callback to be invoked
      * @see #setInitialExpandedChildrenCount(int)
      * @hide
@@ -425,6 +428,8 @@
     /**
      * Returns the callback to be invoked when the expand button is clicked.
      *
+     * Used by Settings.
+     *
      * @return The callback to be invoked when the expand button is clicked.
      * @hide
      */
@@ -550,6 +555,9 @@
 
     /**
      * Definition for a callback to be invoked when the expand button is clicked.
+     *
+     * Used by Settings.
+     *
      * @see #setInitialExpandedChildrenCount(int)
      * @hide
      */
diff --git a/preference/src/main/java/androidx/preference/PreferenceGroupAdapter.java b/preference/src/main/java/androidx/preference/PreferenceGroupAdapter.java
index 6cb7178..9509103 100644
--- a/preference/src/main/java/androidx/preference/PreferenceGroupAdapter.java
+++ b/preference/src/main/java/androidx/preference/PreferenceGroupAdapter.java
@@ -40,6 +40,8 @@
  * An adapter that connects a {@link RecyclerView} to the {@link Preference}s contained in
  * an associated {@link PreferenceGroup}.
  *
+ * Used by Settings.
+ *
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
diff --git a/preference/src/main/java/androidx/preference/PreferenceManager.java b/preference/src/main/java/androidx/preference/PreferenceManager.java
index 7ce4b59..23205d7a 100644
--- a/preference/src/main/java/androidx/preference/PreferenceManager.java
+++ b/preference/src/main/java/androidx/preference/PreferenceManager.java
@@ -96,6 +96,8 @@
     private OnNavigateToScreenListener mOnNavigateToScreenListener;
 
     /**
+     * Used by Settings.
+     *
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
@@ -195,6 +197,8 @@
      * Inflates a preference hierarchy from XML. If a preference hierarchy is given, the new
      * preference hierarchies will be merged in.
      *
+     * Used by Settings.
+     *
      * @param context         The context of the resource
      * @param resId           The resource ID of the XML to inflate
      * @param rootPreferences Optional existing hierarchy to merge the new
diff --git a/preference/src/main/java/androidx/preference/PreferenceRecyclerViewAccessibilityDelegate.java b/preference/src/main/java/androidx/preference/PreferenceRecyclerViewAccessibilityDelegate.java
index 973c0ac..d5847c3 100644
--- a/preference/src/main/java/androidx/preference/PreferenceRecyclerViewAccessibilityDelegate.java
+++ b/preference/src/main/java/androidx/preference/PreferenceRecyclerViewAccessibilityDelegate.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.core.view.AccessibilityDelegateCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -31,12 +32,16 @@
  * The accessibility delegate used by the {@link RecyclerView} that displays views for
  * {@link Preference}s.
  *
+ * Used by Leanback.
+ *
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
 public class PreferenceRecyclerViewAccessibilityDelegate
         extends RecyclerViewAccessibilityDelegate {
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final RecyclerView mRecyclerView;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final AccessibilityDelegateCompat mDefaultItemDelegate = super.getItemDelegate();
 
     public PreferenceRecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
@@ -44,11 +49,12 @@
         mRecyclerView = recyclerView;
     }
 
+    @NonNull
     @Override
     public AccessibilityDelegateCompat getItemDelegate() {
         return mItemDelegate;
     }
-
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     final AccessibilityDelegateCompat mItemDelegate = new AccessibilityDelegateCompat() {
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
diff --git a/preference/src/main/java/androidx/preference/PreferenceScreen.java b/preference/src/main/java/androidx/preference/PreferenceScreen.java
index b8581e8..ba01728 100644
--- a/preference/src/main/java/androidx/preference/PreferenceScreen.java
+++ b/preference/src/main/java/androidx/preference/PreferenceScreen.java
@@ -44,6 +44,8 @@
     /**
      * Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}.
      *
+     * Used by Settings :)
+     *
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP_PREFIX)
diff --git a/preference/src/main/java/androidx/preference/SeekBarPreference.java b/preference/src/main/java/androidx/preference/SeekBarPreference.java
index 6be1b40..1e1471a 100644
--- a/preference/src/main/java/androidx/preference/SeekBarPreference.java
+++ b/preference/src/main/java/androidx/preference/SeekBarPreference.java
@@ -76,6 +76,9 @@
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
             if (fromUser && (mUpdatesContinuously || !mTrackingTouch)) {
                 syncValueInternal(seekBar);
+            } else {
+                // We always want to update the text while the seekbar is being dragged
+                updateLabelValue(progress + mMin);
             }
         }
 
@@ -187,9 +190,7 @@
         }
 
         mSeekBar.setProgress(mSeekBarValue - mMin);
-        if (mSeekBarValueTextView != null) {
-            mSeekBarValueTextView.setText(String.valueOf(mSeekBarValue));
-        }
+        updateLabelValue(mSeekBarValue);
         mSeekBar.setEnabled(isEnabled());
     }
 
@@ -300,12 +301,11 @@
     /**
      * Gets whether the {@link SeekBarPreference} should continuously save the {@link SeekBar} value
      * while it is being dragged. Note that when the value is true,
-     * {@link Preference#OnPreferenceChangeListener} will be called continuously as well.
-     *
-     * {@see #setUpdatesContinuously()}
+     * {@link Preference.OnPreferenceChangeListener} will be called continuously as well.
      *
      * @return Whether the {@link SeekBarPreference} should continuously save the {@link SeekBar}
      * value while it is being dragged
+     * @see #setUpdatesContinuously(boolean)
      */
     public boolean getUpdatesContinuously() {
         return mUpdatesContinuously;
@@ -315,10 +315,9 @@
      * Sets whether the {@link SeekBarPreference} should continuously save the {@link SeekBar} value
      * while it is being dragged.
      *
-     * {@see #getUpdatesContinuously()}
-     *
      * @param updatesContinuously Whether the {@link SeekBarPreference} should continuously save
-     *                           the {@link SeekBar} value while it is being dragged
+     *                            the {@link SeekBar} value while it is being dragged
+     * @see #getUpdatesContinuously()
      */
     public void setUpdatesContinuously(boolean updatesContinuously) {
         mUpdatesContinuously = updatesContinuously;
@@ -327,9 +326,8 @@
     /**
      * Gets whether the current {@link SeekBar} value is displayed to the user.
      *
-     * {@see #setShowSeekBarValue()}
-     *
      * @return Whether the current {@link SeekBar} value is displayed to the user
+     * @see #setShowSeekBarValue(boolean)
      */
     public boolean getShowSeekBarValue() {
         return mShowSeekBarValue;
@@ -338,9 +336,8 @@
     /**
      * Sets whether the current {@link SeekBar} value is displayed to the user.
      *
-     * {@see #getShowSeekBarValue()}
-     *
      * @param showSeekBarValue Whether the current {@link SeekBar} value is displayed to the user
+     * @see #getShowSeekBarValue()
      */
     public void setShowSeekBarValue(boolean showSeekBarValue) {
         mShowSeekBarValue = showSeekBarValue;
@@ -357,9 +354,7 @@
 
         if (seekBarValue != mSeekBarValue) {
             mSeekBarValue = seekBarValue;
-            if (mSeekBarValueTextView != null) {
-                mSeekBarValueTextView.setText(String.valueOf(mSeekBarValue));
-            }
+            updateLabelValue(mSeekBarValue);
             persistInt(seekBarValue);
             if (notifyChanged) {
                 notifyChanged();
@@ -397,10 +392,23 @@
                 setValueInternal(seekBarValue, false);
             } else {
                 seekBar.setProgress(mSeekBarValue - mMin);
+                updateLabelValue(mSeekBarValue);
             }
         }
     }
 
+    /**
+     * Attempts to update the TextView label that displays the current value.
+     *
+     * @param value the value to display next to the {@link SeekBar}
+     */
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    void updateLabelValue(int value) {
+        if (mSeekBarValueTextView != null) {
+            mSeekBarValueTextView.setText(String.valueOf(value));
+        }
+    }
+
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
diff --git a/preference/src/main/java/androidx/preference/SwitchPreference.java b/preference/src/main/java/androidx/preference/SwitchPreference.java
index d668d1c..0c94255 100644
--- a/preference/src/main/java/androidx/preference/SwitchPreference.java
+++ b/preference/src/main/java/androidx/preference/SwitchPreference.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -189,7 +189,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Override
     protected void performClick(View view) {
         super.performClick(view);
diff --git a/preference/src/main/java/androidx/preference/SwitchPreferenceCompat.java b/preference/src/main/java/androidx/preference/SwitchPreferenceCompat.java
index e150346..11318dd 100644
--- a/preference/src/main/java/androidx/preference/SwitchPreferenceCompat.java
+++ b/preference/src/main/java/androidx/preference/SwitchPreferenceCompat.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -187,7 +187,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     @Override
     protected void performClick(View view) {
         super.performClick(view);
diff --git a/preference/src/main/java/androidx/preference/TwoStatePreference.java b/preference/src/main/java/androidx/preference/TwoStatePreference.java
index 84a43fd..f1e4a59 100644
--- a/preference/src/main/java/androidx/preference/TwoStatePreference.java
+++ b/preference/src/main/java/androidx/preference/TwoStatePreference.java
@@ -16,7 +16,7 @@
 
 package androidx.preference;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -215,7 +215,7 @@
     /**
      * @hide
      */
-    @RestrictTo(LIBRARY_GROUP_PREFIX)
+    @RestrictTo(LIBRARY)
     protected void syncSummaryView(View view) {
         if (!(view instanceof TextView)) {
             return;
diff --git a/preference/src/main/java/androidx/preference/UnPressableLinearLayout.java b/preference/src/main/java/androidx/preference/UnPressableLinearLayout.java
index 2a8d4fb..75eba63 100644
--- a/preference/src/main/java/androidx/preference/UnPressableLinearLayout.java
+++ b/preference/src/main/java/androidx/preference/UnPressableLinearLayout.java
@@ -29,6 +29,8 @@
  * By default, the pressed state is propagated to all the children that are not clickable
  * or long-clickable.
  *
+ * Used by Leanback and Car.
+ *
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
diff --git a/preference/src/main/java/androidx/preference/internal/PreferenceImageView.java b/preference/src/main/java/androidx/preference/internal/PreferenceImageView.java
index 3e7c445..12c5758 100644
--- a/preference/src/main/java/androidx/preference/internal/PreferenceImageView.java
+++ b/preference/src/main/java/androidx/preference/internal/PreferenceImageView.java
@@ -30,6 +30,8 @@
 /**
  * Extension of {@link ImageView} that correctly applies maxWidth and maxHeight.
  *
+ * Used by Car.
+ *
  * @hide
  */
 @RestrictTo(LIBRARY_GROUP_PREFIX)
diff --git a/preference/src/main/java/androidx/preference/internal/package-info.java b/preference/src/main/java/androidx/preference/internal/package-info.java
index 70347da..9b46f97 100644
--- a/preference/src/main/java/androidx/preference/internal/package-info.java
+++ b/preference/src/main/java/androidx/preference/internal/package-info.java
@@ -17,9 +17,9 @@
 /**
  * @hide
  */
-@RestrictTo(LIBRARY_GROUP_PREFIX)
+@RestrictTo(LIBRARY)
 package androidx.preference.internal;
 
-import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
 
 import androidx.annotation.RestrictTo;
\ No newline at end of file
diff --git a/room/runtime/api/restricted_2.0.0.txt b/room/runtime/api/restricted_2.0.0.txt
index e23972b4..9738390 100644
--- a/room/runtime/api/restricted_2.0.0.txt
+++ b/room/runtime/api/restricted_2.0.0.txt
@@ -92,11 +92,9 @@
     ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean, java.lang.String...!);
     ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.room.RoomSQLiteQuery!, boolean, java.lang.String...!);
     method protected abstract java.util.List<T>! convertRows(android.database.Cursor!);
-    method public int countItems();
     method public boolean isInvalid();
     method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T>);
     method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T>);
-    method public java.util.List<T>? loadRange(int, int);
   }
 
 }
diff --git a/room/runtime/api/restricted_2.1.0-alpha05.txt b/room/runtime/api/restricted_2.1.0-alpha05.txt
index d667a21..c2532ea 100644
--- a/room/runtime/api/restricted_2.1.0-alpha05.txt
+++ b/room/runtime/api/restricted_2.1.0-alpha05.txt
@@ -98,10 +98,8 @@
     ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean, java.lang.String...!);
     ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.room.RoomSQLiteQuery!, boolean, java.lang.String...!);
     method protected abstract java.util.List<T>! convertRows(android.database.Cursor!);
-    method public int countItems();
     method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T>);
     method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T>);
-    method public java.util.List<T>? loadRange(int, int);
   }
 
 }
diff --git a/room/runtime/api/restricted_current.txt b/room/runtime/api/restricted_current.txt
index d667a21..c2532ea 100644
--- a/room/runtime/api/restricted_current.txt
+++ b/room/runtime/api/restricted_current.txt
@@ -98,10 +98,8 @@
     ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.sqlite.db.SupportSQLiteQuery!, boolean, java.lang.String...!);
     ctor protected LimitOffsetDataSource(androidx.room.RoomDatabase!, androidx.room.RoomSQLiteQuery!, boolean, java.lang.String...!);
     method protected abstract java.util.List<T>! convertRows(android.database.Cursor!);
-    method public int countItems();
     method public void loadInitial(androidx.paging.PositionalDataSource.LoadInitialParams, androidx.paging.PositionalDataSource.LoadInitialCallback<T>);
     method public void loadRange(androidx.paging.PositionalDataSource.LoadRangeParams, androidx.paging.PositionalDataSource.LoadRangeCallback<T>);
-    method public java.util.List<T>? loadRange(int, int);
   }
 
 }
diff --git a/room/runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.java b/room/runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.java
index 563266a..cc1e1ff 100644
--- a/room/runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.java
+++ b/room/runtime/src/main/java/androidx/room/paging/LimitOffsetDataSource.java
@@ -76,6 +76,8 @@
 
     /**
      * Count number of rows query can return
+     *
+     * @hide
      */
     @SuppressWarnings("WeakerAccess")
     public int countItems() {
@@ -147,6 +149,8 @@
 
     /**
      * Return the rows from startPos to startPos + loadCount
+     *
+     * @hide
      */
     @NonNull
     public List<T> loadRange(int startPosition, int loadCount) {
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleDynamicGroupMrp.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleDynamicGroupMrp.java
index 033f8e1..8b7fa4c 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleDynamicGroupMrp.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/SampleDynamicGroupMrp.java
@@ -48,6 +48,7 @@
  */
 final class SampleDynamicGroupMrp extends SampleMediaRouteProvider {
     private static final String TAG = "SampleDynamicGroupMrp";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String FIXED_VOLUME_ROUTE_ID = "fixed";
     private static final String VARIABLE_VOLUME_BASIC_ROUTE_ID = "variable_basic";
@@ -285,7 +286,9 @@
 
             mHelper = new RouteControlHelper(mRouteId);
             updateDynamicRouteDescriptors();
-            Log.d(TAG, mRouteId + ": Controller created.");
+            if (DEBUG) {
+                Log.d(TAG, mRouteId + ": Controller created.");
+            }
         }
 
         //////////////////////////////////////////////
@@ -311,13 +314,13 @@
         public void onAddMemberRoute(String routeId) {
             DynamicRouteDescriptor dynamicDescriptor = mDynamicRouteDescriptors.get(routeId);
             if (dynamicDescriptor == null) {
-                Log.d(TAG, "onAddMemberRoute: Ignored for routeId: " + routeId);
+                Log.w(TAG, "onAddMemberRoute: Ignored for routeId: " + routeId);
                 return;
             }
 
             MediaRouteDescriptor selectedRouteDescriptor = mRouteDescriptors.get(mRouteId);
             if (selectedRouteDescriptor == null) {
-                Log.d(TAG, "onAddMemberRoute: Can't find selected route : " + mRouteId);
+                Log.w(TAG, "onAddMemberRoute: Can't find selected route : " + mRouteId);
                 return;
             }
             if (!selectedRouteDescriptor.isDynamicGroupRoute()) {
@@ -367,11 +370,9 @@
                     new MediaRouteDescriptor.Builder(mRouteDescriptors.get(mRouteId));
 
             for (String routeId : memberRouteIds) {
-                Log.d(TAG, "member : " + routeId);
-
                 DynamicRouteDescriptor dynamicDescriptor = mDynamicRouteDescriptors.get(routeId);
                 if (dynamicDescriptor == null) {
-                    Log.d(TAG, "onAddMemberRoute: Ignored for routeId: " + routeId);
+                    Log.w(TAG, "onAddMemberRoute: Ignored for routeId: " + routeId);
                     return;
                 }
 
@@ -411,13 +412,13 @@
             DynamicRouteDescriptor dynamicDescriptor = mDynamicRouteDescriptors.get(routeId);
             if (dynamicDescriptor == null || !dynamicDescriptor.isUnselectable()
                     || !mMemberRouteIds.remove(routeId)) {
-                Log.d(TAG, "onRemoveMemberRoute: Ignored for routeId: " + routeId);
+                Log.w(TAG, "onRemoveMemberRoute: Ignored for routeId: " + routeId);
                 return;
             }
 
             MediaRouteDescriptor selectedRouteDescriptor = mRouteDescriptors.get(mRouteId);
             if (selectedRouteDescriptor == null) {
-                Log.d(TAG, "onRemoveMemberRoute: Can't find selected route : " + mRouteId);
+                Log.w(TAG, "onRemoveMemberRoute: Can't find selected route : " + mRouteId);
                 return;
             }
             if (!selectedRouteDescriptor.isDynamicGroupRoute()) {
diff --git a/savedstate/api/1.0.0-alpha02.txt b/savedstate/api/1.0.0-alpha02.txt
new file mode 100644
index 0000000..e509849
--- /dev/null
+++ b/savedstate/api/1.0.0-alpha02.txt
@@ -0,0 +1,27 @@
+// Signature format: 3.0
+package androidx.savedstate {
+
+  public final class SavedStateRegistry {
+    method @MainThread public android.os.Bundle? consumeRestoredStateForKey(String);
+    method @MainThread public boolean isRestored();
+    method @MainThread public void registerSavedStateProvider(String, androidx.savedstate.SavedStateRegistry.SavedStateProvider);
+    method @MainThread public void unregisterSavedStateProvider(String);
+  }
+
+  public static interface SavedStateRegistry.SavedStateProvider {
+    method public android.os.Bundle saveState();
+  }
+
+  public final class SavedStateRegistryController {
+    ctor public SavedStateRegistryController();
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+    method @MainThread public void performRestore(android.os.Bundle?);
+    method @MainThread public void performSave(android.os.Bundle);
+  }
+
+  public interface SavedStateRegistryOwner {
+    method public androidx.savedstate.SavedStateRegistry getSavedStateRegistry();
+  }
+
+}
+
diff --git a/savedstate/api/res-1.0.0-alpha02.txt b/savedstate/api/res-1.0.0-alpha02.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/savedstate/api/res-1.0.0-alpha02.txt
diff --git a/savedstate/api/restricted_1.0.0-alpha02.txt b/savedstate/api/restricted_1.0.0-alpha02.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/savedstate/api/restricted_1.0.0-alpha02.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/settings.gradle b/settings.gradle
index fbd60d7..5006b64 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -178,6 +178,7 @@
 includeProject(":vectordrawable-animated", "graphics/drawable/animated")
 includeProject(":viewpager", "viewpager")
 includeProject(":viewpager2", "viewpager2")
+includeProject(":viewpager2:integration-tests:testapp", "viewpager2/integration-tests/testapp")
 includeProject(":wear", "wear")
 includeProject(":webkit", "webkit")
 includeProject(":webkit:integration-tests:testapp", "webkit/integration-tests/testapp")
@@ -226,7 +227,6 @@
 includeProject(":support-v7-demos", new File(samplesRoot, "Support7Demos"))
 includeProject(":support-v13-demos", new File(samplesRoot, "Support13Demos"))
 includeProject(":support-wear-demos", new File(samplesRoot, "SupportWearDemos"))
-includeProject(":viewpager2-demos", new File(samplesRoot, "ViewPager2Demos"))
 
 /////////////////////////////
 //
@@ -274,4 +274,4 @@
 
 // dumb test project that has a test for each size to ensure that at least one test is run
 // for each size and test runner is happy when there is nothing to test.
-includeProject(":dumb-tests", "dumb-tests")
\ No newline at end of file
+includeProject(":dumb-tests", "dumb-tests")
diff --git a/slices/core/src/main/res-public/values-as/strings.xml b/slices/core/src/main/res-public/values-as/strings.xml
new file mode 100644
index 0000000..5ddcef2
--- /dev/null
+++ b/slices/core/src/main/res-public/values-as/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="slice_provider" msgid="7510675753826300262">"androidx.slice.compat.SliceProviderCompat"</string>
+</resources>
diff --git a/slices/view/src/main/res/values-as/strings.xml b/slices/view/src/main/res/values-as/strings.xml
index dbc598f..78cc260 100644
--- a/slices/view/src/main/res/values-as/strings.xml
+++ b/slices/view/src/main/res/values-as/strings.xml
@@ -20,7 +20,7 @@
     <string name="abc_slice_more_content" msgid="6405516388971241142">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
     <string name="abc_slice_more" msgid="1983560225998630901">"অধিক"</string>
     <string name="abc_slice_show_more" msgid="1567717014004692768">"অধিক দেখুৱাওক"</string>
-    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> আপডেট কৰা হৈছিল"</string>
+    <string name="abc_slice_updated" msgid="8155085405396453848">"<xliff:g id="TIME">%1$s</xliff:g> আপডে’ট কৰা হৈছিল"</string>
     <plurals name="abc_slice_duration_min" formatted="false" msgid="6996334305156847955">
       <item quantity="one"><xliff:g id="ID_2">%d</xliff:g> মিনিট আগেয়ে</item>
       <item quantity="other"><xliff:g id="ID_2">%d</xliff:g> মিনিট আগেয়ে</item>
diff --git a/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java b/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
index 6f380a7..282d377 100644
--- a/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
+++ b/slidingpanelayout/src/main/java/androidx/slidingpanelayout/widget/SlidingPaneLayout.java
@@ -117,6 +117,10 @@
      */
     private static final int MIN_FLING_VELOCITY = 400; // dips per second
 
+    /** Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage. */
+    private static final String ACCESSIBILITY_CLASS_NAME =
+            "androidx.slidingpanelayout.widget.SlidingPaneLayout";
+
     /**
      * The fade color used for the panel covered by the slider. 0 = no fading.
      */
@@ -1520,7 +1524,7 @@
             copyNodeInfoNoChildren(info, superNode);
             superNode.recycle();
 
-            info.setClassName(SlidingPaneLayout.class.getName());
+            info.setClassName(ACCESSIBILITY_CLASS_NAME);
             info.setSource(host);
 
             final ViewParent parent = ViewCompat.getParentForAccessibility(host);
@@ -1546,7 +1550,7 @@
         public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
             super.onInitializeAccessibilityEvent(host, event);
 
-            event.setClassName(SlidingPaneLayout.class.getName());
+            event.setClassName(ACCESSIBILITY_CLASS_NAME);
         }
 
         @Override
diff --git a/studiow b/studiow
index 0d94339..2ba8f59 100755
--- a/studiow
+++ b/studiow
@@ -65,9 +65,21 @@
     if [ "${acceptsLicenseAgreement}" == "-y" ]; then
       touch "${licenseAcceptedPath}"
     else
-      echo "Do you accept the license agreement at $(getLicensePath) ?"
-      echo "If you do, then rerun this script with a '-y' argument"
-      exit 1
+      read -r -n 1 -p "Do you accept the license agreement at $(getLicensePath) [Y/n]? " reply
+
+      if [ ! -z "${reply}" ]; then
+	# Fix missing newline
+        echo
+      fi
+
+      case "${reply}" in
+        [yY]|"")
+          touch "${licenseAcceptedPath}"
+          ;;
+        *)
+          exit 1
+          ;;
+      esac
     fi
   fi
 }
diff --git a/textclassifier/src/main/java/androidx/textclassifier/widget/FloatingToolbar.java b/textclassifier/src/main/java/androidx/textclassifier/widget/FloatingToolbar.java
index a7e4b38..3adf957 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/widget/FloatingToolbar.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/widget/FloatingToolbar.java
@@ -133,7 +133,7 @@
                 int oldLeft, int oldRight, int oldTop, int oldBottom) {
             mNewRect.set(newLeft, newRight, newTop, newBottom);
             mOldRect.set(oldLeft, oldRight, oldTop, oldBottom);
-            if (mPopup.isShowing() && !mNewRect.equals(mOldRect)) {
+            if (mPopup.isShowing() && (mNewRect.width() != mOldRect.width())) {
                 mWidthChanged = true;
                 updateLayout();
             }
@@ -319,12 +319,12 @@
         List<SupportMenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
         Collections.sort(menuItems, mMenuItemComparator);
         if (!isCurrentlyShowing(menuItems) || mWidthChanged) {
-            mPopup.dismiss();
+            mPopup.hide();
             mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
             mShowingMenuItems = menuItems;
         }
         if (menuItems.isEmpty()) {
-            // don't update or show the toolbar.
+            mPopup.dismiss();
         } else if (!mPopup.isShowing()) {
             mPopup.show(mContentRect);
         } else if (!mPreviousContentRect.equals(mContentRect)) {
@@ -489,7 +489,6 @@
         /* Outside touch handling */
         final Runnable mDismissRunnable;
         final View.OnClickListener mOnOutsideTouchHandler;
-        PopupWindow.OnDismissListener mOnDismiss;
 
         boolean mOpenOverflowUpwards;  // Whether the overflow opens upwards or downwards.
         boolean mIsOverflowOpen;
@@ -513,9 +512,6 @@
                 public void onClick(View v) {
                     hide();
                     mDismissRunnable.run();
-                    if (mOnDismiss != null) {
-                        mOnDismiss.onDismiss();
-                    }
                 }
             };
             mPopupWindow = createPopupWindow(mContentContainer, mOnOutsideTouchHandler);
@@ -590,7 +586,6 @@
          * Sets the floating popup's onDismissListener.
          */
         public void setOnDismissListener(@Nullable final PopupWindow.OnDismissListener onDismiss) {
-            mOnDismiss = onDismiss;
             mPopupWindow.setOnDismissListener(onDismiss);
         }
 
@@ -1758,7 +1753,10 @@
         popupWindow.setOutsideTouchable(true);
         popupWindow.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
         popupWindow.setAnimationStyle(0);
-        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        int color = Color.TRANSPARENT;
+        // Want to see the floating window? Uncomment the next line.
+        //color = Color.argb(50, 0, 0, 0);
+        popupWindow.setBackgroundDrawable(new ColorDrawable(color));
         content.setLayoutParams(new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
         popupContentHolder.addView(content);
diff --git a/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java b/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
index c571eb2..89c7dfc 100644
--- a/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
+++ b/textclassifier/src/main/java/androidx/textclassifier/widget/ToolbarController.java
@@ -168,9 +168,7 @@
             final int end = spannable.getSpanEnd(highlight);
             final int min = Math.max(0, Math.min(start, end));
             final int max = Math.max(0, Math.max(start, end));
-            if (min >= 0) {
-                return textView.getText().subSequence(min, max).toString();
-            }
+            return textView.getText().subSequence(min, max).toString();
         }
         return null;
     }
@@ -316,131 +314,93 @@
 
     private static void setListeners(
             TextView textView, int start, int end, FloatingToolbar toolbar) {
-        final ViewTreeObserver observer = textView.getViewTreeObserver();
-        final OnCoordinatesChangeHandler onCoordinatesChangeHandler =
-                new OnCoordinatesChangeHandler(toolbar, textView, start, end);
-        final OnWindowFocusChangeListener onWindowFocusChangeListener =
-                new OnWindowFocusChangeListener(toolbar);
-        final OnTextViewFocusChangeListener onTextViewFocusChangeListener =
-                new OnTextViewFocusChangeListener(textView, toolbar);
-        final OnTextViewDetachedListener onTextViewDetachedListener =
-                new OnTextViewDetachedListener(toolbar);
-        observer.addOnPreDrawListener(onCoordinatesChangeHandler);
-        observer.addOnWindowFocusChangeListener(onWindowFocusChangeListener);
-        observer.addOnGlobalFocusChangeListener(onTextViewFocusChangeListener);
-        observer.addOnWindowAttachListener(onTextViewDetachedListener);
-        final ActionModeCallback selectionCallback = new ActionModeCallback(
-                toolbar, textView.getCustomSelectionActionModeCallback(), false);
-        final ActionModeCallback insertionCallback = new ActionModeCallback(
-                toolbar, textView.getCustomInsertionActionModeCallback(), true);
-        textView.setCustomSelectionActionModeCallback(selectionCallback);
-        textView.setCustomInsertionActionModeCallback(insertionCallback);
         toolbar.setOnDismissListener(
                 new OnToolbarDismissListener(
                         textView,
-                        onCoordinatesChangeHandler,
-                        onWindowFocusChangeListener,
-                        onTextViewFocusChangeListener,
-                        onTextViewDetachedListener,
-                        selectionCallback,
-                        insertionCallback));
+                        new TextViewListener(toolbar, textView, start, end),
+                        new ActionModeCallback(
+                                toolbar,
+                                textView.getCustomSelectionActionModeCallback(),
+                                /* preferMe= */ false),
+                        new ActionModeCallback(
+                                toolbar,
+                                textView.getCustomInsertionActionModeCallback(),
+                                /* preferMe= */ true)));
     }
 
     /**
-     * Repositions the toolbar when the coordinates of the highlighted text changes.
-     * It does this by checking just before every draw frame if the coordinates of the highlighted
-     * text have changed. Because this callback is called on every draw frame, it only recalculates
-     * the highlights position when the toolbar is actively showing.
+     * Listens for several TextView events to reposition or dismiss the toolbar.
      */
-    private static final class OnCoordinatesChangeHandler
-            implements ViewTreeObserver.OnPreDrawListener {
+    private static final class TextViewListener implements
+            ViewTreeObserver.OnPreDrawListener,
+            ViewTreeObserver.OnWindowFocusChangeListener,
+            ViewTreeObserver.OnGlobalFocusChangeListener,
+            ViewTreeObserver.OnWindowAttachListener {
+
+        private static final long THROTTLE_DELAY_MS = 300;
 
         private final FloatingToolbar mToolbar;
         private final TextView mTextView;
         private final Rect mContentRect;
+        private final Rect mTempRect;
         private final int mStart;
         private final int mEnd;
 
-        private int[] mLocation = new int[2];
+        private long mLastUpdateTimeMs = System.currentTimeMillis() - THROTTLE_DELAY_MS;
 
-        OnCoordinatesChangeHandler(
-                FloatingToolbar toolbar, TextView textView, int start, int end) {
+        TextViewListener(FloatingToolbar toolbar, TextView textView, int start, int end) {
             mToolbar = Preconditions.checkNotNull(toolbar);
             mTextView = Preconditions.checkNotNull(textView);
-            mTextView.getRootView().getLocationOnScreen(mLocation);
             mContentRect = new Rect();
+            mTempRect = new Rect();
             mStart = start;
             mEnd = end;
         }
 
         @Override
         public boolean onPreDraw() {
-            if (mToolbar.isShowing()) {
-                final int[] location = new int[2];
-                mTextView.getRootView().getLocationOnScreen(location);
-                if (location[0] != mLocation[0] || location[1] != mLocation[1]) {
+            final long now = System.currentTimeMillis();
+            if (!maybeDismissToolbar(true)
+                    && mToolbar.isShowing()
+                    && now - mLastUpdateTimeMs >= THROTTLE_DELAY_MS) {
+                updateRectCoordinates(mTempRect, mTextView, mStart, mEnd);
+                if (!mTempRect.equals(mContentRect)) {
                     // View moved.
-                    updateRectCoordinates(mContentRect, mTextView, mStart, mEnd);
+                    mContentRect.set(mTempRect);
                     mToolbar.setContentRect(mContentRect);
                     mToolbar.updateLayout();
+                    mLastUpdateTimeMs = now;
                 }
-                mLocation = location;
             }
             return true;
         }
-    }
-
-    private static final class OnWindowFocusChangeListener
-            implements ViewTreeObserver.OnWindowFocusChangeListener {
-
-        private final FloatingToolbar mToolbar;
-
-        OnWindowFocusChangeListener(FloatingToolbar toolbar) {
-            mToolbar = Preconditions.checkNotNull(toolbar);
-        }
 
         @Override
         public void onWindowFocusChanged(boolean hasFocus) {
-            if (!hasFocus) {
-                mToolbar.dismiss();
-            }
-        }
-    }
-
-    private static final class OnTextViewFocusChangeListener
-            implements ViewTreeObserver.OnGlobalFocusChangeListener {
-
-        private final TextView mTextView;
-        private final FloatingToolbar mToolbar;
-
-        OnTextViewFocusChangeListener(TextView textView, FloatingToolbar toolbar) {
-            mTextView = Preconditions.checkNotNull(textView);
-            mToolbar = Preconditions.checkNotNull(toolbar);
+            maybeDismissToolbar(hasFocus);
         }
 
         @Override
-        public void onGlobalFocusChanged(View v, View v1) {
-            if (!mTextView.hasFocus()) {
-                mToolbar.dismiss();
-            }
-        }
-    }
-
-    private static final class OnTextViewDetachedListener
-            implements ViewTreeObserver.OnWindowAttachListener {
-
-        private final FloatingToolbar mToolbar;
-
-        OnTextViewDetachedListener(FloatingToolbar toolbar) {
-            mToolbar = Preconditions.checkNotNull(toolbar);
+        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+            maybeDismissToolbar(true);
         }
 
         @Override
-        public void onWindowAttached() {}
+        public void onWindowAttached() {
+            maybeDismissToolbar(true);
+        }
 
         @Override
         public void onWindowDetached() {
-            mToolbar.dismiss();
+            maybeDismissToolbar(true);
+        }
+
+        private boolean maybeDismissToolbar(boolean assumeWindowFocus) {
+            if (assumeWindowFocus && mTextView.hasFocus() && hasValidTextView(mTextView)) {
+                return false;
+            }
+            dismissImmediately(mToolbar);
+            return true;
         }
     }
 
@@ -516,38 +476,45 @@
     private static final class OnToolbarDismissListener implements PopupWindow.OnDismissListener {
 
         private final TextView mTextView;
-        private final OnCoordinatesChangeHandler mOnCoordinatesChangeHandler;
-        private final OnWindowFocusChangeListener mOnWindowFocusChangeListener;
-        private final OnTextViewFocusChangeListener mOnFocusChangeListener;
-        private final OnTextViewDetachedListener mOnTextViewDetachedListener;
+        private final ViewTreeObserver mObserver;
+        private final TextViewListener mTextViewListener;
         private final ActionModeCallback mSelectionCallback;
         private final ActionModeCallback mInsertionCallback;
 
         OnToolbarDismissListener(
                 TextView textView,
-                OnCoordinatesChangeHandler onCoordinatesChangeHandler,
-                OnWindowFocusChangeListener onWindowFocusChangeListener,
-                OnTextViewFocusChangeListener onTextViewFocusChangeListener,
-                OnTextViewDetachedListener onTextViewDetachedListener,
+                TextViewListener textViewListener,
                 ActionModeCallback selectionCallback,
                 ActionModeCallback insertionCallback) {
             mTextView = Preconditions.checkNotNull(textView);
-            mOnCoordinatesChangeHandler = Preconditions.checkNotNull(onCoordinatesChangeHandler);
-            mOnWindowFocusChangeListener = Preconditions.checkNotNull(onWindowFocusChangeListener);
-            mOnFocusChangeListener = Preconditions.checkNotNull(onTextViewFocusChangeListener);
-            mOnTextViewDetachedListener = Preconditions.checkNotNull(onTextViewDetachedListener);
+            mObserver = mTextView.getViewTreeObserver();
+            mTextViewListener = Preconditions.checkNotNull(textViewListener);
+            registerListeners();
             mSelectionCallback = Preconditions.checkNotNull(selectionCallback);
             mInsertionCallback = Preconditions.checkNotNull(insertionCallback);
+            setCallbacks();
         }
 
-        @Override
-        public void onDismiss() {
-            removeHighlight(mTextView);
-            final ViewTreeObserver observer = mTextView.getViewTreeObserver();
-            observer.removeOnPreDrawListener(mOnCoordinatesChangeHandler);
-            observer.removeOnWindowFocusChangeListener(mOnWindowFocusChangeListener);
-            observer.removeOnGlobalFocusChangeListener(mOnFocusChangeListener);
-            observer.removeOnWindowAttachListener(mOnTextViewDetachedListener);
+        private void registerListeners() {
+            mObserver.addOnPreDrawListener(mTextViewListener);
+            mObserver.addOnWindowFocusChangeListener(mTextViewListener);
+            mObserver.addOnGlobalFocusChangeListener(mTextViewListener);
+            mObserver.addOnWindowAttachListener(mTextViewListener);
+        }
+
+        private void unregisterListeners() {
+            mObserver.removeOnPreDrawListener(mTextViewListener);
+            mObserver.removeOnWindowFocusChangeListener(mTextViewListener);
+            mObserver.removeOnGlobalFocusChangeListener(mTextViewListener);
+            mObserver.removeOnWindowAttachListener(mTextViewListener);
+        }
+
+        private void setCallbacks() {
+            mTextView.setCustomSelectionActionModeCallback(mSelectionCallback);
+            mTextView.setCustomInsertionActionModeCallback(mInsertionCallback);
+        }
+
+        private void clearCallbacks() {
             if (mSelectionCallback == mTextView.getCustomSelectionActionModeCallback()) {
                 mTextView.setCustomSelectionActionModeCallback(
                         mSelectionCallback.mOriginalCallback);
@@ -557,6 +524,13 @@
                         mInsertionCallback.mOriginalCallback);
             }
         }
+
+        @Override
+        public void onDismiss() {
+            removeHighlight(mTextView);
+            unregisterListeners();
+            clearCallbacks();
+        }
     }
 
     private static final class OnMenuItemClickListener implements MenuItem.OnMenuItemClickListener {
diff --git a/textclassifier/src/main/res/values-am/strings.xml b/textclassifier/src/main/res/values-am/strings.xml
index 7218771..831ac2b 100644
--- a/textclassifier/src/main/res/values-am/strings.xml
+++ b/textclassifier/src/main/res/values-am/strings.xml
@@ -19,7 +19,7 @@
     <string name="email" msgid="5568050657313893478">"ኢሜይል"</string>
     <string name="email_desc" msgid="6941280589171810022">"ለተመረጡ አድራሻዎች ኢሜይል ላክ"</string>
     <string name="dial" msgid="7317293545368448453">"ደውል"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"ወደተመረጠው ስልክ ቁጥር ደውል"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"ወደ ተመረጠው ስልክ ቁጥር ደውል"</string>
     <string name="browse" msgid="3733970143542020945">"ክፈት"</string>
     <string name="browse_desc" msgid="3898254913938219011">"የተመረጠውን ዩአርኤል ክፈት"</string>
     <string name="sms" msgid="5495416906312064886">"መልዕክት"</string>
diff --git a/textclassifier/src/main/res/values-ar/strings.xml b/textclassifier/src/main/res/values-ar/strings.xml
index b2569b9..52fb7d7 100644
--- a/textclassifier/src/main/res/values-ar/strings.xml
+++ b/textclassifier/src/main/res/values-ar/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"البريد الإلكتروني"</string>
+    <string name="email" msgid="5568050657313893478">"إرسال بريد إلكتروني"</string>
     <string name="email_desc" msgid="6941280589171810022">"مراسلة العنوان المحدد عبر البريد الإلكتروني"</string>
     <string name="dial" msgid="7317293545368448453">"اتصال"</string>
     <string name="dial_desc" msgid="5129451396208040332">"الاتصال برقم الهاتف المحدد"</string>
     <string name="browse" msgid="3733970143542020945">"فتح"</string>
     <string name="browse_desc" msgid="3898254913938219011">"‏فتح عنوان URL المحدد"</string>
-    <string name="sms" msgid="5495416906312064886">"رسالة"</string>
+    <string name="sms" msgid="5495416906312064886">"إرسال رسائل قصيرة"</string>
     <string name="sms_desc" msgid="8293660783374489324">"مراسلة رقم الهاتف المحدد"</string>
     <string name="add_contact" msgid="9005634177208282449">"إضافة"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"إضافة إلى جهات الاتصال"</string>
diff --git a/textclassifier/src/main/res/values-as/strings.xml b/textclassifier/src/main/res/values-as/strings.xml
new file mode 100644
index 0000000..7f52871
--- /dev/null
+++ b/textclassifier/src/main/res/values-as/strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2018 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="email" msgid="5568050657313893478">"ইমেইল কৰক"</string>
+    <string name="email_desc" msgid="6941280589171810022">"বাছনি কৰা ঠিকনালৈ ইমেইল পঠিয়াওক"</string>
+    <string name="dial" msgid="7317293545368448453">"কল কৰক"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"বাছনি কৰা ফ\'ন নম্বৰত কল কৰক"</string>
+    <string name="browse" msgid="3733970143542020945">"খোলক"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"বাছনি কৰা URL খোলক"</string>
+    <string name="sms" msgid="5495416906312064886">"বাৰ্তা পঠিয়াওক"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"বাছনি কৰা ফ’ন নম্বৰলৈ বাৰ্তা পঠিয়াওক"</string>
+    <string name="add_contact" msgid="9005634177208282449">"যোগ কৰক"</string>
+    <string name="add_contact_desc" msgid="2475604767309086575">"সর্ম্পকসূচীত যোগ কৰক"</string>
+    <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"অধিক বিকল্প"</string>
+    <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"অভাৰফ্ল\' বন্ধ কৰক"</string>
+    <string name="abc_share" msgid="7091841667818715717">"শ্বেয়াৰ কৰক"</string>
+</resources>
diff --git a/textclassifier/src/main/res/values-az/strings.xml b/textclassifier/src/main/res/values-az/strings.xml
index e8b583e..b8de8f8 100644
--- a/textclassifier/src/main/res/values-az/strings.xml
+++ b/textclassifier/src/main/res/values-az/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-poçt"</string>
+    <string name="email" msgid="5568050657313893478">"E-poçt yazın"</string>
     <string name="email_desc" msgid="6941280589171810022">"Seçilmiş ünvana e-məktub yazın"</string>
     <string name="dial" msgid="7317293545368448453">"Zəng edin"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Seçilmiş telefon nömrəsinə zəng edin"</string>
     <string name="browse" msgid="3733970143542020945">"Açın"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Seçilmiş linki açın"</string>
-    <string name="sms" msgid="5495416906312064886">"Mesaj"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Seçilmiş telefon nömrəsini mesajla göndərin"</string>
+    <string name="sms" msgid="5495416906312064886">"Mesaj yazın"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Seçilmiş telefon nömrəsinə mesaj göndərin"</string>
     <string name="add_contact" msgid="9005634177208282449">"Əlavə edin"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Kontakta əlavə edin"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Digər seçimlər"</string>
diff --git a/textclassifier/src/main/res/values-bs/strings.xml b/textclassifier/src/main/res/values-bs/strings.xml
index 0bc610f8..0ad050a 100644
--- a/textclassifier/src/main/res/values-bs/strings.xml
+++ b/textclassifier/src/main/res/values-bs/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-pošta"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Pošaljite e-poruku na odabrane adrese"</string>
-    <string name="dial" msgid="7317293545368448453">"Poziv"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Pozovite odabrani broj telefona"</string>
+    <string name="email" msgid="5568050657313893478">"Pošalji e-poruku"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Pošalji e-poruku na odabranu adresu"</string>
+    <string name="dial" msgid="7317293545368448453">"Pozovi"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Pozovi odabrani broj telefona"</string>
     <string name="browse" msgid="3733970143542020945">"Otvori"</string>
-    <string name="browse_desc" msgid="3898254913938219011">"Otvorite odabrani URL"</string>
-    <string name="sms" msgid="5495416906312064886">"Poruka"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Pošaljite poruku odabranom broju telefona"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"Otvori odabrani URL"</string>
+    <string name="sms" msgid="5495416906312064886">"Pošalji SMS"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Pošalji SMS odabranom broju telefona"</string>
     <string name="add_contact" msgid="9005634177208282449">"Dodaj"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Dodaj u kontakte"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Više opcija"</string>
diff --git a/textclassifier/src/main/res/values-ca/strings.xml b/textclassifier/src/main/res/values-ca/strings.xml
index 0f2cf5ab..e4932cb 100644
--- a/textclassifier/src/main/res/values-ca/strings.xml
+++ b/textclassifier/src/main/res/values-ca/strings.xml
@@ -22,7 +22,7 @@
     <string name="dial_desc" msgid="5129451396208040332">"Truca al número de telèfon seleccionat"</string>
     <string name="browse" msgid="3733970143542020945">"Obre"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Obre l\'URL seleccionat"</string>
-    <string name="sms" msgid="5495416906312064886">"Missatge"</string>
+    <string name="sms" msgid="5495416906312064886">"Envia un SMS"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Envia un missatge al número de telèfon seleccionat"</string>
     <string name="add_contact" msgid="9005634177208282449">"Afegeix"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Afegeix als contactes"</string>
diff --git a/textclassifier/src/main/res/values-cs/strings.xml b/textclassifier/src/main/res/values-cs/strings.xml
index cc7360f..757a68d 100644
--- a/textclassifier/src/main/res/values-cs/strings.xml
+++ b/textclassifier/src/main/res/values-cs/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E‑mail"</string>
+    <string name="email" msgid="5568050657313893478">"Poslat e-mail"</string>
     <string name="email_desc" msgid="6941280589171810022">"Napsat na vybranou e‑mailovou adresu"</string>
-    <string name="dial" msgid="7317293545368448453">"Volat"</string>
+    <string name="dial" msgid="7317293545368448453">"Zavolat"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Zavolat na vybrané telefonní číslo"</string>
     <string name="browse" msgid="3733970143542020945">"Otevřít"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Otevřít vybranou adresu URL"</string>
-    <string name="sms" msgid="5495416906312064886">"Zpráva"</string>
+    <string name="sms" msgid="5495416906312064886">"Napsat zprávu"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Napsat SMS na vybrané telefonní číslo"</string>
     <string name="add_contact" msgid="9005634177208282449">"Přidat"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Přidat do kontaktů"</string>
diff --git a/textclassifier/src/main/res/values-da/strings.xml b/textclassifier/src/main/res/values-da/strings.xml
index 50e7f3d..d519fc9 100644
--- a/textclassifier/src/main/res/values-da/strings.xml
+++ b/textclassifier/src/main/res/values-da/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Mail"</string>
+    <string name="email" msgid="5568050657313893478">"Send mail"</string>
     <string name="email_desc" msgid="6941280589171810022">"Send en mail til den valgte adresse"</string>
-    <string name="dial" msgid="7317293545368448453">"Opkald"</string>
+    <string name="dial" msgid="7317293545368448453">"Ring op"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Ring til det valgte telefonnummer"</string>
     <string name="browse" msgid="3733970143542020945">"Åbn"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Åbn den valgte webadresse"</string>
-    <string name="sms" msgid="5495416906312064886">"Besked"</string>
+    <string name="sms" msgid="5495416906312064886">"Send besked"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Send en besked til det valgte telefonnummer"</string>
     <string name="add_contact" msgid="9005634177208282449">"Tilføj"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Føj til kontakter"</string>
diff --git a/textclassifier/src/main/res/values-de/strings.xml b/textclassifier/src/main/res/values-de/strings.xml
index 373ba36..463609c 100644
--- a/textclassifier/src/main/res/values-de/strings.xml
+++ b/textclassifier/src/main/res/values-de/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-Mail"</string>
+    <string name="email" msgid="5568050657313893478">"E-Mail senden"</string>
     <string name="email_desc" msgid="6941280589171810022">"E-Mail an ausgewählte Adresse senden"</string>
     <string name="dial" msgid="7317293545368448453">"Anruf"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Ausgewählte Telefonnummer anrufen"</string>
     <string name="browse" msgid="3733970143542020945">"Öffnen"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Ausgewählte URL öffnen"</string>
-    <string name="sms" msgid="5495416906312064886">"SMS"</string>
+    <string name="sms" msgid="5495416906312064886">"SMS senden"</string>
     <string name="sms_desc" msgid="8293660783374489324">"SMS an ausgewählte Telefonnummer senden"</string>
     <string name="add_contact" msgid="9005634177208282449">"Hinzufügen"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Zu Kontakten hinzufügen"</string>
diff --git a/textclassifier/src/main/res/values-es/strings.xml b/textclassifier/src/main/res/values-es/strings.xml
index 007e62e..dd4c644 100644
--- a/textclassifier/src/main/res/values-es/strings.xml
+++ b/textclassifier/src/main/res/values-es/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Correo electrónico"</string>
+    <string name="email" msgid="5568050657313893478">"Enviar correo"</string>
     <string name="email_desc" msgid="6941280589171810022">"Enviar un correo electrónico a la dirección seleccionada"</string>
     <string name="dial" msgid="7317293545368448453">"Llamar"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Llamar al número de teléfono seleccionado"</string>
     <string name="browse" msgid="3733970143542020945">"Abrir"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Abrir la URL seleccionada"</string>
-    <string name="sms" msgid="5495416906312064886">"Mensaje"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Enviar un mensaje al número de teléfono seleccionado"</string>
+    <string name="sms" msgid="5495416906312064886">"Enviar SMS"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Enviar SMS al teléfono seleccionado"</string>
     <string name="add_contact" msgid="9005634177208282449">"Añadir"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Añadir a contactos"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Más opciones"</string>
diff --git a/textclassifier/src/main/res/values-et/strings.xml b/textclassifier/src/main/res/values-et/strings.xml
index da2ecfb..b4b2e29 100644
--- a/textclassifier/src/main/res/values-et/strings.xml
+++ b/textclassifier/src/main/res/values-et/strings.xml
@@ -16,16 +16,16 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-post"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Valitud aadressile meili saatmine"</string>
-    <string name="dial" msgid="7317293545368448453">"Kõne"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Valitud telefoninumbrile helistamine"</string>
+    <string name="email" msgid="5568050657313893478">"Saada meil"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Saada valitud aadressile meil"</string>
+    <string name="dial" msgid="7317293545368448453">"Helista"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Helista valitud telefoninumbrile"</string>
     <string name="browse" msgid="3733970143542020945">"Ava"</string>
-    <string name="browse_desc" msgid="3898254913938219011">"Valitud URL-i avamine"</string>
-    <string name="sms" msgid="5495416906312064886">"Sõnum"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Valitud telefoninumbrile sõnumi saatmine"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"Ava valitud URL"</string>
+    <string name="sms" msgid="5495416906312064886">"Saada sõnum"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Saada valitud telefoninumbrile sõnum"</string>
     <string name="add_contact" msgid="9005634177208282449">"Lisa"</string>
-    <string name="add_contact_desc" msgid="2475604767309086575">"Lisamine kontaktidesse"</string>
+    <string name="add_contact_desc" msgid="2475604767309086575">"Lisa kontaktidesse"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Rohkem valikuid"</string>
     <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"Ületäite sulgemine"</string>
     <string name="abc_share" msgid="7091841667818715717">"Jaga"</string>
diff --git a/textclassifier/src/main/res/values-eu/strings.xml b/textclassifier/src/main/res/values-eu/strings.xml
index 0cfaf81..5b98777 100644
--- a/textclassifier/src/main/res/values-eu/strings.xml
+++ b/textclassifier/src/main/res/values-eu/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Bidali mezu elektroniko bat"</string>
+    <string name="email" msgid="5568050657313893478">"Bidali mezu bat"</string>
     <string name="email_desc" msgid="6941280589171810022">"Bidali mezu elektroniko bat hautatutako helbidera"</string>
     <string name="dial" msgid="7317293545368448453">"Deitu"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Deitu hautatutako telefono-zenbakira"</string>
diff --git a/textclassifier/src/main/res/values-fa/strings.xml b/textclassifier/src/main/res/values-fa/strings.xml
index f9d259f..0c01dc6 100644
--- a/textclassifier/src/main/res/values-fa/strings.xml
+++ b/textclassifier/src/main/res/values-fa/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ایمیل"</string>
+    <string name="email" msgid="5568050657313893478">"فرستادن ایمیل"</string>
     <string name="email_desc" msgid="6941280589171810022">"ارسال ایمیل به نشانی انتخابی"</string>
-    <string name="dial" msgid="7317293545368448453">"تماس"</string>
+    <string name="dial" msgid="7317293545368448453">"تماس گرفتن"</string>
     <string name="dial_desc" msgid="5129451396208040332">"تماس با شماره تلفن انتخابی"</string>
     <string name="browse" msgid="3733970143542020945">"باز کردن"</string>
     <string name="browse_desc" msgid="3898254913938219011">"باز کردن نشانی وب انتخابی"</string>
-    <string name="sms" msgid="5495416906312064886">"پیام"</string>
+    <string name="sms" msgid="5495416906312064886">"فرستادن پیام"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ارسال پیام به شماره تلفن انتخابی"</string>
     <string name="add_contact" msgid="9005634177208282449">"افزودن"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"افزودن به مخاطبین"</string>
diff --git a/textclassifier/src/main/res/values-fr-rCA/strings.xml b/textclassifier/src/main/res/values-fr-rCA/strings.xml
index d1ff08c..d56c13f 100644
--- a/textclassifier/src/main/res/values-fr-rCA/strings.xml
+++ b/textclassifier/src/main/res/values-fr-rCA/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Courriel"</string>
+    <string name="email" msgid="5568050657313893478">"Envoyer un courriel"</string>
     <string name="email_desc" msgid="6941280589171810022">"Envoyer un courriel à l\'adresse sélectionnée"</string>
     <string name="dial" msgid="7317293545368448453">"Appeler"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Téléphoner au numéro sélectionné"</string>
     <string name="browse" msgid="3733970143542020945">"Ouvrir"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Ouvrir l\'adresse URL sélectionnée"</string>
-    <string name="sms" msgid="5495416906312064886">"Message"</string>
+    <string name="sms" msgid="5495416906312064886">"Envoyer un texto"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Envoyer un message texte au numéro de téléphone sélectionné"</string>
     <string name="add_contact" msgid="9005634177208282449">"Ajouter"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Ajouter aux contacts"</string>
diff --git a/textclassifier/src/main/res/values-fr/strings.xml b/textclassifier/src/main/res/values-fr/strings.xml
index 87beba5..8fc7a9a 100644
--- a/textclassifier/src/main/res/values-fr/strings.xml
+++ b/textclassifier/src/main/res/values-fr/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-mail"</string>
+    <string name="email" msgid="5568050657313893478">"Envoyer un e-mail"</string>
     <string name="email_desc" msgid="6941280589171810022">"Envoyer un e-mail à l\'adresse sélectionnée"</string>
     <string name="dial" msgid="7317293545368448453">"Appeler"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Appeler le numéro de téléphone sélectionné"</string>
     <string name="browse" msgid="3733970143542020945">"Ouvrir"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Ouvrir l\'URL sélectionnée"</string>
-    <string name="sms" msgid="5495416906312064886">"Message"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Envoyer un message au numéro de téléphone sélectionné"</string>
+    <string name="sms" msgid="5495416906312064886">"Envoyer un SMS"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Envoyer un SMS au numéro de téléphone sélectionné"</string>
     <string name="add_contact" msgid="9005634177208282449">"Ajouter"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Ajouter aux contacts"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Autres options"</string>
diff --git a/textclassifier/src/main/res/values-gl/strings.xml b/textclassifier/src/main/res/values-gl/strings.xml
index b17e574..2c4385c 100644
--- a/textclassifier/src/main/res/values-gl/strings.xml
+++ b/textclassifier/src/main/res/values-gl/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Correo electrónico"</string>
+    <string name="email" msgid="5568050657313893478">"Enviar correo e."</string>
     <string name="email_desc" msgid="6941280589171810022">"Envía un correo electrónico ao enderezo seleccionado"</string>
     <string name="dial" msgid="7317293545368448453">"Chamar"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Chama ao número de teléfono seleccionado"</string>
     <string name="browse" msgid="3733970143542020945">"Abrir"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Abre o URL seleccionado"</string>
-    <string name="sms" msgid="5495416906312064886">"Enviar mensaxe"</string>
+    <string name="sms" msgid="5495416906312064886">"Enviar SMS"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Envía unha mensaxe ao número de teléfono seleccionado"</string>
     <string name="add_contact" msgid="9005634177208282449">"Engadir"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Engade o elemento aos contactos"</string>
diff --git a/textclassifier/src/main/res/values-gu/strings.xml b/textclassifier/src/main/res/values-gu/strings.xml
index 84ec7d3..c92cbd17 100644
--- a/textclassifier/src/main/res/values-gu/strings.xml
+++ b/textclassifier/src/main/res/values-gu/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ઇમેઇલ"</string>
+    <string name="email" msgid="5568050657313893478">"ઇમેઇલ કરો"</string>
     <string name="email_desc" msgid="6941280589171810022">"પસંદ કરેલા ઍડ્રેસ પર ઇમેઇલ મોકલો"</string>
-    <string name="dial" msgid="7317293545368448453">"કૉલ"</string>
+    <string name="dial" msgid="7317293545368448453">"કૉલ કરો"</string>
     <string name="dial_desc" msgid="5129451396208040332">"પસંદ કરેલા ફોન નંબર પર કૉલ કરો"</string>
     <string name="browse" msgid="3733970143542020945">"ખોલો"</string>
     <string name="browse_desc" msgid="3898254913938219011">"પસંદ કરેલું URL ખોલો"</string>
-    <string name="sms" msgid="5495416906312064886">"સંદેશ"</string>
+    <string name="sms" msgid="5495416906312064886">"સંદેશ મોકલો"</string>
     <string name="sms_desc" msgid="8293660783374489324">"પસંદ કરેલા ફોન નંબર પર સંદેશ મોકલો"</string>
     <string name="add_contact" msgid="9005634177208282449">"ઉમેરો"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"સંપર્કોમાં ઉમેરો"</string>
diff --git a/textclassifier/src/main/res/values-hi/strings.xml b/textclassifier/src/main/res/values-hi/strings.xml
index 0d3da9e2..e9cf510 100644
--- a/textclassifier/src/main/res/values-hi/strings.xml
+++ b/textclassifier/src/main/res/values-hi/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ईमेल"</string>
+    <string name="email" msgid="5568050657313893478">"ईमेल करें"</string>
     <string name="email_desc" msgid="6941280589171810022">"चुने गए पते पर ईमेल भेजें"</string>
     <string name="dial" msgid="7317293545368448453">"कॉल करें"</string>
     <string name="dial_desc" msgid="5129451396208040332">"चुने गए फ़ोन नंबर पर कॉल करें"</string>
     <string name="browse" msgid="3733970143542020945">"खोलें"</string>
     <string name="browse_desc" msgid="3898254913938219011">"चुना गया यूआरएल खोलें"</string>
-    <string name="sms" msgid="5495416906312064886">"मैसेज"</string>
+    <string name="sms" msgid="5495416906312064886">"मैसेज करें"</string>
     <string name="sms_desc" msgid="8293660783374489324">"चुने गए फ़ोन नंबर को मैसेज करें"</string>
     <string name="add_contact" msgid="9005634177208282449">"जोड़ें"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"संपर्क सूची में जोड़ें"</string>
diff --git a/textclassifier/src/main/res/values-hr/strings.xml b/textclassifier/src/main/res/values-hr/strings.xml
index 197bf47..0a05d23 100644
--- a/textclassifier/src/main/res/values-hr/strings.xml
+++ b/textclassifier/src/main/res/values-hr/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-pošta"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Slanje e-poruke na odabranu adresu"</string>
-    <string name="dial" msgid="7317293545368448453">"Poziv"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Pozivanje odabranog telefonskog broja"</string>
+    <string name="email" msgid="5568050657313893478">"Pošalji e-poštu"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Pošalji e-poštu na odabranu adresu"</string>
+    <string name="dial" msgid="7317293545368448453">"Nazovi"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Nazovi odabrani telefonski broj"</string>
     <string name="browse" msgid="3733970143542020945">"Otvori"</string>
-    <string name="browse_desc" msgid="3898254913938219011">"Otvaranje odabranog URL-a"</string>
-    <string name="sms" msgid="5495416906312064886">"Poruka"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Slanje poruke na odabrani telefonski broj"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"Otvori odabrani URL"</string>
+    <string name="sms" msgid="5495416906312064886">"Pošalji poruku"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Pošalji poruku na odabrani telefonski broj"</string>
     <string name="add_contact" msgid="9005634177208282449">"Dodaj"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Dodaj u kontakte"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Više opcija"</string>
diff --git a/textclassifier/src/main/res/values-hu/strings.xml b/textclassifier/src/main/res/values-hu/strings.xml
index b58ea3c..59d8bf4 100644
--- a/textclassifier/src/main/res/values-hu/strings.xml
+++ b/textclassifier/src/main/res/values-hu/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="email" msgid="5568050657313893478">"E-mail"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Kiválasztott cím elküldése e-mailben"</string>
+    <string name="email_desc" msgid="6941280589171810022">"E-mail küldése a kiválasztott címre"</string>
     <string name="dial" msgid="7317293545368448453">"Hívás"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Kiválasztott telefonszám hívása"</string>
     <string name="browse" msgid="3733970143542020945">"Megnyitás"</string>
diff --git a/textclassifier/src/main/res/values-in/strings.xml b/textclassifier/src/main/res/values-in/strings.xml
index ea214c8..85dbb52 100644
--- a/textclassifier/src/main/res/values-in/strings.xml
+++ b/textclassifier/src/main/res/values-in/strings.xml
@@ -17,13 +17,13 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="email" msgid="5568050657313893478">"Email"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Mengirimkan email ke alamat yang dipilih"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Kirim email ke alamat yang dipilih"</string>
     <string name="dial" msgid="7317293545368448453">"Panggil"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Memanggil nomor telepon yang dipilih"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Panggil nomor telepon yang dipilih"</string>
     <string name="browse" msgid="3733970143542020945">"Buka"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Buka URL yang dipilih"</string>
     <string name="sms" msgid="5495416906312064886">"Pesan"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Mengirimkan SMS ke nomor telepon yang dipilih"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Kirim SMS ke nomor telepon yang dipilih"</string>
     <string name="add_contact" msgid="9005634177208282449">"Tambahkan"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Tambahkan ke kontak"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Opsi lain"</string>
diff --git a/textclassifier/src/main/res/values-iw/strings.xml b/textclassifier/src/main/res/values-iw/strings.xml
index 84df5c1..b577732 100644
--- a/textclassifier/src/main/res/values-iw/strings.xml
+++ b/textclassifier/src/main/res/values-iw/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"אימייל"</string>
+    <string name="email" msgid="5568050657313893478">"התכתבות באימייל"</string>
     <string name="email_desc" msgid="6941280589171810022">"שליחת אימייל לכתובת שנבחרה"</string>
-    <string name="dial" msgid="7317293545368448453">"שיחה"</string>
+    <string name="dial" msgid="7317293545368448453">"ביצוע שיחה"</string>
     <string name="dial_desc" msgid="5129451396208040332">"התקשרות למספר הטלפון שנבחר"</string>
     <string name="browse" msgid="3733970143542020945">"פתיחה"</string>
     <string name="browse_desc" msgid="3898254913938219011">"פתיחה של כתובת האתר שנבחרה"</string>
-    <string name="sms" msgid="5495416906312064886">"הודעה"</string>
+    <string name="sms" msgid="5495416906312064886">"התכתבות בהודעות"</string>
     <string name="sms_desc" msgid="8293660783374489324">"שליחת הודעה למספר הטלפון שנבחר"</string>
     <string name="add_contact" msgid="9005634177208282449">"הוספה"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"הוספה לאנשי הקשר"</string>
diff --git a/textclassifier/src/main/res/values-kk/strings.xml b/textclassifier/src/main/res/values-kk/strings.xml
index 49cae0b..70a0090 100644
--- a/textclassifier/src/main/res/values-kk/strings.xml
+++ b/textclassifier/src/main/res/values-kk/strings.xml
@@ -16,16 +16,16 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Электрондық пошта"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Таңдалған электрондық пошта мекенжайына хабар жіберу"</string>
+    <string name="email" msgid="5568050657313893478">"Эл. поштаны ашу"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Таңдалған мекенжайға хабар жіберу"</string>
     <string name="dial" msgid="7317293545368448453">"Қоңырау шалу"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Таңдалған телефон нөміріне қоңырау шалу"</string>
     <string name="browse" msgid="3733970143542020945">"Ашу"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Таңдалған URL мекенжайын ашу"</string>
-    <string name="sms" msgid="5495416906312064886">"Хабар"</string>
+    <string name="sms" msgid="5495416906312064886">"Хабар жіберу"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Таңдалған телефон нөміріне хабар жіберу"</string>
-    <string name="add_contact" msgid="9005634177208282449">"Қосу"</string>
-    <string name="add_contact_desc" msgid="2475604767309086575">"Контактілерге қосу"</string>
+    <string name="add_contact" msgid="9005634177208282449">"Енгізу"</string>
+    <string name="add_contact_desc" msgid="2475604767309086575">"Контактілер тізіміне енгізу"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Басқа опциялар"</string>
     <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"Қосымша мәзірді жабу"</string>
     <string name="abc_share" msgid="7091841667818715717">"Бөлісу"</string>
diff --git a/textclassifier/src/main/res/values-km/strings.xml b/textclassifier/src/main/res/values-km/strings.xml
index 11fcb96..e71c64b 100644
--- a/textclassifier/src/main/res/values-km/strings.xml
+++ b/textclassifier/src/main/res/values-km/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"អ៊ីមែល"</string>
+    <string name="email" msgid="5568050657313893478">"ផ្ញើអ៊ីមែល"</string>
     <string name="email_desc" msgid="6941280589171810022">"ផ្ញើ​អ៊ីមែល​ទៅ​អាសយដ្ឋាន​ដែល​បាន​ជ្រើសរើស"</string>
-    <string name="dial" msgid="7317293545368448453">"ហៅ"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"ហៅ​ទៅ​លេខ​ទូរសព្ទដែល​បាន​ជ្រើសរើស"</string>
+    <string name="dial" msgid="7317293545368448453">"ហៅទូរសព្ទ"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"ហៅ​ទូរសព្ទ​ទៅ​លេខ​ដែល​បាន​ជ្រើសរើស"</string>
     <string name="browse" msgid="3733970143542020945">"បើក"</string>
     <string name="browse_desc" msgid="3898254913938219011">"បើក URL ដែល​បាន​ជ្រើសរើស"</string>
-    <string name="sms" msgid="5495416906312064886">"សារ"</string>
+    <string name="sms" msgid="5495416906312064886">"ផ្ញើសារ"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ផ្ញើសារ​ទៅ​លេខ​ទូរសព្ទ​ដែល​បាន​ជ្រើសរើស"</string>
     <string name="add_contact" msgid="9005634177208282449">"បញ្ចូល"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"បញ្ចូល​ទៅ​ក្នុង​ទំនាក់ទំនង"</string>
diff --git a/textclassifier/src/main/res/values-kn/strings.xml b/textclassifier/src/main/res/values-kn/strings.xml
index 57769c7..14dfb18 100644
--- a/textclassifier/src/main/res/values-kn/strings.xml
+++ b/textclassifier/src/main/res/values-kn/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ಇಮೇಲ್"</string>
+    <string name="email" msgid="5568050657313893478">"ಇಮೇಲ್ ಮಾಡಿ"</string>
     <string name="email_desc" msgid="6941280589171810022">"ಆಯ್ಕೆಮಾಡಿದ ವಿಳಾಸಕ್ಕೆ ಇಮೇಲ್‌ ಮಾಡಿ"</string>
-    <string name="dial" msgid="7317293545368448453">"ಕರೆ"</string>
+    <string name="dial" msgid="7317293545368448453">"ಕರೆ ಮಾಡಿ"</string>
     <string name="dial_desc" msgid="5129451396208040332">"ಆಯ್ಕೆಮಾಡಿದ ಫೋನ್ ಸಂಖ್ಯೆಗೆ ಕರೆ ಮಾಡಿ"</string>
     <string name="browse" msgid="3733970143542020945">"ತೆರೆಯಿರಿ"</string>
     <string name="browse_desc" msgid="3898254913938219011">"ಆಯ್ಕೆ ಮಾಡಿದ URL ತೆರೆಯಿರಿ"</string>
-    <string name="sms" msgid="5495416906312064886">"ಸಂದೇಶ"</string>
+    <string name="sms" msgid="5495416906312064886">"ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ಆಯ್ಕೆಮಾಡಿದ ಫೋನ್ ಸಂಖ್ಯೆಗೆ ಸಂದೇಶ ಕಳುಹಿಸಿ"</string>
     <string name="add_contact" msgid="9005634177208282449">"ಸೇರಿಸಿ"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"ಸಂಪರ್ಕಗಳಿಗೆ ಸೇರಿಸಿ"</string>
diff --git a/textclassifier/src/main/res/values-ky/strings.xml b/textclassifier/src/main/res/values-ky/strings.xml
index f0da54b..1ae09c4 100644
--- a/textclassifier/src/main/res/values-ky/strings.xml
+++ b/textclassifier/src/main/res/values-ky/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Электрондук почта"</string>
+    <string name="email" msgid="5568050657313893478">"Электрондук кат жөнөтүү"</string>
     <string name="email_desc" msgid="6941280589171810022">"Тандалган дарекке электрондук кат жөнөтүү"</string>
     <string name="dial" msgid="7317293545368448453">"Чалуу"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Тандалган телефон номерине чалуу"</string>
     <string name="browse" msgid="3733970143542020945">"Ачуу"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Тандалган URL\'ди ачуу"</string>
-    <string name="sms" msgid="5495416906312064886">"Билдирүү"</string>
+    <string name="sms" msgid="5495416906312064886">"Билдирүү жөнөтүү"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Тандалган телефон номерине билдирүү жөнөтүү"</string>
     <string name="add_contact" msgid="9005634177208282449">"Кошуу"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Байланыштарга кошуу"</string>
diff --git a/textclassifier/src/main/res/values-lt/strings.xml b/textclassifier/src/main/res/values-lt/strings.xml
index f0affdd..8055d7e 100644
--- a/textclassifier/src/main/res/values-lt/strings.xml
+++ b/textclassifier/src/main/res/values-lt/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"El. paštas"</string>
+    <string name="email" msgid="5568050657313893478">"Rašyti el. laišką"</string>
     <string name="email_desc" msgid="6941280589171810022">"Siųsti el. laišką pasirinktu adresu"</string>
     <string name="dial" msgid="7317293545368448453">"Skambinti"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Skambinti pasirinktu telefono numeriu"</string>
     <string name="browse" msgid="3733970143542020945">"Atidaryti"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Atidaryti pasirinktą URL"</string>
-    <string name="sms" msgid="5495416906312064886">"Pranešimas"</string>
+    <string name="sms" msgid="5495416906312064886">"Rašyti pranešimą"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Siųsti pranešimą pasirinktu telefono numeriu"</string>
     <string name="add_contact" msgid="9005634177208282449">"Pridėti"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Pridėti prie kontaktų"</string>
diff --git a/textclassifier/src/main/res/values-lv/strings.xml b/textclassifier/src/main/res/values-lv/strings.xml
index b3d2599..01b7230 100644
--- a/textclassifier/src/main/res/values-lv/strings.xml
+++ b/textclassifier/src/main/res/values-lv/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-pasta ziņojums"</string>
+    <string name="email" msgid="5568050657313893478">"E-pasts"</string>
     <string name="email_desc" msgid="6941280589171810022">"Nosūtīt e-pasta ziņojumu uz atlasīto adresi"</string>
     <string name="dial" msgid="7317293545368448453">"Zvans"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Zvanīt uz atlasīto tālruņa numuru"</string>
diff --git a/textclassifier/src/main/res/values-mk/strings.xml b/textclassifier/src/main/res/values-mk/strings.xml
index dc5183b..39235db 100644
--- a/textclassifier/src/main/res/values-mk/strings.xml
+++ b/textclassifier/src/main/res/values-mk/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Е-пошта"</string>
+    <string name="email" msgid="5568050657313893478">"Испрати е-пошта"</string>
     <string name="email_desc" msgid="6941280589171810022">"Испрати е-порака до избраната адреса"</string>
     <string name="dial" msgid="7317293545368448453">"Повикај"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Повикај го избраниот телефонски број"</string>
     <string name="browse" msgid="3733970143542020945">"Отвори"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Отвори ја избраната URL-адреса"</string>
-    <string name="sms" msgid="5495416906312064886">"Порака"</string>
+    <string name="sms" msgid="5495416906312064886">"Испрати порака"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Испрати порака до избраниот телефонски број"</string>
     <string name="add_contact" msgid="9005634177208282449">"Додај"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Додај во контакти"</string>
diff --git a/textclassifier/src/main/res/values-ml/strings.xml b/textclassifier/src/main/res/values-ml/strings.xml
index 1d5d95b..b8726a9 100644
--- a/textclassifier/src/main/res/values-ml/strings.xml
+++ b/textclassifier/src/main/res/values-ml/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ഇമെയിൽ"</string>
+    <string name="email" msgid="5568050657313893478">"ഇമെയിൽ അയയ്ക്കൂ"</string>
     <string name="email_desc" msgid="6941280589171810022">"തിരഞ്ഞെടുത്ത വിലാസത്തിലേക്ക് ഇമെയിൽ അയയ്ക്കുക"</string>
     <string name="dial" msgid="7317293545368448453">"വിളിക്കുക"</string>
     <string name="dial_desc" msgid="5129451396208040332">"തിരഞ്ഞെടുത്ത ഫോൺ നമ്പറിലേക്ക് വിളിക്കുക"</string>
     <string name="browse" msgid="3733970143542020945">"തുറക്കുക"</string>
     <string name="browse_desc" msgid="3898254913938219011">"തിരഞ്ഞെടുത്ത URL തുറക്കുക"</string>
-    <string name="sms" msgid="5495416906312064886">"സന്ദേശം"</string>
+    <string name="sms" msgid="5495416906312064886">"സന്ദേശം അയയ്ക്കൂ"</string>
     <string name="sms_desc" msgid="8293660783374489324">"തിരഞ്ഞെടുത്ത ഫോൺ നമ്പറിലേക്ക് സന്ദേശം അയയ്ക്കുക"</string>
     <string name="add_contact" msgid="9005634177208282449">"ചേർക്കുക"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"കോൺടാക്‌റ്റുകളിലേക്ക് ചേർക്കുക"</string>
diff --git a/textclassifier/src/main/res/values-mn/strings.xml b/textclassifier/src/main/res/values-mn/strings.xml
index c4e9d7e..9dfd17b 100644
--- a/textclassifier/src/main/res/values-mn/strings.xml
+++ b/textclassifier/src/main/res/values-mn/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Имэйл"</string>
+    <string name="email" msgid="5568050657313893478">"Имэйл бичих"</string>
     <string name="email_desc" msgid="6941280589171810022">"Сонгосон хаяг руу имэйл илгээх"</string>
     <string name="dial" msgid="7317293545368448453">"Залгах"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Сонгосон утасны дугаар руу залгах"</string>
     <string name="browse" msgid="3733970143542020945">"Нээх"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Сонгосон URL-г нээх"</string>
-    <string name="sms" msgid="5495416906312064886">"Мессеж"</string>
+    <string name="sms" msgid="5495416906312064886">"Мессеж бичих"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Сонгосон утасны дугаар руу мессеж илгээх"</string>
     <string name="add_contact" msgid="9005634177208282449">"Нэмэх"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Харилцагчид нэмэх"</string>
diff --git a/textclassifier/src/main/res/values-mr/strings.xml b/textclassifier/src/main/res/values-mr/strings.xml
index 895d1dc..11a2d55 100644
--- a/textclassifier/src/main/res/values-mr/strings.xml
+++ b/textclassifier/src/main/res/values-mr/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ईमेल"</string>
+    <string name="email" msgid="5568050657313893478">"ईमेल करा"</string>
     <string name="email_desc" msgid="6941280589171810022">"निवडलेल्या अॅड्रेसवर ईमेल करा"</string>
     <string name="dial" msgid="7317293545368448453">"कॉल करा"</string>
     <string name="dial_desc" msgid="5129451396208040332">"निवडलेल्या फोन नंबरवर कॉल करा"</string>
     <string name="browse" msgid="3733970143542020945">"उघडा"</string>
     <string name="browse_desc" msgid="3898254913938219011">"निवडलेली URL उघडा"</string>
-    <string name="sms" msgid="5495416906312064886">"मेसेज"</string>
+    <string name="sms" msgid="5495416906312064886">"मेसेज करा"</string>
     <string name="sms_desc" msgid="8293660783374489324">"निवडलेल्या फोन नंबरवर मेसेज करा"</string>
     <string name="add_contact" msgid="9005634177208282449">"जोडा"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"संपर्कांमध्ये जोडा"</string>
diff --git a/textclassifier/src/main/res/values-my/strings.xml b/textclassifier/src/main/res/values-my/strings.xml
index 9a159f0..78edba2 100644
--- a/textclassifier/src/main/res/values-my/strings.xml
+++ b/textclassifier/src/main/res/values-my/strings.xml
@@ -16,16 +16,16 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"အီးမေးလ်"</string>
+    <string name="email" msgid="5568050657313893478">"အီးမေးလ်ပို့ရန်"</string>
     <string name="email_desc" msgid="6941280589171810022">"ရွေးထားသည့် လိပ်စာသို့ အီးမေးလ်ပို့ရန်"</string>
     <string name="dial" msgid="7317293545368448453">"ခေါ်ဆိုရန်"</string>
     <string name="dial_desc" msgid="5129451396208040332">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို ခေါ်ရန်"</string>
     <string name="browse" msgid="3733970143542020945">"ဖွင့်ရန်"</string>
     <string name="browse_desc" msgid="3898254913938219011">"ရွေးထားသည့် URL ကို ဖွင့်ရန်"</string>
-    <string name="sms" msgid="5495416906312064886">"မက်ဆေ့ဂျ်"</string>
+    <string name="sms" msgid="5495416906312064886">"မက်ဆေ့ဂျ်ပို့ရန်"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ရွေးထားသည့် ဖုန်းနံပါတ်ကို မက်ဆေ့ဂျ်ပို့ရန်"</string>
     <string name="add_contact" msgid="9005634177208282449">"ထည့်ရန်"</string>
-    <string name="add_contact_desc" msgid="2475604767309086575">"ထည့်ရန်"</string>
+    <string name="add_contact_desc" msgid="2475604767309086575">"အဆက်အသွယ်များသို့ ထည့်ရန်"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"နောက်ထပ် ရွေးစရာများ"</string>
     <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"အပိုမီနူးကို ပိတ်ရန်"</string>
     <string name="abc_share" msgid="7091841667818715717">"မျှဝေရန်"</string>
diff --git a/textclassifier/src/main/res/values-nb/strings.xml b/textclassifier/src/main/res/values-nb/strings.xml
index 31bac6e..1959f52 100644
--- a/textclassifier/src/main/res/values-nb/strings.xml
+++ b/textclassifier/src/main/res/values-nb/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-post"</string>
+    <string name="email" msgid="5568050657313893478">"Send e-post"</string>
     <string name="email_desc" msgid="6941280589171810022">"Send e-post til den valgte adressen"</string>
     <string name="dial" msgid="7317293545368448453">"Ring"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Ring det valgte telefonnummeret"</string>
     <string name="browse" msgid="3733970143542020945">"Åpne"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Åpne den valgte nettadressen"</string>
-    <string name="sms" msgid="5495416906312064886">"Melding"</string>
+    <string name="sms" msgid="5495416906312064886">"Send melding"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Send melding til det valgte telefonnummeret"</string>
     <string name="add_contact" msgid="9005634177208282449">"Legg til"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Legg til i kontakter"</string>
diff --git a/textclassifier/src/main/res/values-ne/strings.xml b/textclassifier/src/main/res/values-ne/strings.xml
index e33e441..34325fe 100644
--- a/textclassifier/src/main/res/values-ne/strings.xml
+++ b/textclassifier/src/main/res/values-ne/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"इमेल"</string>
+    <string name="email" msgid="5568050657313893478">"इमेल गर्नुहोस्"</string>
     <string name="email_desc" msgid="6941280589171810022">"चयन गरिएको ठेगानामा इमेल पठाउनुहोस्"</string>
     <string name="dial" msgid="7317293545368448453">"कल गर्नुहोस्"</string>
     <string name="dial_desc" msgid="5129451396208040332">"चयन गरिएको फोन नम्बरमा कल गर्नुहोस्"</string>
     <string name="browse" msgid="3733970143542020945">"खोल्नुहोस्"</string>
     <string name="browse_desc" msgid="3898254913938219011">"चयन गरिएको URL खोल्नुहोस्"</string>
-    <string name="sms" msgid="5495416906312064886">"सन्देश"</string>
+    <string name="sms" msgid="5495416906312064886">"सन्देश पठाउनुहोस्"</string>
     <string name="sms_desc" msgid="8293660783374489324">"चयन गरिएको फोन नम्बरमा सन्देश पठाउनुहोस्‌"</string>
     <string name="add_contact" msgid="9005634177208282449">"थप्नुहोस्"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"सम्पर्कहरूमा थप्नुहोस्"</string>
diff --git a/textclassifier/src/main/res/values-or/strings.xml b/textclassifier/src/main/res/values-or/strings.xml
index 3454944..00a45bb 100644
--- a/textclassifier/src/main/res/values-or/strings.xml
+++ b/textclassifier/src/main/res/values-or/strings.xml
@@ -18,14 +18,14 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="email" msgid="5568050657313893478">"ଇମେଲ୍"</string>
     <string name="email_desc" msgid="6941280589171810022">"ଚୟନିତ ଠିକଣାକୁ ଇମେଲ୍‌ ପଠାନ୍ତୁ"</string>
-    <string name="dial" msgid="7317293545368448453">"କଲ୍ କରନ୍ତୁ"</string>
+    <string name="dial" msgid="7317293545368448453">"କଲ୍‍"</string>
     <string name="dial_desc" msgid="5129451396208040332">"ଚୟନିତ ଫୋନ୍‌ ନମ୍ବର୍‌କୁ କଲ୍‌ କରନ୍ତୁ"</string>
     <string name="browse" msgid="3733970143542020945">"ଖୋଲନ୍ତୁ"</string>
     <string name="browse_desc" msgid="3898254913938219011">"ଚୟନିତ URL ଖୋଲନ୍ତୁ"</string>
     <string name="sms" msgid="5495416906312064886">"ମେସେଜ୍"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ଚୟନିତ ଫୋନ୍‌ ନମ୍ବର୍‌କୁ ମେସେଜ୍‌ ପଠାନ୍ତୁ"</string>
-    <string name="add_contact" msgid="9005634177208282449">"ଯୋଡ଼ନ୍ତୁ"</string>
-    <string name="add_contact_desc" msgid="2475604767309086575">"ଯୋଗାଯୋଗରେ ଯୋଡ଼ନ୍ତୁ"</string>
+    <string name="add_contact" msgid="9005634177208282449">"ଯୋଗ କରନ୍ତୁ"</string>
+    <string name="add_contact_desc" msgid="2475604767309086575">"ଯୋଗାଯୋଗରେ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"ଅଧିକ ବିକଳ୍ପ"</string>
     <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"ଓଭରଫ୍ଲୋ ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="abc_share" msgid="7091841667818715717">"ଶେୟାର୍ କରନ୍ତୁ"</string>
diff --git a/textclassifier/src/main/res/values-pa/strings.xml b/textclassifier/src/main/res/values-pa/strings.xml
index 6831001..004bc9c 100644
--- a/textclassifier/src/main/res/values-pa/strings.xml
+++ b/textclassifier/src/main/res/values-pa/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ਈਮੇਲ"</string>
+    <string name="email" msgid="5568050657313893478">"ਈਮੇਲ ਖੋਲ੍ਹੋ"</string>
     <string name="email_desc" msgid="6941280589171810022">"ਚੁਣੇ ਹੋਏ ਪਤੇ \'ਤੇ ਈਮੇਲ ਭੇਜੋ"</string>
     <string name="dial" msgid="7317293545368448453">"ਕਾਲ ਕਰੋ"</string>
     <string name="dial_desc" msgid="5129451396208040332">"ਚੁਣੇ ਗਏ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਕਾਲ ਕਰੋ"</string>
     <string name="browse" msgid="3733970143542020945">"ਖੋਲ੍ਹੋ"</string>
     <string name="browse_desc" msgid="3898254913938219011">"ਚੁਣਿਆ ਗਿਆ URL ਖੋਲ੍ਹੋ"</string>
-    <string name="sms" msgid="5495416906312064886">"ਸੁਨੇਹਾ"</string>
+    <string name="sms" msgid="5495416906312064886">"ਸੁਨੇਹਾ ਖੋਲ੍ਹੋ"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ਚੁਣੇ ਗਏ ਫ਼ੋਨ ਨੰਬਰ \'ਤੇ ਸੁਨੇਹਾ ਭੇਜੋ"</string>
     <string name="add_contact" msgid="9005634177208282449">"ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
diff --git a/textclassifier/src/main/res/values-pt-rPT/strings.xml b/textclassifier/src/main/res/values-pt-rPT/strings.xml
index 3eef4f1e..c1ff32b 100644
--- a/textclassifier/src/main/res/values-pt-rPT/strings.xml
+++ b/textclassifier/src/main/res/values-pt-rPT/strings.xml
@@ -22,7 +22,7 @@
     <string name="dial_desc" msgid="5129451396208040332">"Ligar para o número de telefone selecionado"</string>
     <string name="browse" msgid="3733970143542020945">"Abrir"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Abrir o URL selecionado"</string>
-    <string name="sms" msgid="5495416906312064886">"Mensagem"</string>
+    <string name="sms" msgid="5495416906312064886">"Enviar mensagem"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Enviar uma mensagem para o número de telefone selecionado"</string>
     <string name="add_contact" msgid="9005634177208282449">"Adicionar"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Adicionar aos contactos"</string>
diff --git a/textclassifier/src/main/res/values-ro/strings.xml b/textclassifier/src/main/res/values-ro/strings.xml
index d41f9b7..3656640 100644
--- a/textclassifier/src/main/res/values-ro/strings.xml
+++ b/textclassifier/src/main/res/values-ro/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-mail"</string>
+    <string name="email" msgid="5568050657313893478">"Trimiteți un e-mail"</string>
     <string name="email_desc" msgid="6941280589171810022">"Trimiteți un e-mail la adresa selectată"</string>
-    <string name="dial" msgid="7317293545368448453">"Apelați"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Apelați numărul de telefon selectat"</string>
+    <string name="dial" msgid="7317293545368448453">"Sunați"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Sunați la numărul de telefon selectat"</string>
     <string name="browse" msgid="3733970143542020945">"Deschideți"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Deschideți adresa URL selectată"</string>
-    <string name="sms" msgid="5495416906312064886">"Mesaj"</string>
+    <string name="sms" msgid="5495416906312064886">"Trimiteți mesaj"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Trimiteți un mesaj la numărul de telefon selectat"</string>
     <string name="add_contact" msgid="9005634177208282449">"Adăugați"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Adăugați în agendă"</string>
diff --git a/textclassifier/src/main/res/values-ru/strings.xml b/textclassifier/src/main/res/values-ru/strings.xml
index a34d303..c3e4610 100644
--- a/textclassifier/src/main/res/values-ru/strings.xml
+++ b/textclassifier/src/main/res/values-ru/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Написать по почте"</string>
+    <string name="email" msgid="5568050657313893478">"Написать письмо"</string>
     <string name="email_desc" msgid="6941280589171810022">"Отправить письмо выбранному адресату"</string>
     <string name="dial" msgid="7317293545368448453">"Позвонить"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Позвонить по выбранному номеру"</string>
diff --git a/textclassifier/src/main/res/values-sl/strings.xml b/textclassifier/src/main/res/values-sl/strings.xml
index 7a9981f..9cf16f6 100644
--- a/textclassifier/src/main/res/values-sl/strings.xml
+++ b/textclassifier/src/main/res/values-sl/strings.xml
@@ -16,16 +16,16 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-pošta"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Pošiljanje e-poštnega sporočila na izbrani naslov"</string>
-    <string name="dial" msgid="7317293545368448453">"Telefonski klic"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Klic izbrane telefonske številke"</string>
-    <string name="browse" msgid="3733970143542020945">"Odpiranje"</string>
-    <string name="browse_desc" msgid="3898254913938219011">"Odpiranje izbranega URL-ja"</string>
-    <string name="sms" msgid="5495416906312064886">"Sporočilo"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Pošiljanje sporočila na izbrano telefonsko številko"</string>
-    <string name="add_contact" msgid="9005634177208282449">"Dodajanje"</string>
-    <string name="add_contact_desc" msgid="2475604767309086575">"Dodajanje med stike"</string>
+    <string name="email" msgid="5568050657313893478">"Odpri e-pošto"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Pošlji e-poštno sporočilo na izbrani naslov"</string>
+    <string name="dial" msgid="7317293545368448453">"Pokliči"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Pokliči izbrano telefonsko številko"</string>
+    <string name="browse" msgid="3733970143542020945">"Odpri"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"Odpri izbrani URL"</string>
+    <string name="sms" msgid="5495416906312064886">"Pošlji SMS"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Pošlji SMS na izbrano telefonsko številko"</string>
+    <string name="add_contact" msgid="9005634177208282449">"Dodaj"</string>
+    <string name="add_contact_desc" msgid="2475604767309086575">"Dodaj med stike"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Več možnosti"</string>
     <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"Zapiranje dodatnih elementov"</string>
     <string name="abc_share" msgid="7091841667818715717">"Skup. raba"</string>
diff --git a/textclassifier/src/main/res/values-sq/strings.xml b/textclassifier/src/main/res/values-sq/strings.xml
index 2735efc..90f6619 100644
--- a/textclassifier/src/main/res/values-sq/strings.xml
+++ b/textclassifier/src/main/res/values-sq/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Email"</string>
+    <string name="email" msgid="5568050657313893478">"Dërgo email"</string>
     <string name="email_desc" msgid="6941280589171810022">"Dërgo email tek adresa e zgjedhur"</string>
     <string name="dial" msgid="7317293545368448453">"Telefono"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Telefono në numrin e zgjedhur të telefonit"</string>
     <string name="browse" msgid="3733970143542020945">"Hap"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Hap URL-në e zgjedhur"</string>
-    <string name="sms" msgid="5495416906312064886">"Mesazh"</string>
+    <string name="sms" msgid="5495416906312064886">"Dërgo mesazh"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Dërgo mesazh te numri i zgjedhur i telefonit"</string>
     <string name="add_contact" msgid="9005634177208282449">"Shto"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Shto te kontaktet"</string>
diff --git a/textclassifier/src/main/res/values-sv/strings.xml b/textclassifier/src/main/res/values-sv/strings.xml
index 483a79f..8a677c4 100644
--- a/textclassifier/src/main/res/values-sv/strings.xml
+++ b/textclassifier/src/main/res/values-sv/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"E-post"</string>
+    <string name="email" msgid="5568050657313893478">"Skicka e-post"</string>
     <string name="email_desc" msgid="6941280589171810022">"Skicka e-post till vald adress"</string>
     <string name="dial" msgid="7317293545368448453">"Ring"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Ring valt telefonnummer"</string>
diff --git a/textclassifier/src/main/res/values-sw/strings.xml b/textclassifier/src/main/res/values-sw/strings.xml
index 0171aa6..e940614 100644
--- a/textclassifier/src/main/res/values-sw/strings.xml
+++ b/textclassifier/src/main/res/values-sw/strings.xml
@@ -16,9 +16,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Barua pepe"</string>
+    <string name="email" msgid="5568050657313893478">"Tuma barua pepe"</string>
     <string name="email_desc" msgid="6941280589171810022">"Tuma barua pepe kwa anwani uliyochagua"</string>
-    <string name="dial" msgid="7317293545368448453">"Simu"</string>
+    <string name="dial" msgid="7317293545368448453">"Piga simu"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Piga simu kwa nambari uliyochagua"</string>
     <string name="browse" msgid="3733970143542020945">"Fungua"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Fungua URL uliyochagua"</string>
diff --git a/textclassifier/src/main/res/values-ta/strings.xml b/textclassifier/src/main/res/values-ta/strings.xml
index 4f3123b..c90ac79 100644
--- a/textclassifier/src/main/res/values-ta/strings.xml
+++ b/textclassifier/src/main/res/values-ta/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"மின்னஞ்சல்"</string>
+    <string name="email" msgid="5568050657313893478">"மின்னஞ்சல் அனுப்பு"</string>
     <string name="email_desc" msgid="6941280589171810022">"தேர்ந்தெடுத்த முகவரிக்கு மின்னஞ்சலை அனுப்பும்"</string>
     <string name="dial" msgid="7317293545368448453">"அழை"</string>
     <string name="dial_desc" msgid="5129451396208040332">"தேர்ந்தெடுத்த ஃபோன் எண்ணை அழைக்கும்"</string>
     <string name="browse" msgid="3733970143542020945">"திற"</string>
     <string name="browse_desc" msgid="3898254913938219011">"தேர்ந்தெடுத்த URLலைத் திறக்கும்"</string>
-    <string name="sms" msgid="5495416906312064886">"மெசேஜ்"</string>
+    <string name="sms" msgid="5495416906312064886">"செய்தி அனுப்பு"</string>
     <string name="sms_desc" msgid="8293660783374489324">"தேர்ந்தெடுத்த ஃபோன் எண்ணிற்கு மெசேஜ் அனுப்பும்"</string>
     <string name="add_contact" msgid="9005634177208282449">"சேர்"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"தொடர்புகளில் சேர்க்கும்"</string>
diff --git a/textclassifier/src/main/res/values-te/strings.xml b/textclassifier/src/main/res/values-te/strings.xml
index 4adcfbf..25e41ff 100644
--- a/textclassifier/src/main/res/values-te/strings.xml
+++ b/textclassifier/src/main/res/values-te/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ఇమెయిల్"</string>
+    <string name="email" msgid="5568050657313893478">"ఇమెయిల్ పంపు"</string>
     <string name="email_desc" msgid="6941280589171810022">"ఎంచుకున్న చిరునామాకు ఇమెయిల్‌ను పంపుతుంది"</string>
-    <string name="dial" msgid="7317293545368448453">"కాల్"</string>
+    <string name="dial" msgid="7317293545368448453">"కాల్ చేయి"</string>
     <string name="dial_desc" msgid="5129451396208040332">"ఎంచుకున్న ఫోన్ నంబర్‌కు కాల్ చేస్తుంది"</string>
     <string name="browse" msgid="3733970143542020945">"తెరువు"</string>
     <string name="browse_desc" msgid="3898254913938219011">"ఎంచుకున్న URLని తెరుస్తుంది"</string>
-    <string name="sms" msgid="5495416906312064886">"సందేశం"</string>
+    <string name="sms" msgid="5495416906312064886">"సందేశం పంపు"</string>
     <string name="sms_desc" msgid="8293660783374489324">"ఎంచుకున్న ఫోన్ నంబర్‌కి సందేశం పంపుతుంది"</string>
     <string name="add_contact" msgid="9005634177208282449">"జోడించు"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"పరిచయాలకు జోడిస్తుంది"</string>
diff --git a/textclassifier/src/main/res/values-tl/strings.xml b/textclassifier/src/main/res/values-tl/strings.xml
index 734524e..f75b004 100644
--- a/textclassifier/src/main/res/values-tl/strings.xml
+++ b/textclassifier/src/main/res/values-tl/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Email"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Mag-email sa napiling address"</string>
-    <string name="dial" msgid="7317293545368448453">"Tawag"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Tawagan ang napiling numero ng telepono"</string>
+    <string name="email" msgid="5568050657313893478">"Mag-email"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Mag-email sa piniling address"</string>
+    <string name="dial" msgid="7317293545368448453">"Tumawag"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Tawagan ang piniling numero ng telepono"</string>
     <string name="browse" msgid="3733970143542020945">"Buksan"</string>
-    <string name="browse_desc" msgid="3898254913938219011">"Buksan ang napiling URL"</string>
-    <string name="sms" msgid="5495416906312064886">"Mensahe"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"Padalhan ng mensahe ang napiling numero ng telepono"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"Buksan ang piniling URL"</string>
+    <string name="sms" msgid="5495416906312064886">"Magmensahe"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"Padalhan ng mensahe ang piniling numero ng telepono"</string>
     <string name="add_contact" msgid="9005634177208282449">"Magdagdag"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Idagdag sa mga contact"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Higit pang opsyon"</string>
diff --git a/textclassifier/src/main/res/values-tr/strings.xml b/textclassifier/src/main/res/values-tr/strings.xml
index be12836..044d43f 100644
--- a/textclassifier/src/main/res/values-tr/strings.xml
+++ b/textclassifier/src/main/res/values-tr/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="email" msgid="5568050657313893478">"E-posta"</string>
     <string name="email_desc" msgid="6941280589171810022">"Seçilen adrese e-posta gönder"</string>
-    <string name="dial" msgid="7317293545368448453">"Ara"</string>
+    <string name="dial" msgid="7317293545368448453">"Telefon et"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Seçilen telefon numarasını ara"</string>
     <string name="browse" msgid="3733970143542020945">"Aç"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Seçilen URL\'yi aç"</string>
diff --git a/textclassifier/src/main/res/values-uk/strings.xml b/textclassifier/src/main/res/values-uk/strings.xml
index 241021a..a40df77 100644
--- a/textclassifier/src/main/res/values-uk/strings.xml
+++ b/textclassifier/src/main/res/values-uk/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"Електронна адреса"</string>
+    <string name="email" msgid="5568050657313893478">"Написати лист"</string>
     <string name="email_desc" msgid="6941280589171810022">"Надіслати електронний лист на вибрану адресу"</string>
-    <string name="dial" msgid="7317293545368448453">"Виклик"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"Набрати вибраний номер телефону"</string>
+    <string name="dial" msgid="7317293545368448453">"Телефонувати"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"Телефонувати за вибраним номером"</string>
     <string name="browse" msgid="3733970143542020945">"Відкрити"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Відкрити вибрану URL-адресу"</string>
-    <string name="sms" msgid="5495416906312064886">"Повідомлення"</string>
+    <string name="sms" msgid="5495416906312064886">"Написати SMS"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Надіслати повідомлення за вибраним номером телефону"</string>
     <string name="add_contact" msgid="9005634177208282449">"Додати"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Додати в контакти"</string>
diff --git a/textclassifier/src/main/res/values-ur/strings.xml b/textclassifier/src/main/res/values-ur/strings.xml
index 55b2e6d..acdb424 100644
--- a/textclassifier/src/main/res/values-ur/strings.xml
+++ b/textclassifier/src/main/res/values-ur/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"ای میل"</string>
+    <string name="email" msgid="5568050657313893478">"ای میل بھیجیں"</string>
     <string name="email_desc" msgid="6941280589171810022">"منتخب کردہ پتے پر ای میل کریں"</string>
-    <string name="dial" msgid="7317293545368448453">"کال"</string>
+    <string name="dial" msgid="7317293545368448453">"کال کریں"</string>
     <string name="dial_desc" msgid="5129451396208040332">"منتخب کردہ فون نمبر پر کال کریں"</string>
     <string name="browse" msgid="3733970143542020945">"کھولیں"</string>
     <string name="browse_desc" msgid="3898254913938219011">"‏منتخب کردہ URL کھولیں"</string>
-    <string name="sms" msgid="5495416906312064886">"پیغام"</string>
+    <string name="sms" msgid="5495416906312064886">"پیغام بھیجیں"</string>
     <string name="sms_desc" msgid="8293660783374489324">"منتخب کردہ فون نمبر پر پیغام بھیجیں"</string>
     <string name="add_contact" msgid="9005634177208282449">"شامل کریں"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"رابطوں میں شامل کریں"</string>
diff --git a/textclassifier/src/main/res/values-uz/strings.xml b/textclassifier/src/main/res/values-uz/strings.xml
index 74d563d..3f6b781 100644
--- a/textclassifier/src/main/res/values-uz/strings.xml
+++ b/textclassifier/src/main/res/values-uz/strings.xml
@@ -17,14 +17,14 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="email" msgid="5568050657313893478">"E-pochta"</string>
-    <string name="email_desc" msgid="6941280589171810022">"Belgilangan manzilga xat yuborish"</string>
+    <string name="email_desc" msgid="6941280589171810022">"Belgilangan e-pochta manziliga xat yuborish"</string>
     <string name="dial" msgid="7317293545368448453">"Chaqiruv"</string>
     <string name="dial_desc" msgid="5129451396208040332">"Belgilangan raqamga telefon qilish"</string>
     <string name="browse" msgid="3733970143542020945">"Ochish"</string>
     <string name="browse_desc" msgid="3898254913938219011">"Belgilangan URL manzilini ochish"</string>
-    <string name="sms" msgid="5495416906312064886">"Xabar"</string>
+    <string name="sms" msgid="5495416906312064886">"SMS yozish"</string>
     <string name="sms_desc" msgid="8293660783374489324">"Belgilangan telefon raqamiga SMS yuborish"</string>
-    <string name="add_contact" msgid="9005634177208282449">"Qo‘shish"</string>
+    <string name="add_contact" msgid="9005634177208282449">"Saqlab olish"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"Kontaktlarga saqlash"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"Yana"</string>
     <string name="floating_toolbar_close_overflow_description" msgid="6243666280435354232">"Qalqib turuvchi asboblar panelini yopish"</string>
diff --git a/textclassifier/src/main/res/values-zh-rCN/strings.xml b/textclassifier/src/main/res/values-zh-rCN/strings.xml
index a990409..afa89ca 100644
--- a/textclassifier/src/main/res/values-zh-rCN/strings.xml
+++ b/textclassifier/src/main/res/values-zh-rCN/strings.xml
@@ -16,13 +16,13 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"电子邮件"</string>
+    <string name="email" msgid="5568050657313893478">"发送电子邮件"</string>
     <string name="email_desc" msgid="6941280589171810022">"将电子邮件发送至所选地址"</string>
     <string name="dial" msgid="7317293545368448453">"通话"</string>
     <string name="dial_desc" msgid="5129451396208040332">"拨打所选电话号码"</string>
     <string name="browse" msgid="3733970143542020945">"打开"</string>
     <string name="browse_desc" msgid="3898254913938219011">"打开所选网址"</string>
-    <string name="sms" msgid="5495416906312064886">"短信"</string>
+    <string name="sms" msgid="5495416906312064886">"发短信"</string>
     <string name="sms_desc" msgid="8293660783374489324">"将短信发送至所选电话号码"</string>
     <string name="add_contact" msgid="9005634177208282449">"添加"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"添加到通讯录"</string>
diff --git a/textclassifier/src/main/res/values-zh-rHK/strings.xml b/textclassifier/src/main/res/values-zh-rHK/strings.xml
index 946fd6c..2ada5ec 100644
--- a/textclassifier/src/main/res/values-zh-rHK/strings.xml
+++ b/textclassifier/src/main/res/values-zh-rHK/strings.xml
@@ -22,7 +22,7 @@
     <string name="dial_desc" msgid="5129451396208040332">"打指定嘅電話號碼"</string>
     <string name="browse" msgid="3733970143542020945">"開啟"</string>
     <string name="browse_desc" msgid="3898254913938219011">"打開指定網址"</string>
-    <string name="sms" msgid="5495416906312064886">"訊息"</string>
+    <string name="sms" msgid="5495416906312064886">"發短訊"</string>
     <string name="sms_desc" msgid="8293660783374489324">"傳短訊去指定電話號碼"</string>
     <string name="add_contact" msgid="9005634177208282449">"新增"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"加入聯絡人"</string>
diff --git a/textclassifier/src/main/res/values-zh-rTW/strings.xml b/textclassifier/src/main/res/values-zh-rTW/strings.xml
index 4fc184f..d2bd3ab 100644
--- a/textclassifier/src/main/res/values-zh-rTW/strings.xml
+++ b/textclassifier/src/main/res/values-zh-rTW/strings.xml
@@ -16,14 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="email" msgid="5568050657313893478">"電子郵件"</string>
-    <string name="email_desc" msgid="6941280589171810022">"將電子郵件寄到所選地址"</string>
-    <string name="dial" msgid="7317293545368448453">"撥號"</string>
-    <string name="dial_desc" msgid="5129451396208040332">"撥打所選電話號碼"</string>
+    <string name="email" msgid="5568050657313893478">"發送電子郵件"</string>
+    <string name="email_desc" msgid="6941280589171810022">"將電子郵件寄到選取的地址"</string>
+    <string name="dial" msgid="7317293545368448453">"撥號通話"</string>
+    <string name="dial_desc" msgid="5129451396208040332">"撥打選取的電話號碼"</string>
     <string name="browse" msgid="3733970143542020945">"開啟"</string>
-    <string name="browse_desc" msgid="3898254913938219011">"開啟所選網址"</string>
-    <string name="sms" msgid="5495416906312064886">"訊息"</string>
-    <string name="sms_desc" msgid="8293660783374489324">"將訊息傳送到所選電話號碼"</string>
+    <string name="browse_desc" msgid="3898254913938219011">"開啟選取的網址"</string>
+    <string name="sms" msgid="5495416906312064886">"發送訊息"</string>
+    <string name="sms_desc" msgid="8293660783374489324">"將訊息傳送到選取的電話號碼"</string>
     <string name="add_contact" msgid="9005634177208282449">"新增"</string>
     <string name="add_contact_desc" msgid="2475604767309086575">"新增至聯絡人"</string>
     <string name="floating_toolbar_open_overflow_description" msgid="1187148927509077545">"更多選項"</string>
diff --git a/viewpager/src/main/java/androidx/viewpager/widget/ViewPager.java b/viewpager/src/main/java/androidx/viewpager/widget/ViewPager.java
index c067c75..d1efc7d 100644
--- a/viewpager/src/main/java/androidx/viewpager/widget/ViewPager.java
+++ b/viewpager/src/main/java/androidx/viewpager/widget/ViewPager.java
@@ -119,6 +119,10 @@
 
     private static final int MIN_FLING_VELOCITY = 400; // dips
 
+    /** Class name may be obfuscated by Proguard. Hardcode the string for accessibility usage. */
+    private static final String ACCESSIBILITY_CLASS_NAME =
+            "androidx.viewpager.widget.ViewPager";
+
     static final int[] LAYOUT_ATTRS = new int[] {
         android.R.attr.layout_gravity
     };
@@ -3048,7 +3052,7 @@
         @Override
         public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
             super.onInitializeAccessibilityEvent(host, event);
-            event.setClassName(ViewPager.class.getName());
+            event.setClassName(ACCESSIBILITY_CLASS_NAME);
             event.setScrollable(canScroll());
             if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
                 event.setItemCount(mAdapter.getCount());
@@ -3060,7 +3064,7 @@
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
             super.onInitializeAccessibilityNodeInfo(host, info);
-            info.setClassName(ViewPager.class.getName());
+            info.setClassName(ACCESSIBILITY_CLASS_NAME);
             info.setScrollable(canScroll());
             if (canScrollHorizontally(1)) {
                 info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
diff --git a/samples/ViewPager2Demos/OWNERS b/viewpager2/integration-tests/testapp/OWNERS
similarity index 100%
rename from samples/ViewPager2Demos/OWNERS
rename to viewpager2/integration-tests/testapp/OWNERS
diff --git a/samples/ViewPager2Demos/build.gradle b/viewpager2/integration-tests/testapp/build.gradle
similarity index 66%
rename from samples/ViewPager2Demos/build.gradle
rename to viewpager2/integration-tests/testapp/build.gradle
index 902c3e6..2b3a756 100644
--- a/samples/ViewPager2Demos/build.gradle
+++ b/viewpager2/integration-tests/testapp/build.gradle
@@ -14,8 +14,12 @@
  * limitations under the License.
  */
 
+import static androidx.build.dependencies.DependenciesKt.ARCH_LIFECYCLE_EXTENSIONS
+import static androidx.build.dependencies.DependenciesKt.ESPRESSO_CORE
 import static androidx.build.dependencies.DependenciesKt.KOTLIN_STDLIB
 import static androidx.build.dependencies.DependenciesKt.SUPPORT_DESIGN
+import static androidx.build.dependencies.DependenciesKt.TEST_EXT_JUNIT
+import static androidx.build.dependencies.DependenciesKt.TEST_RULES
 
 plugins {
     id("AndroidXPlugin")
@@ -27,7 +31,11 @@
 dependencies {
     api(KOTLIN_STDLIB)
     implementation(project(":viewpager2"))
-    implementation(project(":lifecycle:lifecycle-extensions"))
-    implementation(SUPPORT_DESIGN, libs.exclude_for_material)
+    implementation(ARCH_LIFECYCLE_EXTENSIONS)
+    implementation(SUPPORT_DESIGN)
     implementation(project(":cardview"))
+
+    androidTestImplementation(TEST_RULES)
+    androidTestImplementation(TEST_EXT_JUNIT)
+    androidTestImplementation(ESPRESSO_CORE)
 }
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/BaseTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/BaseTest.kt
new file mode 100644
index 0000000..6d1ef0e
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/BaseTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import android.view.View
+import androidx.annotation.LayoutRes
+import androidx.fragment.app.FragmentActivity
+import androidx.test.espresso.Espresso.onIdle
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.rule.ActivityTestRule
+import androidx.viewpager2.widget.ViewPager2
+import com.example.androidx.viewpager2.test.onCurrentPage
+import com.example.androidx.viewpager2.test.onViewPager
+import com.example.androidx.viewpager2.test.swipeNext
+import com.example.androidx.viewpager2.test.swipePrevious
+import com.example.androidx.viewpager2.test.ViewPagerIdleWatcher
+import org.hamcrest.Matcher
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+
+abstract class BaseTest<T : FragmentActivity>(clazz: Class<T>) {
+    @Rule
+    @JvmField
+    var activityTestRule = ActivityTestRule(clazz)
+
+    @get:LayoutRes
+    abstract val layoutId: Int
+
+    lateinit var idleWatcher: ViewPagerIdleWatcher
+
+    @Before
+    open fun setUp() {
+        val viewPager = activityTestRule.activity.findViewById<ViewPager2>(layoutId)
+        idleWatcher = ViewPagerIdleWatcher.registerViewPagerIdlingResource(viewPager)
+    }
+
+    @After
+    open fun tearDown() {
+        idleWatcher.unregister()
+    }
+
+    fun swipeToNextPage(action: (() -> Unit)? = null) {
+        onViewPager().perform(swipeNext())
+        if (action != null) action()
+        idleWatcher.waitForIdle()
+        onIdle()
+    }
+
+    fun swipeToPreviousPage(action: (() -> Unit)? = null) {
+        onViewPager().perform(swipePrevious())
+        if (action != null) action()
+        idleWatcher.waitForIdle()
+        onIdle()
+    }
+
+    fun verifyCurrentPage(pageText: String) {
+        verifyCurrentPage(hasDescendant(withText(pageText)))
+    }
+
+    fun verifyCurrentPage(matcher: Matcher<View>) {
+        onCurrentPage().check(matches(matcher))
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionBaseTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionBaseTest.kt
new file mode 100644
index 0000000..3dc1d78
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionBaseTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import androidx.test.espresso.Espresso.onData
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.equalTo
+import org.junit.Test
+
+abstract class MutableCollectionBaseTest<T : MutableCollectionBaseActivity>(clazz: Class<T>) :
+    BaseTest<T>(clazz) {
+    override val layoutId get() = R.id.viewPager
+
+    @Test
+    fun testKeepsState() {
+        // increase count of page 1 to 1
+        verifyPage(1)
+        increaseCount()
+        verifyCount(1)
+
+        // increase count of page 8 to 3
+        repeat(7) { swipeToNextPage() }
+        verifyPage(8)
+        repeat(3) { increaseCount() }
+        verifyCount(3)
+
+        // insert page at the beginning
+        choosePage(1)
+        insertPageBefore()
+        // check that we're now looking at the page before page 8
+        verifyPage(7)
+        verifyCount(0)
+        // swipe back to page 8
+        swipeToNextPage()
+        verifyPage(8)
+        verifyCount(3)
+
+        // swipe back to page 1
+        repeat(7) { swipeToPreviousPage() }
+        verifyPage(1)
+        verifyCount(1)
+
+        // check the newly inserted page
+        swipeToPreviousPage()
+        verifyPage(10)
+        verifyCount(0)
+    }
+
+    private fun increaseCount() {
+        onView(withId(R.id.buttonCountIncrease)).perform(click())
+    }
+
+    private fun verifyCount(count: Int) {
+        onView(withId(R.id.textViewCount)).check(matches(withText("$count")))
+    }
+
+    private fun verifyPage(page: Int) {
+        verifyCurrentPage(hasDescendant(allOf(
+            withId(R.id.textViewItemText),
+            withText("item#$page")
+        )))
+    }
+
+    private fun choosePage(page: Int) {
+        onView(withId(R.id.itemSpinner)).perform(click())
+        onData(equalTo("item#$page")).perform(click())
+    }
+
+    private fun insertPageBefore() {
+        onView(withId(R.id.buttonAddBefore)).perform(click())
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionFragmentTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionFragmentTest.kt
new file mode 100644
index 0000000..a78db21
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionFragmentTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class MutableCollectionFragmentTest :
+    MutableCollectionBaseTest<MutableCollectionFragmentActivity>(
+        MutableCollectionFragmentActivity::class.java
+    )
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionViewTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionViewTest.kt
new file mode 100644
index 0000000..4bc13e1
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/MutableCollectionViewTest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class MutableCollectionViewTest :
+    MutableCollectionBaseTest<MutableCollectionViewActivity>(
+        MutableCollectionViewActivity::class.java
+    )
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/TabLayoutTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/TabLayoutTest.kt
new file mode 100644
index 0000000..b02ea70
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/TabLayoutTest.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import androidx.test.espresso.Espresso.onIdle
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.action.ViewActions.scrollTo
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isSelected
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import com.example.androidx.viewpager2.test.onTab
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+class TabLayoutTest : BaseTest<CardViewTabLayoutActivity>(CardViewTabLayoutActivity::class.java) {
+    private val nineOfHeartsTab = "9 ♥"
+    private val tenOfHeartsTab = "10 ♥"
+    private val nineOfHeartsPage = "9\n♥"
+    private val tenOfHeartsPage = "10\n♥"
+
+    override val layoutId get() = R.id.view_pager
+
+    @Test
+    fun testTabLayoutIntegration() {
+        // test if ViewPager2 follows TabLayout when clicking a tab
+        selectTab(tenOfHeartsTab)
+        verifySelectedTab(tenOfHeartsTab)
+        verifyCurrentPage(tenOfHeartsPage)
+
+        // test if TabLayout follows ViewPager2 when swiping to a page
+        swipeToPreviousPage()
+        verifySelectedTab(nineOfHeartsTab)
+        verifyCurrentPage(nineOfHeartsPage)
+    }
+
+    private fun selectTab(text: String) {
+        onTab(text).perform(scrollTo(), click())
+        idleWatcher.waitForIdle()
+        onIdle()
+    }
+
+    private fun verifySelectedTab(text: String) {
+        onTab(text).check(matches(isSelected()))
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerBaseTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerBaseTest.kt
new file mode 100644
index 0000000..2f4d61e
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerBaseTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import android.view.View
+import androidx.test.espresso.Espresso.onData
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.viewpager2.widget.ViewPager2
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL
+import androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL
+import com.example.androidx.viewpager2.test.onCurrentPage
+import com.example.androidx.viewpager2.test.withRotation
+import com.example.androidx.viewpager2.test.withScale
+import com.example.androidx.viewpager2.test.withTranslation
+import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.CoreMatchers.equalTo
+import org.hamcrest.Matcher
+import org.junit.Before
+import org.junit.Test
+import org.junit.runners.Parameterized
+
+abstract class ViewPagerBaseTest<T : BaseCardActivity>(
+    clazz: Class<T>,
+    private val config: TestConfig
+) : BaseTest<T>(clazz) {
+    data class TestConfig(
+        @ViewPager2.Orientation val orientation: Int,
+        val animateRotate: Boolean,
+        val animateTranslate: Boolean,
+        val animateScale: Boolean
+    )
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0}")
+        fun spec(): List<TestConfig> {
+            return listOf(ORIENTATION_HORIZONTAL, ORIENTATION_VERTICAL).flatMap { orientation ->
+                listOf(false, true).flatMap { rotate ->
+                    listOf(false, true).flatMap { translate ->
+                        listOf(false, true).map { scale ->
+                            TestConfig(orientation, rotate, translate, scale)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    override val layoutId get() = R.id.view_pager
+
+    private val twoOfSpades = "2\n♣"
+    private val threeOfSpades = "3\n♣"
+
+    @Before
+    override fun setUp() {
+        super.setUp()
+        selectOrientation()
+        if (config.animateRotate) check(R.id.rotate_checkbox)
+        if (config.animateTranslate) check(R.id.translate_checkbox)
+        if (config.animateScale) check(R.id.scale_checkbox)
+    }
+
+    @Test
+    fun testSwipe() {
+        // Swipe to page 2
+        swipeToNextPage { verifyAnimation() }
+        verifyCurrentPage(threeOfSpades)
+
+        // Swipe back to page 1
+        swipeToPreviousPage { verifyAnimation() }
+        verifyCurrentPage(twoOfSpades)
+    }
+
+    private fun selectOrientation() {
+        onView(withId(R.id.orientation_spinner)).perform(click())
+        onData(equalTo(
+            when (config.orientation) {
+                ORIENTATION_HORIZONTAL -> "horizontal"
+                ORIENTATION_VERTICAL -> "vertical"
+                else -> "unknown"
+            }
+        )).perform(click())
+    }
+
+    private fun check(id: Int) {
+        onView(allOf(withId(id), isNotChecked())).perform(click())
+    }
+
+    private fun verifyAnimation() {
+        val animationVerifiers = mutableListOf<Matcher<View>>()
+        if (config.animateRotate) animationVerifiers.add(withRotation())
+        if (config.animateTranslate) animationVerifiers.add(withTranslation())
+        if (config.animateScale) animationVerifiers.add(withScale())
+        onCurrentPage().check(matches(allOf(animationVerifiers)))
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerFragmentTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerFragmentTest.kt
new file mode 100644
index 0000000..f772201
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerFragmentTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import androidx.test.filters.LargeTest
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class ViewPagerFragmentTest(config: ViewPagerBaseTest.TestConfig) :
+    ViewPagerBaseTest<CardFragmentActivity>(CardFragmentActivity::class.java, config)
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerViewTest.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerViewTest.kt
new file mode 100644
index 0000000..a48ee69
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/ViewPagerViewTest.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2
+
+import androidx.test.filters.LargeTest
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+class ViewPagerViewTest(config: ViewPagerBaseTest.TestConfig) :
+    ViewPagerBaseTest<CardViewActivity>(CardViewActivity::class.java, config)
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewInteractions.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewInteractions.kt
new file mode 100644
index 0000000..52b23f2
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewInteractions.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2.test
+
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.ViewInteraction
+import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
+import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast
+import androidx.test.espresso.matcher.ViewMatchers.withChild
+import androidx.test.espresso.matcher.ViewMatchers.withParent
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.viewpager2.widget.ViewPager2
+import com.google.android.material.tabs.TabLayout
+import org.hamcrest.CoreMatchers.allOf
+
+fun onViewPager(): ViewInteraction {
+    return onView(isAssignableFrom(ViewPager2::class.java))
+}
+
+fun onCurrentPage(): ViewInteraction {
+    return onView(allOf(
+        withParent(withParent(isAssignableFrom(ViewPager2::class.java))),
+        isDisplayingAtLeast(50)
+    ))
+}
+
+fun onTab(withText: String): ViewInteraction {
+    return onView(
+        allOf(
+            isDescendantOfA(isAssignableFrom(TabLayout::class.java)),
+            withChild(withText(withText))
+        )
+    )
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewMatchers.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewMatchers.kt
new file mode 100644
index 0000000..5bb9c46
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewMatchers.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2.test
+
+import android.view.View
+import androidx.test.espresso.matcher.BoundedMatcher
+import org.hamcrest.CoreMatchers.not
+import org.hamcrest.Description
+import org.hamcrest.Matcher
+
+fun withRotation(): Matcher<View> {
+    return WithRotationMatcher(not(0f))
+}
+
+fun withTranslation(): Matcher<View> {
+    return WithTranslationMatcher(not(0f))
+}
+
+fun withScale(): Matcher<View> {
+    return WithScaleMatcher(not(0f))
+}
+
+private class WithRotationMatcher(private val rotation: Matcher<Float>) :
+    BoundedMatcher<View, View>(View::class.java) {
+    override fun describeTo(description: Description) {
+        description.appendText("with rotation: ")
+        rotation.describeTo(description)
+    }
+
+    override fun matchesSafely(item: View): Boolean {
+        return rotation.matches(item.rotation)
+    }
+}
+
+private class WithTranslationMatcher(private val translation: Matcher<Float>) :
+    BoundedMatcher<View, View>(View::class.java) {
+    override fun describeTo(description: Description) {
+        description.appendText("with translation: ")
+        translation.describeTo(description)
+    }
+
+    override fun matchesSafely(item: View): Boolean {
+        return translation.matches(item.translationX) ||
+                translation.matches(item.translationY) ||
+                translation.matches(item.translationZ)
+    }
+}
+
+private class WithScaleMatcher(private val scale: Matcher<Float>) :
+    BoundedMatcher<View, View>(View::class.java) {
+    override fun describeTo(description: Description) {
+        description.appendText("with scale: ")
+        scale.describeTo(description)
+    }
+
+    override fun matchesSafely(item: View): Boolean {
+        return scale.matches(item.scaleX) ||
+                scale.matches(item.scaleY)
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewPagerActions.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewPagerActions.kt
new file mode 100644
index 0000000..6ea1f00
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewPagerActions.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2.test
+
+import android.view.View
+import androidx.core.view.ViewCompat
+import androidx.test.espresso.UiController
+import androidx.test.espresso.ViewAction
+import androidx.test.espresso.action.ViewActions.swipeDown
+import androidx.test.espresso.action.ViewActions.swipeLeft
+import androidx.test.espresso.action.ViewActions.swipeRight
+import androidx.test.espresso.action.ViewActions.swipeUp
+import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
+import androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast
+import androidx.viewpager2.widget.ViewPager2
+import com.example.androidx.viewpager2.test.SwipeAction.Direction.BACKWARD
+import com.example.androidx.viewpager2.test.SwipeAction.Direction.FORWARD
+import org.hamcrest.CoreMatchers.allOf
+import org.hamcrest.Matcher
+
+fun swipeNext(): ViewAction {
+    return SwipeAction(FORWARD)
+}
+
+fun swipePrevious(): ViewAction {
+    return SwipeAction(BACKWARD)
+}
+
+private class SwipeAction(val direction: Direction) : ViewAction {
+    enum class Direction {
+        FORWARD,
+        BACKWARD
+    }
+
+    override fun getDescription(): String = "Swiping $direction"
+
+    override fun getConstraints(): Matcher<View> =
+        allOf(isAssignableFrom(ViewPager2::class.java), isDisplayingAtLeast(90))
+
+    override fun perform(uiController: UiController, view: View) {
+        val vp = view as ViewPager2
+        val isForward = direction == FORWARD
+        val swipeAction: ViewAction
+        if (vp.orientation == ViewPager2.ORIENTATION_HORIZONTAL) {
+            swipeAction = if (isForward == vp.isRtl()) swipeRight() else swipeLeft()
+        } else {
+            swipeAction = if (isForward) swipeUp() else swipeDown()
+        }
+        swipeAction.perform(uiController, view)
+    }
+
+    private fun ViewPager2.isRtl(): Boolean {
+        return ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
+    }
+}
diff --git a/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewPagerIdleWatcher.kt b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewPagerIdleWatcher.kt
new file mode 100644
index 0000000..f9d7165
--- /dev/null
+++ b/viewpager2/integration-tests/testapp/src/androidTest/java/com/example/androidx/viewpager2/test/ViewPagerIdleWatcher.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 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 com.example.androidx.viewpager2.test
+
+import androidx.test.espresso.IdlingRegistry
+import androidx.test.espresso.idling.CountingIdlingResource
+import androidx.viewpager2.widget.ViewPager2
+
+class ViewPagerIdleWatcher(private val counter: CountingIdlingResource) :
+    ViewPager2.OnPageChangeCallback() {
+    private var state = ViewPager2.SCROLL_STATE_IDLE
+    private var waitingForIdle = false
+    private val lock = Object()
+
+    override fun onPageScrollStateChanged(state: Int) {
+        synchronized(lock) {
+            this.state = state
+            if (waitingForIdle && state == ViewPager2.SCROLL_STATE_IDLE) {
+                counter.decrement()
+                waitingForIdle = false
+            }
+        }
+    }
+
+    fun waitForIdle() {
+        synchronized(lock) {
+            if (!waitingForIdle && state != ViewPager2.SCROLL_STATE_IDLE) {
+                waitingForIdle = true
+                counter.increment()
+            }
+        }
+    }
+
+    fun unregister() {
+        IdlingRegistry.getInstance().unregister(counter)
+    }
+
+    companion object {
+        fun registerViewPagerIdlingResource(viewPager: ViewPager2): ViewPagerIdleWatcher {
+            val counter = CountingIdlingResource("Idle when $this is not scrolling")
+            IdlingRegistry.getInstance().register(counter)
+            val idleWatcher = ViewPagerIdleWatcher(counter)
+            viewPager.registerOnPageChangeCallback(idleWatcher)
+            return idleWatcher
+        }
+    }
+}
\ No newline at end of file
diff --git a/samples/ViewPager2Demos/src/main/AndroidManifest.xml b/viewpager2/integration-tests/testapp/src/main/AndroidManifest.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/AndroidManifest.xml
rename to viewpager2/integration-tests/testapp/src/main/AndroidManifest.xml
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BaseCardActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/BaseCardActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BaseCardActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/BaseCardActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BrowseActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/BrowseActivity.kt
similarity index 96%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BrowseActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/BrowseActivity.kt
index 759db00..ca544d3 100644
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/BrowseActivity.kt
+++ b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/BrowseActivity.kt
@@ -57,7 +57,7 @@
             Intent(Intent.ACTION_VIEW).setClassName(this.packageName, activity)
 
     override fun onListItemClick(listView: ListView, view: View, position: Int, id: Long) {
-        val map = listView.getItemAtPosition(position) as Map<String, Any>
+        val map = listView.getItemAtPosition(position) as Map<*, *>
 
         val intent = Intent(map["intent"] as Intent)
         intent.addCategory(Intent.CATEGORY_SAMPLE_CODE)
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/CardFragmentActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardViewActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/CardViewActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardViewActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/CardViewActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardViewTabLayoutActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/CardViewTabLayoutActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/CardViewTabLayoutActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/CardViewTabLayoutActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/MutableCollectionBaseActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/MutableCollectionBaseActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/MutableCollectionBaseActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/MutableCollectionBaseActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/MutableCollectionFragmentActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/MutableCollectionFragmentActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/MutableCollectionFragmentActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/MutableCollectionFragmentActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/MutableCollectionViewActivity.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/MutableCollectionViewActivity.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/MutableCollectionViewActivity.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/MutableCollectionViewActivity.kt
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/TabLayoutMediator.java b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/TabLayoutMediator.java
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/TabLayoutMediator.java
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/TabLayoutMediator.java
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/Card.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/cards/Card.kt
similarity index 93%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/Card.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/cards/Card.kt
index 5458f7c..0499751 100644
--- a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/Card.kt
+++ b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/cards/Card.kt
@@ -38,7 +38,7 @@
     }
 
     companion object {
-        private val ARGS_BUNDLE = Card::class.java.name + ":Bundle"
+        internal val ARGS_BUNDLE = Card::class.java.name + ":Bundle"
 
         val SUITS = setOf("♣" /* clubs*/, "♦" /* diamonds*/, "♥" /* hearts*/, "♠" /*spades*/)
         val VALUES = setOf("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")
@@ -49,7 +49,7 @@
         /** Use in conjunction with [Card.toBundle]  */
         fun fromBundle(bundle: Bundle): Card {
             val spec = bundle.getStringArray(ARGS_BUNDLE)
-            return Card(spec[0], spec[1])
+            return Card(spec!![0], spec[1])
         }
     }
 }
diff --git a/samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/CardView.kt b/viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/cards/CardView.kt
similarity index 100%
rename from samples/ViewPager2Demos/src/main/java/com/example/androidx/viewpager2/cards/CardView.kt
rename to viewpager2/integration-tests/testapp/src/main/java/com/example/androidx/viewpager2/cards/CardView.kt
diff --git a/samples/ViewPager2Demos/src/main/res/drawable-hdpi/app_sample_code.png b/viewpager2/integration-tests/testapp/src/main/res/drawable-hdpi/app_sample_code.png
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/drawable-hdpi/app_sample_code.png
rename to viewpager2/integration-tests/testapp/src/main/res/drawable-hdpi/app_sample_code.png
Binary files differ
diff --git a/samples/ViewPager2Demos/src/main/res/drawable-mdpi/app_sample_code.png b/viewpager2/integration-tests/testapp/src/main/res/drawable-mdpi/app_sample_code.png
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/drawable-mdpi/app_sample_code.png
rename to viewpager2/integration-tests/testapp/src/main/res/drawable-mdpi/app_sample_code.png
Binary files differ
diff --git a/samples/ViewPager2Demos/src/main/res/drawable/border.xml b/viewpager2/integration-tests/testapp/src/main/res/drawable/border.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/drawable/border.xml
rename to viewpager2/integration-tests/testapp/src/main/res/drawable/border.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout-land/activity_no_tablayout.xml b/viewpager2/integration-tests/testapp/src/main/res/layout-land/activity_no_tablayout.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout-land/activity_no_tablayout.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout-land/activity_no_tablayout.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout-land/activity_tablayout.xml b/viewpager2/integration-tests/testapp/src/main/res/layout-land/activity_tablayout.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout-land/activity_tablayout.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout-land/activity_tablayout.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout-land/controls.xml b/viewpager2/integration-tests/testapp/src/main/res/layout-land/controls.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout-land/controls.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout-land/controls.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout/activity_mutable_collection.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/activity_mutable_collection.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout/activity_mutable_collection.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout/activity_mutable_collection.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout/activity_no_tablayout.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/activity_no_tablayout.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout/activity_no_tablayout.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout/activity_no_tablayout.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout/activity_tablayout.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/activity_tablayout.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout/activity_tablayout.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout/activity_tablayout.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout/controls.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/controls.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout/controls.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout/controls.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout/item_card_layout.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/item_card_layout.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout/item_card_layout.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout/item_card_layout.xml
diff --git a/samples/ViewPager2Demos/src/main/res/layout/item_mutable_collection.xml b/viewpager2/integration-tests/testapp/src/main/res/layout/item_mutable_collection.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/layout/item_mutable_collection.xml
rename to viewpager2/integration-tests/testapp/src/main/res/layout/item_mutable_collection.xml
diff --git a/samples/ViewPager2Demos/src/main/res/values-land/dimens.xml b/viewpager2/integration-tests/testapp/src/main/res/values-land/dimens.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/values-land/dimens.xml
rename to viewpager2/integration-tests/testapp/src/main/res/values-land/dimens.xml
diff --git a/samples/ViewPager2Demos/src/main/res/values/colors.xml b/viewpager2/integration-tests/testapp/src/main/res/values/colors.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/values/colors.xml
rename to viewpager2/integration-tests/testapp/src/main/res/values/colors.xml
diff --git a/samples/ViewPager2Demos/src/main/res/values/dimens.xml b/viewpager2/integration-tests/testapp/src/main/res/values/dimens.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/values/dimens.xml
rename to viewpager2/integration-tests/testapp/src/main/res/values/dimens.xml
diff --git a/samples/ViewPager2Demos/src/main/res/values/strings.xml b/viewpager2/integration-tests/testapp/src/main/res/values/strings.xml
similarity index 100%
rename from samples/ViewPager2Demos/src/main/res/values/strings.xml
rename to viewpager2/integration-tests/testapp/src/main/res/values/strings.xml
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
index 9544f1c..9b308b1 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/BaseTest.kt
@@ -48,6 +48,7 @@
 import androidx.viewpager2.widget.swipe.PageSwiper
 import androidx.viewpager2.widget.swipe.PageSwiperEspresso
 import androidx.viewpager2.widget.swipe.PageSwiperManual
+import androidx.viewpager2.widget.swipe.SelfChecking
 import androidx.viewpager2.widget.swipe.TestActivity
 import androidx.viewpager2.widget.swipe.ViewAdapter
 import org.hamcrest.CoreMatchers.equalTo
@@ -342,14 +343,8 @@
             matches(withText(value))
         )
 
-        // FIXME: too tight coupling
-        if (viewPager.adapter is FragmentAdapter) {
-            val adapter = viewPager.adapter as FragmentAdapter
-            assertThat(
-                "Number of fragment attaches minus fragment destroys must be " +
-                        "between 1 and 4 (inclusive)",
-                adapter.attachCount.get() - adapter.destroyCount.get(), isBetweenInIn(1, 4)
-            )
+        if (viewPager.adapter is SelfChecking) {
+            (viewPager.adapter as SelfChecking).selfCheck()
         }
     }
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt
index ca81829..aafcea4 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/FragmentAdapter.kt
@@ -24,6 +24,10 @@
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.FragmentManager
 import androidx.viewpager2.adapter.FragmentStateAdapter
+import org.hamcrest.Matchers.allOf
+import org.hamcrest.Matchers.greaterThanOrEqualTo
+import org.hamcrest.Matchers.lessThanOrEqualTo
+import org.junit.Assert.assertThat
 import java.util.concurrent.atomic.AtomicInteger
 
 private const val ARG_KEY = "key"
@@ -31,9 +35,9 @@
 class FragmentAdapter(
     fragmentManager: FragmentManager,
     private val items: List<String>
-) : FragmentStateAdapter(fragmentManager) {
-    val attachCount = AtomicInteger(0)
-    val destroyCount = AtomicInteger(0)
+) : FragmentStateAdapter(fragmentManager), SelfChecking {
+    private val attachCount = AtomicInteger(0)
+    private val destroyCount = AtomicInteger(0)
 
     override fun getItem(position: Int): Fragment = PageFragment().apply {
         arguments = Bundle(1).apply { putString(ARG_KEY, items[position]) }
@@ -48,6 +52,15 @@
     var itemIdToContains: (Long) -> Boolean = { itemId -> super.containsItem(itemId) }
     override fun getItemId(position: Int): Long = positionToItemId(position)
     override fun containsItem(itemId: Long): Boolean = itemIdToContains(itemId)
+
+    override fun selfCheck() =
+        /** Detects [Fragment] 'memory leaks'. Core premise of [FragmentStateAdapter] is to keep
+         * only a handful of [Fragment]s alive and handle the rest via state save/restore. */
+        assertThat(
+            "Number of alive fragments must be between 0 and 4",
+            attachCount.get() - destroyCount.get(),
+            allOf(greaterThanOrEqualTo(0), lessThanOrEqualTo(4))
+        )
 }
 
 class PageFragment : Fragment() {
@@ -55,10 +68,10 @@
     var onDestroyListener: () -> Unit = {}
 
     override fun onCreateView(
-        inflater: LayoutInflater,
+        layoutInflater: LayoutInflater,
         container: ViewGroup?,
         savedInstanceState: Bundle?
-    ): View = PageView.inflatePage(container!!)
+    ): View = PageView.inflatePage(layoutInflater, container)
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         setValue(when {
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
index aaea628..4913605 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
@@ -28,8 +28,8 @@
 private val PAGE_COLOR_ODD = Color.parseColor("#AAAAFF")
 
 object PageView {
-    fun inflatePage(parent: ViewGroup): View =
-            LayoutInflater.from(parent.context).inflate(R.layout.item_test_layout, parent, false)
+    fun inflatePage(layoutInflater: LayoutInflater, parent: ViewGroup?): View =
+        layoutInflater.inflate(R.layout.item_test_layout, parent, false)
 
     fun findPageInActivity(activity: Activity): View? = activity.findViewById(R.id.text_view)
 
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt
new file mode 100644
index 0000000..59b4da7
--- /dev/null
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/SelfChecking.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 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 androidx.viewpager2.widget.swipe
+
+/** Interface for test elements that have internal validation logic */
+interface SelfChecking {
+    fun selfCheck()
+}
\ No newline at end of file
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt
index 5aa525b..684cfdb 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/ViewAdapter.kt
@@ -16,13 +16,14 @@
 
 package androidx.viewpager2.widget.swipe
 
+import android.view.LayoutInflater
 import android.view.ViewGroup
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView.ViewHolder
 
 open class ViewAdapter(private val items: List<String>) : RecyclerView.Adapter<ViewHolder>() {
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
-            object : ViewHolder(PageView.inflatePage(parent)) {}
+        object : ViewHolder(PageView.inflatePage(LayoutInflater.from(parent.context), parent)) {}
 
     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
         PageView.setPageText(holder.itemView, items[position])
diff --git a/wear/res/values-bs/strings.xml b/wear/res/values-bs/strings.xml
index 82c990c..cf30702 100644
--- a/wear/res/values-bs/strings.xml
+++ b/wear/res/values-bs/strings.xml
@@ -16,6 +16,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="ws_navigation_drawer_content_description" msgid="7216697245762194759">"Panel za navigaciju"</string>
-    <string name="ws_action_drawer_content_description" msgid="1837365417701148489">"Panel za radnju"</string>
+    <string name="ws_navigation_drawer_content_description" msgid="7216697245762194759">"Ladica za navigaciju"</string>
+    <string name="ws_action_drawer_content_description" msgid="1837365417701148489">"Ladica za radnju"</string>
 </resources>
diff --git a/webkit/integration-tests/testapp/build.gradle b/webkit/integration-tests/testapp/build.gradle
index ecbe178..4e10fdf 100644
--- a/webkit/integration-tests/testapp/build.gradle
+++ b/webkit/integration-tests/testapp/build.gradle
@@ -28,6 +28,9 @@
     defaultConfig {
         minSdkVersion 19
     }
+    lintOptions {
+        disable 'UnusedResources'
+    }
     buildTypes {
         release {
             minifyEnabled true
diff --git a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
index 73754f5..a5cd5df 100644
--- a/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/webkit/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -43,5 +43,7 @@
           android:exported="true" />
         <activity android:name=".AssetLoaderSimpleActivity"
           android:exported="true" />
+        <activity android:name=".AssetLoaderAjaxActivity"
+          android:exported="true" />
     </application>
 </manifest>
diff --git a/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html b/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html
new file mode 100644
index 0000000..66d4564
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/assets/www/ajax_requests.html
@@ -0,0 +1,50 @@
+<html>
+<!-- Copyright (C) 2019 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.
+-->
+<head>
+    <title>Load html over XMLHttpRequest</title>
+
+    <!-- Load html file from android_assets -->
+    <script type="text/javascript">
+        fetch('https://example.com/androidx_webkit/example/assets/www/some_text.html')
+            .then(response => response.text())
+            .then(response => {
+                document.getElementById("assets_html").innerHTML = response;
+            })
+            .catch(err => {
+                document.getElementById("assets_html").innerHTML = "Loading file from assets failed";
+            })
+    </script>
+
+    <!-- Load html file from android_resources -->
+    <script type="text/javascript">
+        fetch('https://example.com/androidx_webkit/example/res/raw/some_text.html')
+            .then(response => response.text())
+            .then(response => {
+                document.getElementById("res_html").innerHTML = response;
+            })
+            .catch(err => {
+                document.getElementById("res_html").innerHTML = "Loading file from resources failed";
+            })
+    </script>
+</head>
+
+<body>
+    <h1>Loaded HTML should appear below on success</h1>
+    <hr>
+    <div id="assets_html"></div>
+    <div id="res_html"></div>
+</body>
+</html>
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
new file mode 100644
index 0000000..28588c1
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderAjaxActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 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 com.example.androidx.webkit;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.webkit.WebViewAssetLoader;
+
+/**
+ * An {@link Activity} to show a more useful usecase: performing ajax calls to load files from
+ * local app assets and resources in a safer way using WebViewAssetLoader.
+ */
+public class AssetLoaderAjaxActivity extends AppCompatActivity {
+
+    private class MyWebViewClient extends WebViewClient {
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, String url) {
+            return false;
+        }
+
+        @RequiresApi(21)
+        public WebResourceResponse shouldInterceptRequest(WebView view,
+                                            WebResourceRequest request) {
+            return mAssetLoader.shouldInterceptRequest(request);
+        }
+
+        @Override
+        public WebResourceResponse shouldInterceptRequest(WebView view, String request) {
+            return mAssetLoader.shouldInterceptRequest(request);
+        }
+    }
+
+    private WebViewAssetLoader mAssetLoader;
+    private WebView mWebView;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.activity_asset_loader);
+        setTitle(R.string.asset_loader_ajax_activity_title);
+        WebkitHelpers.appendWebViewVersionToTitle(this);
+
+        mAssetLoader = new WebViewAssetLoader(this);
+        mWebView = new WebView(this);
+        mWebView.setWebViewClient(new MyWebViewClient());
+        setContentView(mWebView);
+
+        WebSettings webViewSettings = mWebView.getSettings();
+        webViewSettings.setJavaScriptEnabled(true);
+        // Setting this off for security. Off by default for SDK versions >= 16.
+        webViewSettings.setAllowFileAccessFromFileURLs(false);
+        webViewSettings.setAllowUniversalAccessFromFileURLs(false);
+        // Keeping these off is less critical but still a good idea, especially
+        // if your app is not using file:// or content:// URLs.
+        webViewSettings.setAllowFileAccess(false);
+        webViewSettings.setAllowContentAccess(false);
+
+        // The "http://example.com" domain with the virtual path "/androidx_webkit/example/
+        // is used to host resources/assets is used for demonstration purpose only.
+        // The developer should ALWAYS use a domain which they are in control of or use
+        // the default androidplatform.net reserved by Google for this purpose.
+
+        // Host android resources ... under http(s)://example.com/androidx_webkit/example/res/...
+        mAssetLoader.hostResources("example.com", "/androidx_webkit/example/res/", true);
+        // Host the app assets under http(s)://example.com/androidx_webkit/example/assets/...
+        mAssetLoader.hostAssets("example.com", "/androidx_webkit/example/assets/", true);
+        Uri path = mAssetLoader.getAssetsHttpsPrefix().buildUpon()
+                                                .appendPath("www")
+                                                .appendPath("ajax_requests.html").build();
+
+        // Load the url http(s)://example.com/androidx_webkit/example/assets/www/ajax_requests.html
+        mWebView.loadUrl(path.toString());
+    }
+}
diff --git a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java
index 6d0c76f..24628b15 100644
--- a/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java
+++ b/webkit/integration-tests/testapp/src/main/java/com/example/androidx/webkit/AssetLoaderListActivity.java
@@ -44,9 +44,11 @@
         MenuListView listView = findViewById(R.id.asset_loader_list);
         MenuListView.MenuItem[] menuItems = new MenuListView.MenuItem[] {
                 new MenuListView.MenuItem(
-                        getResources()
-                                .getString(R.string.asset_loader_simple_activity_title),
+                        getResources().getString(R.string.asset_loader_simple_activity_title),
                         new Intent(activityContext, AssetLoaderSimpleActivity.class)),
+                new MenuListView.MenuItem(
+                    getResources().getString(R.string.asset_loader_ajax_activity_title),
+                    new Intent(activityContext, AssetLoaderAjaxActivity.class)),
         };
         listView.setItems(menuItems);
     }
diff --git a/webkit/integration-tests/testapp/src/main/res/raw/some_text.html b/webkit/integration-tests/testapp/src/main/res/raw/some_text.html
new file mode 100644
index 0000000..502e241
--- /dev/null
+++ b/webkit/integration-tests/testapp/src/main/res/raw/some_text.html
@@ -0,0 +1,15 @@
+<!-- Copyright (C) 2019 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.
+-->
+<h3>Successfully loaded html from resources!</h3>
diff --git a/webkit/integration-tests/testapp/src/main/res/values/strings.xml b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
index 5984d45..5a4fc1e 100644
--- a/webkit/integration-tests/testapp/src/main/res/values/strings.xml
+++ b/webkit/integration-tests/testapp/src/main/res/values/strings.xml
@@ -20,6 +20,7 @@
     <string name="proxy_override_activity_title">Proxy Override</string>
     <string name="not_updateable_webview">not updatable</string>
     <string name="proxy_override_requests_served">Requests served: %d</string>
+    <string name="asset_loader_ajax_activity_title">Asset Loader AJAX Demo</string>
     <string name="asset_loader_list_activity_title">WebView Asset Loader Demos</string>
     <string name="asset_loader_simple_activity_title">Simple Asset Loader</string>
 </resources>
diff --git a/work/workmanager-ktx/api/1.0.0-rc03.txt b/work/workmanager-ktx/api/1.0.0-rc03.txt
new file mode 100644
index 0000000..d76133a
--- /dev/null
+++ b/work/workmanager-ktx/api/1.0.0-rc03.txt
@@ -0,0 +1,42 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class CoroutineWorker extends androidx.work.ListenableWorker {
+    ctor public CoroutineWorker(android.content.Context appContext, androidx.work.WorkerParameters params);
+    method public abstract suspend Object? doWork(kotlin.coroutines.experimental.Continuation<? super androidx.work.ListenableWorker.Result> p);
+    method public kotlinx.coroutines.CoroutineDispatcher getCoroutineContext();
+    method public final void onStopped();
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+    property public kotlinx.coroutines.CoroutineDispatcher coroutineContext;
+  }
+
+  public final class DataKt {
+    ctor public DataKt();
+    method public static inline androidx.work.Data workDataOf(kotlin.Pair<java.lang.String,?>... pairs);
+  }
+
+  public final class ListenableFutureKt {
+    ctor public ListenableFutureKt();
+  }
+
+  public final class OneTimeWorkRequestKt {
+    ctor public OneTimeWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.OneTimeWorkRequest.Builder! OneTimeWorkRequestBuilder();
+    method public static inline androidx.work.OneTimeWorkRequest.Builder setInputMerger(androidx.work.OneTimeWorkRequest.Builder, kotlin.reflect.KClass<? extends androidx.work.InputMerger> inputMerger);
+  }
+
+  public final class OperationKt {
+    ctor public OperationKt();
+    method public static suspend inline Object! await(androidx.work.Operation, kotlin.coroutines.experimental.Continuation<? super androidx.work.Operation.State.SUCCESS>! p);
+  }
+
+  public final class PeriodicWorkRequestKt {
+    ctor public PeriodicWorkRequestKt();
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit! repeatIntervalTimeUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration! repeatInterval);
+    method public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(long repeatInterval, java.util.concurrent.TimeUnit! repeatIntervalTimeUnit, long flexTimeInterval, java.util.concurrent.TimeUnit! flexTimeIntervalUnit);
+    method @RequiresApi(26) public static inline <reified W extends androidx.work.ListenableWorker> androidx.work.PeriodicWorkRequest.Builder! PeriodicWorkRequestBuilder(java.time.Duration! repeatInterval, java.time.Duration! flexTimeInterval);
+  }
+
+}
+
diff --git a/work/workmanager-ktx/api/res-1.0.0-rc03.txt b/work/workmanager-ktx/api/res-1.0.0-rc03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-ktx/api/res-1.0.0-rc03.txt
diff --git a/work/workmanager-ktx/api/restricted_1.0.0-rc03.txt b/work/workmanager-ktx/api/restricted_1.0.0-rc03.txt
new file mode 100644
index 0000000..bdec7f9
--- /dev/null
+++ b/work/workmanager-ktx/api/restricted_1.0.0-rc03.txt
@@ -0,0 +1,14 @@
+// Signature format: 3.0
+package androidx.work {
+
+  @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) public enum DirectExecutor implements java.util.concurrent.Executor {
+    method public void execute(Runnable command);
+    enum_constant public static final androidx.work.DirectExecutor INSTANCE;
+  }
+
+  public final class ListenableFutureKt {
+    method @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP}) public static suspend inline <R> Object! await(com.google.common.util.concurrent.ListenableFuture<R>, kotlin.coroutines.experimental.Continuation<? super R>! p);
+  }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/1.0.0-rc03.txt b/work/workmanager-rxjava2/api/1.0.0-rc03.txt
new file mode 100644
index 0000000..fc1c405
--- /dev/null
+++ b/work/workmanager-rxjava2/api/1.0.0-rc03.txt
@@ -0,0 +1,12 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public abstract class RxWorker extends androidx.work.ListenableWorker {
+    ctor public RxWorker(android.content.Context, androidx.work.WorkerParameters);
+    method @MainThread public abstract io.reactivex.Single<androidx.work.ListenableWorker.Result>! createWork();
+    method protected io.reactivex.Scheduler! getBackgroundScheduler();
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+  }
+
+}
+
diff --git a/work/workmanager-rxjava2/api/res-1.0.0-rc03.txt b/work/workmanager-rxjava2/api/res-1.0.0-rc03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-rxjava2/api/res-1.0.0-rc03.txt
diff --git a/work/workmanager-rxjava2/api/restricted_1.0.0-rc03.txt b/work/workmanager-rxjava2/api/restricted_1.0.0-rc03.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/work/workmanager-rxjava2/api/restricted_1.0.0-rc03.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/work/workmanager-testing/api/1.0.0-rc03.txt b/work/workmanager-testing/api/1.0.0-rc03.txt
new file mode 100644
index 0000000..4754b0d
--- /dev/null
+++ b/work/workmanager-testing/api/1.0.0-rc03.txt
@@ -0,0 +1,22 @@
+// Signature format: 3.0
+package androidx.work.testing {
+
+  public class SynchronousExecutor implements java.util.concurrent.Executor {
+    ctor public SynchronousExecutor();
+    method public void execute(Runnable);
+  }
+
+  public interface TestDriver {
+    method public void setAllConstraintsMet(java.util.UUID);
+    method public void setInitialDelayMet(java.util.UUID);
+    method public void setPeriodDelayMet(java.util.UUID);
+  }
+
+  public final class WorkManagerTestInitHelper {
+    method public static androidx.work.testing.TestDriver! getTestDriver();
+    method public static void initializeTestWorkManager(android.content.Context);
+    method public static void initializeTestWorkManager(android.content.Context, androidx.work.Configuration);
+  }
+
+}
+
diff --git a/work/workmanager-testing/api/res-1.0.0-rc03.txt b/work/workmanager-testing/api/res-1.0.0-rc03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager-testing/api/res-1.0.0-rc03.txt
diff --git a/work/workmanager-testing/api/restricted_1.0.0-rc03.txt b/work/workmanager-testing/api/restricted_1.0.0-rc03.txt
new file mode 100644
index 0000000..da4f6cc
--- /dev/null
+++ b/work/workmanager-testing/api/restricted_1.0.0-rc03.txt
@@ -0,0 +1 @@
+// Signature format: 3.0
diff --git a/work/workmanager/api/1.0.0-rc03.txt b/work/workmanager/api/1.0.0-rc03.txt
new file mode 100644
index 0000000..bc7e240
--- /dev/null
+++ b/work/workmanager/api/1.0.0-rc03.txt
@@ -0,0 +1,284 @@
+// Signature format: 3.0
+package androidx.work {
+
+  public final class ArrayCreatingInputMerger extends androidx.work.InputMerger {
+    ctor public ArrayCreatingInputMerger();
+    method public androidx.work.Data merge(java.util.List<androidx.work.Data>);
+  }
+
+  public enum BackoffPolicy {
+    enum_constant public static final androidx.work.BackoffPolicy EXPONENTIAL;
+    enum_constant public static final androidx.work.BackoffPolicy LINEAR;
+  }
+
+  public final class Configuration {
+    method public java.util.concurrent.Executor getExecutor();
+    method public int getMaxJobSchedulerId();
+    method public int getMinJobSchedulerId();
+    method public androidx.work.WorkerFactory getWorkerFactory();
+    field public static final int MIN_SCHEDULER_LIMIT = 20; // 0x14
+  }
+
+  public static final class Configuration.Builder {
+    ctor public Configuration.Builder();
+    method public androidx.work.Configuration build();
+    method public androidx.work.Configuration.Builder setExecutor(java.util.concurrent.Executor);
+    method public androidx.work.Configuration.Builder setJobSchedulerJobIdRange(int, int);
+    method public androidx.work.Configuration.Builder setMaxSchedulerLimit(int);
+    method public androidx.work.Configuration.Builder setMinimumLoggingLevel(int);
+    method public androidx.work.Configuration.Builder setWorkerFactory(androidx.work.WorkerFactory);
+  }
+
+  public final class Constraints {
+    ctor public Constraints(androidx.work.Constraints);
+    method public androidx.work.NetworkType getRequiredNetworkType();
+    method public boolean requiresBatteryNotLow();
+    method public boolean requiresCharging();
+    method @RequiresApi(23) public boolean requiresDeviceIdle();
+    method public boolean requiresStorageNotLow();
+    field public static final androidx.work.Constraints! NONE;
+  }
+
+  public static final class Constraints.Builder {
+    ctor public Constraints.Builder();
+    method @RequiresApi(24) public androidx.work.Constraints.Builder addContentUriTrigger(android.net.Uri, boolean);
+    method public androidx.work.Constraints build();
+    method public androidx.work.Constraints.Builder setRequiredNetworkType(androidx.work.NetworkType);
+    method public androidx.work.Constraints.Builder setRequiresBatteryNotLow(boolean);
+    method public androidx.work.Constraints.Builder setRequiresCharging(boolean);
+    method @RequiresApi(23) public androidx.work.Constraints.Builder setRequiresDeviceIdle(boolean);
+    method public androidx.work.Constraints.Builder setRequiresStorageNotLow(boolean);
+    method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentMaxDelay(java.time.Duration!);
+    method @RequiresApi(24) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.Constraints.Builder setTriggerContentUpdateDelay(java.time.Duration!);
+  }
+
+  public final class Data {
+    ctor public Data(androidx.work.Data);
+    method public boolean getBoolean(String, boolean);
+    method public boolean[]? getBooleanArray(String);
+    method public double getDouble(String, double);
+    method public double[]? getDoubleArray(String);
+    method public float getFloat(String, float);
+    method public float[]? getFloatArray(String);
+    method public int getInt(String, int);
+    method public int[]? getIntArray(String);
+    method public java.util.Map<java.lang.String,java.lang.Object> getKeyValueMap();
+    method public long getLong(String, long);
+    method public long[]? getLongArray(String);
+    method public String? getString(String);
+    method public String[]? getStringArray(String);
+    field public static final androidx.work.Data! EMPTY;
+    field public static final int MAX_DATA_BYTES = 10240; // 0x2800
+  }
+
+  public static final class Data.Builder {
+    ctor public Data.Builder();
+    method public androidx.work.Data build();
+    method public androidx.work.Data.Builder putAll(androidx.work.Data);
+    method public androidx.work.Data.Builder putAll(java.util.Map<java.lang.String,java.lang.Object>);
+    method public androidx.work.Data.Builder putBoolean(String, boolean);
+    method public androidx.work.Data.Builder putBooleanArray(String, boolean[]);
+    method public androidx.work.Data.Builder putDouble(String, double);
+    method public androidx.work.Data.Builder putDoubleArray(String, double[]);
+    method public androidx.work.Data.Builder putFloat(String, float);
+    method public androidx.work.Data.Builder putFloatArray(String, float[]);
+    method public androidx.work.Data.Builder putInt(String, int);
+    method public androidx.work.Data.Builder putIntArray(String, int[]);
+    method public androidx.work.Data.Builder putLong(String, long);
+    method public androidx.work.Data.Builder putLongArray(String, long[]);
+    method public androidx.work.Data.Builder putString(String, String?);
+    method public androidx.work.Data.Builder putStringArray(String, String[]);
+  }
+
+  public enum ExistingPeriodicWorkPolicy {
+    enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy KEEP;
+    enum_constant public static final androidx.work.ExistingPeriodicWorkPolicy REPLACE;
+  }
+
+  public enum ExistingWorkPolicy {
+    enum_constant public static final androidx.work.ExistingWorkPolicy APPEND;
+    enum_constant public static final androidx.work.ExistingWorkPolicy KEEP;
+    enum_constant public static final androidx.work.ExistingWorkPolicy REPLACE;
+  }
+
+  public abstract class InputMerger {
+    ctor public InputMerger();
+    method public abstract androidx.work.Data merge(java.util.List<androidx.work.Data>);
+  }
+
+  public abstract class ListenableWorker {
+    ctor @Keep public ListenableWorker(android.content.Context, androidx.work.WorkerParameters);
+    method public final android.content.Context getApplicationContext();
+    method public final java.util.UUID getId();
+    method public final androidx.work.Data getInputData();
+    method @RequiresApi(28) public final android.net.Network? getNetwork();
+    method public final int getRunAttemptCount();
+    method public final java.util.Set<java.lang.String> getTags();
+    method @RequiresApi(24) public final java.util.List<java.lang.String> getTriggeredContentAuthorities();
+    method @RequiresApi(24) public final java.util.List<android.net.Uri> getTriggeredContentUris();
+    method public final boolean isStopped();
+    method public void onStopped();
+    method @MainThread public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+  }
+
+  public abstract static class ListenableWorker.Result {
+    method public static androidx.work.ListenableWorker.Result failure();
+    method public static androidx.work.ListenableWorker.Result failure(androidx.work.Data);
+    method public static androidx.work.ListenableWorker.Result retry();
+    method public static androidx.work.ListenableWorker.Result success();
+    method public static androidx.work.ListenableWorker.Result success(androidx.work.Data);
+  }
+
+  public enum NetworkType {
+    enum_constant public static final androidx.work.NetworkType CONNECTED;
+    enum_constant public static final androidx.work.NetworkType METERED;
+    enum_constant public static final androidx.work.NetworkType NOT_REQUIRED;
+    enum_constant public static final androidx.work.NetworkType NOT_ROAMING;
+    enum_constant public static final androidx.work.NetworkType UNMETERED;
+  }
+
+  public final class OneTimeWorkRequest extends androidx.work.WorkRequest {
+    method public static androidx.work.OneTimeWorkRequest from(Class<? extends androidx.work.ListenableWorker>);
+    method public static java.util.List<androidx.work.OneTimeWorkRequest> from(java.util.List<java.lang.Class<? extends androidx.work.ListenableWorker>>);
+  }
+
+  public static final class OneTimeWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.OneTimeWorkRequest.Builder,androidx.work.OneTimeWorkRequest> {
+    ctor public OneTimeWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>);
+    method public androidx.work.OneTimeWorkRequest.Builder setInitialDelay(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public androidx.work.OneTimeWorkRequest.Builder setInitialDelay(java.time.Duration);
+    method public androidx.work.OneTimeWorkRequest.Builder setInputMerger(Class<? extends androidx.work.InputMerger>);
+  }
+
+  public interface Operation {
+    method public com.google.common.util.concurrent.ListenableFuture<androidx.work.Operation.State.SUCCESS> getResult();
+    method public android.arch.lifecycle.LiveData<androidx.work.Operation.State> getState();
+  }
+
+  public abstract static class Operation.State {
+  }
+
+  public static final class Operation.State.FAILURE extends androidx.work.Operation.State {
+    ctor public Operation.State.FAILURE(Throwable);
+    method public Throwable getThrowable();
+  }
+
+  public static final class Operation.State.IN_PROGRESS extends androidx.work.Operation.State {
+  }
+
+  public static final class Operation.State.SUCCESS extends androidx.work.Operation.State {
+  }
+
+  public final class OverwritingInputMerger extends androidx.work.InputMerger {
+    ctor public OverwritingInputMerger();
+    method public androidx.work.Data merge(java.util.List<androidx.work.Data>);
+  }
+
+  public final class PeriodicWorkRequest extends androidx.work.WorkRequest {
+    field public static final long MIN_PERIODIC_FLEX_MILLIS = 300000L; // 0x493e0L
+    field public static final long MIN_PERIODIC_INTERVAL_MILLIS = 900000L; // 0xdbba0L
+  }
+
+  public static final class PeriodicWorkRequest.Builder extends androidx.work.WorkRequest.Builder<androidx.work.PeriodicWorkRequest.Builder,androidx.work.PeriodicWorkRequest> {
+    ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit);
+    ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration);
+    ctor public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit);
+    ctor @RequiresApi(26) public PeriodicWorkRequest.Builder(Class<? extends androidx.work.ListenableWorker>, java.time.Duration, java.time.Duration);
+  }
+
+  public abstract class WorkContinuation {
+    ctor public WorkContinuation();
+    method public static androidx.work.WorkContinuation combine(java.util.List<androidx.work.WorkContinuation>);
+    method public abstract androidx.work.Operation enqueue();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo>> getWorkInfos();
+    method public abstract android.arch.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo>> getWorkInfosLiveData();
+    method public final androidx.work.WorkContinuation then(androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation then(java.util.List<androidx.work.OneTimeWorkRequest>);
+  }
+
+  public final class WorkInfo {
+    method public java.util.UUID getId();
+    method public androidx.work.Data getOutputData();
+    method public androidx.work.WorkInfo.State getState();
+    method public java.util.Set<java.lang.String> getTags();
+  }
+
+  public enum WorkInfo.State {
+    method public boolean isFinished();
+    enum_constant public static final androidx.work.WorkInfo.State BLOCKED;
+    enum_constant public static final androidx.work.WorkInfo.State CANCELLED;
+    enum_constant public static final androidx.work.WorkInfo.State ENQUEUED;
+    enum_constant public static final androidx.work.WorkInfo.State FAILED;
+    enum_constant public static final androidx.work.WorkInfo.State RUNNING;
+    enum_constant public static final androidx.work.WorkInfo.State SUCCEEDED;
+  }
+
+  public abstract class WorkManager {
+    method public final androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation beginUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest>);
+    method public final androidx.work.WorkContinuation beginWith(androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.WorkContinuation beginWith(java.util.List<androidx.work.OneTimeWorkRequest>);
+    method public abstract androidx.work.Operation cancelAllWork();
+    method public abstract androidx.work.Operation cancelAllWorkByTag(String);
+    method public abstract androidx.work.Operation cancelUniqueWork(String);
+    method public abstract androidx.work.Operation cancelWorkById(java.util.UUID);
+    method public final androidx.work.Operation enqueue(androidx.work.WorkRequest);
+    method public abstract androidx.work.Operation enqueue(java.util.List<? extends androidx.work.WorkRequest>);
+    method public abstract androidx.work.Operation enqueueUniquePeriodicWork(String, androidx.work.ExistingPeriodicWorkPolicy, androidx.work.PeriodicWorkRequest);
+    method public androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, androidx.work.OneTimeWorkRequest);
+    method public abstract androidx.work.Operation enqueueUniqueWork(String, androidx.work.ExistingWorkPolicy, java.util.List<androidx.work.OneTimeWorkRequest>);
+    method public static androidx.work.WorkManager getInstance();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.lang.Long> getLastCancelAllTimeMillis();
+    method public abstract android.arch.lifecycle.LiveData<java.lang.Long> getLastCancelAllTimeMillisLiveData();
+    method public abstract com.google.common.util.concurrent.ListenableFuture<androidx.work.WorkInfo> getWorkInfoById(java.util.UUID);
+    method public abstract android.arch.lifecycle.LiveData<androidx.work.WorkInfo> getWorkInfoByIdLiveData(java.util.UUID);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo>> getWorkInfosByTag(String);
+    method public abstract android.arch.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo>> getWorkInfosByTagLiveData(String);
+    method public abstract com.google.common.util.concurrent.ListenableFuture<java.util.List<androidx.work.WorkInfo>> getWorkInfosForUniqueWork(String);
+    method public abstract android.arch.lifecycle.LiveData<java.util.List<androidx.work.WorkInfo>> getWorkInfosForUniqueWorkLiveData(String);
+    method public static void initialize(android.content.Context, androidx.work.Configuration);
+    method public abstract androidx.work.Operation pruneWork();
+  }
+
+  public abstract class WorkRequest {
+    method public java.util.UUID getId();
+    field public static final long DEFAULT_BACKOFF_DELAY_MILLIS = 30000L; // 0x7530L
+    field public static final long MAX_BACKOFF_MILLIS = 18000000L; // 0x112a880L
+    field public static final long MIN_BACKOFF_MILLIS = 10000L; // 0x2710L
+  }
+
+  public abstract static class WorkRequest.Builder<B extends androidx.work.WorkRequest.Builder, W extends androidx.work.WorkRequest> {
+    method public final B addTag(String);
+    method public final W build();
+    method public final B keepResultsForAtLeast(long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public final B keepResultsForAtLeast(java.time.Duration);
+    method public final B setBackoffCriteria(androidx.work.BackoffPolicy, long, java.util.concurrent.TimeUnit);
+    method @RequiresApi(26) public final B setBackoffCriteria(androidx.work.BackoffPolicy, java.time.Duration);
+    method public final B setConstraints(androidx.work.Constraints);
+    method public final B setInputData(androidx.work.Data);
+  }
+
+  public abstract class Worker extends androidx.work.ListenableWorker {
+    ctor @Keep public Worker(android.content.Context, androidx.work.WorkerParameters);
+    method @WorkerThread public abstract androidx.work.ListenableWorker.Result doWork();
+    method public final com.google.common.util.concurrent.ListenableFuture<androidx.work.ListenableWorker.Result> startWork();
+  }
+
+  public abstract class WorkerFactory {
+    ctor public WorkerFactory();
+    method public abstract androidx.work.ListenableWorker? createWorker(android.content.Context, String, androidx.work.WorkerParameters);
+  }
+
+  public final class WorkerParameters {
+    method public java.util.UUID getId();
+    method public androidx.work.Data getInputData();
+    method @RequiresApi(28) public android.net.Network? getNetwork();
+    method public int getRunAttemptCount();
+    method public java.util.Set<java.lang.String> getTags();
+    method @RequiresApi(24) public java.util.List<java.lang.String> getTriggeredContentAuthorities();
+    method @RequiresApi(24) public java.util.List<android.net.Uri> getTriggeredContentUris();
+  }
+
+}
+
diff --git a/work/workmanager/api/res-1.0.0-rc03.txt b/work/workmanager/api/res-1.0.0-rc03.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/work/workmanager/api/res-1.0.0-rc03.txt
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java
index 0b901d5..3a29aed 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/WorkerWrapperTest.java
@@ -1080,6 +1080,19 @@
         assertThat(mWorkSpecDao.getState(work.getStringId()), is(FAILED));
     }
 
+    @Test
+    @SmallTest
+    public void testWorkerThatThrowsAnException() {
+        OneTimeWorkRequest work =
+                new OneTimeWorkRequest.Builder(ExceptionWorker.class).build();
+        insertWork(work);
+        WorkerWrapper workerWrapper = createBuilder(work.getStringId()).build();
+        FutureListener listener = createAndAddFutureListener(workerWrapper);
+        workerWrapper.run();
+        assertThat(listener.mResult, is(false));
+        assertThat(mWorkSpecDao.getState(work.getStringId()), is(FAILED));
+    }
+
     private WorkerWrapper.Builder createBuilder(String workSpecId) {
         return new WorkerWrapper.Builder(
                 mContext,
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
index 2038d71..a35a26f 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
@@ -18,6 +18,9 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -31,20 +34,27 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
+import androidx.work.Configuration;
+import androidx.work.impl.Scheduler;
 import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
+import androidx.work.impl.model.WorkSpec;
 import androidx.work.impl.model.WorkSpecDao;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Collections;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class ForceStopRunnableTest {
 
     private Context mContext;
     private WorkManagerImpl mWorkManager;
+    private Scheduler mScheduler;
+    private Configuration mConfiguration;
     private WorkDatabase mWorkDatabase;
     private WorkSpecDao mWorkSpecDao;
     private Preferences mPreferences;
@@ -57,9 +67,14 @@
         mWorkDatabase = mock(WorkDatabase.class);
         mWorkSpecDao = mock(WorkSpecDao.class);
         mPreferences = mock(Preferences.class);
+        mScheduler = mock(Scheduler.class);
+        mConfiguration = new Configuration.Builder().build();
+
         when(mWorkManager.getWorkDatabase()).thenReturn(mWorkDatabase);
+        when(mWorkManager.getSchedulers()).thenReturn(Collections.singletonList(mScheduler));
         when(mWorkDatabase.workSpecDao()).thenReturn(mWorkSpecDao);
         when(mWorkManager.getPreferences()).thenReturn(mPreferences);
+        when(mWorkManager.getConfiguration()).thenReturn(mConfiguration);
         mRunnable = new ForceStopRunnable(mContext, mWorkManager);
     }
 
@@ -99,4 +114,24 @@
         runnable.run();
         verify(mPreferences, times(1)).setNeedsReschedule(false);
     }
+
+    @Test
+    public void test_UnfinishedWork_getsScheduled() {
+        ForceStopRunnable runnable = spy(mRunnable);
+        when(runnable.shouldRescheduleWorkers()).thenReturn(false);
+        when(runnable.isForceStopped()).thenReturn(false);
+        String id = "id";
+        String worker = "Worker";
+        WorkSpec workSpec = new WorkSpec(id, worker);
+
+        when(mWorkSpecDao.getEnqueuedWork()).thenReturn(Collections.singletonList(workSpec));
+        when(mWorkSpecDao.getEligibleWorkForScheduling(anyInt())).thenReturn(
+                Collections.singletonList(workSpec));
+
+        runnable.run();
+        verify(mWorkSpecDao, times(2))
+                .markWorkSpecScheduled(eq(id), anyLong());
+
+        verify(mScheduler, times(1)).schedule(eq(workSpec));
+    }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/Worker.java b/work/workmanager/src/main/java/androidx/work/Worker.java
index ac0ec0e..7bba996 100644
--- a/work/workmanager/src/main/java/androidx/work/Worker.java
+++ b/work/workmanager/src/main/java/androidx/work/Worker.java
@@ -81,8 +81,13 @@
         getBackgroundExecutor().execute(new Runnable() {
             @Override
             public void run() {
-                Result result = doWork();
-                mFuture.set(result);
+                try {
+                    Result result = doWork();
+                    mFuture.set(result);
+                } catch (Throwable throwable) {
+                    mFuture.setException(throwable);
+                }
+
             }
         });
         return mFuture;
diff --git a/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpecDao.java b/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpecDao.java
index 77b0233..9756b5e 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpecDao.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/model/WorkSpecDao.java
@@ -303,6 +303,15 @@
     List<WorkSpec> getScheduledWork();
 
     /**
+     * @return The List of {@link WorkSpec}s that are unfinished and scheduled.
+     */
+    @Query("SELECT * FROM workspec WHERE "
+            // Unfinished work
+            + "state=" + WorkTypeConverters.StateIds.ENQUEUED
+    )
+    List<WorkSpec> getEnqueuedWork();
+
+    /**
      * Immediately prunes eligible work from the database meeting the following criteria:
      * - Is finished (succeeded, failed, or cancelled)
      * - Has zero unfinished dependents
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
index e5506628..adc4d48 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
@@ -20,6 +20,8 @@
 import static android.app.PendingIntent.FLAG_NO_CREATE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 
+import static androidx.work.impl.model.WorkSpec.SCHEDULE_NOT_REQUESTED_YET;
+
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -31,8 +33,13 @@
 import android.support.annotation.VisibleForTesting;
 
 import androidx.work.Logger;
+import androidx.work.impl.Schedulers;
+import androidx.work.impl.WorkDatabase;
 import androidx.work.impl.WorkManagerImpl;
+import androidx.work.impl.model.WorkSpec;
+import androidx.work.impl.model.WorkSpecDao;
 
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -72,6 +79,35 @@
         } else if (isForceStopped()) {
             Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");
             mWorkManager.rescheduleEligibleWork();
+        } else {
+            WorkDatabase workDatabase = mWorkManager.getWorkDatabase();
+            WorkSpecDao workSpecDao = workDatabase.workSpecDao();
+            try {
+                workDatabase.beginTransaction();
+                List<WorkSpec> workSpecs = workSpecDao.getEnqueuedWork();
+                if (workSpecs != null && !workSpecs.isEmpty()) {
+                    Logger.get().debug(TAG, "Found unfinished work, scheduling it.");
+                    // Mark every instance of unfinished work with
+                    // SCHEDULE_NOT_REQUESTED_AT = -1 irrespective of its current state.
+                    // This is because the application might have crashed previously and we should
+                    // reschedule jobs that may have been running previously.
+                    // Also there is a chance that an application crash, happened during
+                    // onStartJob() and now no corresponding job now exists in JobScheduler.
+                    // To solve this, we simply force-reschedule all unfinished work.
+                    for (WorkSpec workSpec : workSpecs) {
+                        workSpecDao.markWorkSpecScheduled(workSpec.id, SCHEDULE_NOT_REQUESTED_YET);
+                    }
+                    Schedulers.schedule(
+                            mWorkManager.getConfiguration(),
+                            workDatabase,
+                            mWorkManager.getSchedulers());
+                }
+                workDatabase.setTransactionSuccessful();
+            } finally {
+                workDatabase.endTransaction();
+            }
+            Logger.get().debug(TAG, "Unfinished Workers exist, rescheduling.");
+
         }
         mWorkManager.onForceStopRunnableCompleted();
     }