Merge "Revert "a11y: disabling accessibility scrolling of the home screen"" into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index c24850d..fe8d65e 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -20,7 +20,6 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3">
-    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
 
     <!--
     The manifest defines the common entries that should be present in any derivative of Launcher3.
diff --git a/build.gradle b/build.gradle
index 0030b8b..4ae6600 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,14 +1,16 @@
 buildscript {
     repositories {
         mavenCentral()
-        jcenter()
+        google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.3'
-        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
+        classpath 'com.android.tools.build:gradle:3.2.0-alpha12'
+        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3'
     }
 }
 
+final String SUPPORT_LIBS_VERSION = '28.0.0-SNAPSHOT'
+
 apply plugin: 'com.android.application'
 apply plugin: 'com.google.protobuf'
 
@@ -23,6 +25,7 @@
         versionName "1.0"
 
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        vectorDrawables.useSupportLibrary = true
     }
     buildTypes {
         debug {
@@ -30,18 +33,28 @@
         }
     }
 
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    flavorDimensions "default"
+
     productFlavors {
         aosp {
+            dimension "default"
             applicationId 'com.android.launcher3'
             testApplicationId 'com.android.launcher3.tests'
         }
 
         l3go {
+            dimension "default"
             applicationId 'com.android.launcher3'
             testApplicationId 'com.android.launcher3.tests'
         }
 
         quickstep {
+            dimension "default"
             applicationId 'com.android.launcher3'
             testApplicationId 'com.android.launcher3.tests'
         }
@@ -98,27 +111,28 @@
 }
 
 repositories {
+    maven { url "../../../prebuilts/fullsdk-darwin/extras/android/m2repository" }
+    maven { url "../../../prebuilts/fullsdk-linux/extras/android/m2repository" }
     mavenCentral()
-    jcenter()
+    google()
 }
 
-final String SUPPORT_LIBS_VERSION = '28.0.0-SNAPSHOT'
 dependencies {
-    compile "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
-    compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
-    compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
-    compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
+    implementation "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
+    implementation "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
+    implementation "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
+    implementation 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7'
 
-    quickstepCompile fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
+    quickstepImplementation fileTree(dir: "quickstep/libs", include: 'sysui_shared.jar')
 
-    testCompile 'junit:junit:4.12'
-    androidTestCompile "org.mockito:mockito-core:1.9.5"
-    androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
-    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2'
-    androidTestCompile 'com.android.support.test:runner:1.0.0'
-    androidTestCompile 'com.android.support.test:rules:1.0.0'
-    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
-    androidTestCompile "com.android.support:support-annotations:${SUPPORT_LIBS_VERSION}"
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation "org.mockito:mockito-core:1.9.5"
+    androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
+    androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
+    androidTestImplementation 'com.android.support.test:runner:1.0.0'
+    androidTestImplementation 'com.android.support.test:rules:1.0.0'
+    androidTestImplementation 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
+    androidTestImplementation "com.android.support:support-annotations:${SUPPORT_LIBS_VERSION}"
 }
 
 protobuf {
diff --git a/go/res/values-bs/strings.xml b/go/res/values-bs/strings.xml
index 7042468..3141b9d 100644
--- a/go/res/values-bs/strings.xml
+++ b/go/res/values-bs/strings.xml
@@ -20,7 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i držite da uzmete prečicu."</string>
-    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
+    <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dodirnite dvaput i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
     <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
     <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 4013429..7e15434 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -106,6 +106,7 @@
   BACK_BUTTON = 11; // Deprecated, use enum Command instead
   // GO_TO_PLAYSTORE
   QUICK_SCRUB_BUTTON = 12;
+  CLEAR_ALL_BUTTON = 13;
 }
 
 // Used to define the action component of the LauncherEvent.
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index f62d1d6..bab2cd7 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -21,8 +21,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3" >
 
-    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
-
     <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index f5c6149..13f41ee 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index 22f8b55..7ecab32 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -20,13 +20,23 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <com.android.quickstep.fallback.FallbackRecentsView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/overview_panel"
+    <com.android.quickstep.views.RecentsViewContainer
+        android:id="@+id/overview_panel_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:theme="@style/HomeScreenElementTheme" />
+    >
+        <include layout="@layout/overview_clear_all_button"/>
 
-</com.android.quickstep.fallback.RecentsRootView>
\ No newline at end of file
+        <com.android.quickstep.fallback.FallbackRecentsView
+            android:id="@id/overview_panel"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:focusableInTouchMode="true"
+            android:theme="@style/HomeScreenElementTheme"
+        >
+
+        </com.android.quickstep.fallback.FallbackRecentsView>
+    </com.android.quickstep.views.RecentsViewContainer>
+</com.android.quickstep.fallback.RecentsRootView>
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
new file mode 100644
index 0000000..1ada914
--- /dev/null
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clear_all_button"
+    style="@android:style/Widget.DeviceDefault.Button.Borderless"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="start|top"
+    android:fontFamily="sans-serif-medium"
+    android:text="@string/recents_clear_all"
+    android:textColor="?attr/workspaceTextColor"
+    android:textSize="14sp"
+/>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 89e0571..6102c38 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -14,14 +14,23 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.views.LauncherRecentsView
+<com.android.quickstep.views.RecentsViewContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:theme="@style/HomeScreenElementTheme"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false"
     android:visibility="invisible"
-    android:focusableInTouchMode="true" >
+>
+    <include layout="@layout/overview_clear_all_button"/>
 
-</com.android.quickstep.views.LauncherRecentsView>
\ No newline at end of file
+    <com.android.quickstep.views.LauncherRecentsView
+        android:id="@id/overview_panel"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:focusableInTouchMode="true"
+        android:theme="@style/HomeScreenElementTheme"
+    >
+
+    </com.android.quickstep.views.LauncherRecentsView>
+</com.android.quickstep.views.RecentsViewContainer>
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index fa8f9dc..4e1d327 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Verdeelde skerm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Speld vas"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swiep van onder af op om programme te wissel"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oorsig"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Maak toe"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index d14e06c..8d44446 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"መተግበሪያዎችን ለመቀያየር ከግርጌ ወደ ላይ በጣት ጠረግ ያድርጉ"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ማጠቃለያ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"ዝጋ"</string>
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 8efffd2..ec5f855 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"تقسيم الشاشة"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"تثبيت"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"التمرير سريعًا لأعلى من أسفل للتبديل بين التطبيقات"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نظرة عامة"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"إغلاق"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index a832f9a..19d0f6b 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Tətbiqləri dəyişmək üçün aşağıdan yuxarı doğru sürüşdürün"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"İcmal"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Bağlayın"</string>
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index ba44830..9a5aed0 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Prevucite nagore da biste prešli na drugu aplikaciju"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
 </resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index df55803..fab5d03 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Для пераключэння праграм правядзіце па экране пальцам знізу ўверх"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Агляд"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыць"</string>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index c46245c..db4b96c 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Разделен екран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Фиксиране"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Прекарайте пръст нагоре от долната част, за да превключите между приложенията"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Общ преглед"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Няма скорошни елементи"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Затваряне"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 9080072..4856e90 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -22,10 +22,7 @@
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"স্ক্রিন স্প্লিট করুন"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"পিন করুন"</string>
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"অ্যাপগুলির মধ্যে সুইচ করতে উপর থেকে নিচের দিকে সোয়াইপ করুন"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
-    <skip />
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"এক নজরে"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"কোনো সাম্প্রতিক আইটেম নেই"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"বন্ধ করুন"</string>
 </resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 7e61277..95285d2 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Način rada podijeljenog ekrana"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Prevucite od dolje prema gore za promjenu aplikacije"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvaranje"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index ac77992..7c57b3c 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -21,11 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Divideix la pantalla"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixa"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Llisca cap amunt des de la part inferior per canviar d\'aplicació"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
-    <skip />
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicacions recents"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"No hi ha cap element recent"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Tanca"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index e8c0cb0..e8d661c 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Aplikace můžete přepínat přejetím zdola nahoru"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Přehled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žádné nedávné položky"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zavřít"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 6ddb31b..72db09d 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Stryg opad fra bunden for at skifte apps"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversigt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Luk"</string>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 01c785e..ddc83c6 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Zum Wechseln zwischen Apps vom unteren Bildschirmrand nach oben wischen"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Übersicht"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Schließen"</string>
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 6b2a25f..eeffe84 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Διαχωρισμός οθόνης"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Καρφίτσωμα"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Σύρετε από κάτω προς τα επάνω για εναλλαγή εφαρμογών"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Επισκόπηση"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Κλείσιμο"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index 402499e..a8cbc3f 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swipe up from the bottom to switch apps"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index 402499e..a8cbc3f 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swipe up from the bottom to switch apps"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index 402499e..a8cbc3f 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swipe up from the bottom to switch apps"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
 </resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 1b9f926..be3c74b 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Desliza el dedo hacia arriba para cambiar de app"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recientes"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Cerrar"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index c63f1d3..859cb2c 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Dividir pantalla"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Desliza el dedo hacia arriba desde la parte inferior para cambiar de aplicación"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicaciones recientes"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Cerrar"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 30199b9..b9cb587 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Jagatud ekraan"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Kinnita"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Rakenduste vahetamiseks pühkige alaosast üles"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ülevaade"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Sule"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index b6386cd..5c0dd8f 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Zatitu pantaila"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Ainguratu"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Aplikazioak aldatzeko, pasatu hatza pantailako behealdetik gora"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikuspegi orokorra"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Itxi"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 52beadd..b58cdb9 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"برای تغییر برنامه‌ها،‌ از پایین تند به بالا بکشید"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نمای کلی"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"بدون موارد اخیر"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"بستن"</string>
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index a27a9cb..9c7375d 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Vaihda sovellusta pyyhkäisemällä alareunasta ylös."</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Viimeisimmät"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ei viimeaikaisia kohteita"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Sulje"</string>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 8a603e9..cd73822 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Balayez l\'écran du bas vers le haut pour changer d\'application"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aperçu"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Fermer"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 9192287..ff216fa 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Écran partagé"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Balayer l\'écran de bas en haut pour changer d\'application"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aperçu"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Fermer"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 25d3796..e2883cb 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Pasa o dedo cara arriba desde a parte inferior para cambiar de aplicacións"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visión xeral"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Non hai elementos recentes"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Pecha a aplicación"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index f463e13..416b361 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -21,11 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"સ્ક્રીનને વિભાજિત કરો"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"પિન કરો"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ઍપને સ્વિચ કરવા માટે નીચેથી ઉપર સ્વાઇપ કરો"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
-    <skip />
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ઝલક"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"તાજેતરની કોઈ આઇટમ નથી"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"બંધ કરો"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index ee933d1..1c18700 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -21,11 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"स्क्रीन को दो हिस्सों में बाँटना (स्प्लिट स्क्रीन)"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करना"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ऐप्लिकेशन स्विच करने के लिए सबसे नीचे से ऊपर की ओर स्वाइप करें"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
-    <skip />
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"खास जानकारी"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"हाल ही में इस्तेमाल किया गया कोई ऐप्लिकेशन नहीं है"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"बंद करें"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index a0b734f..a1deed3 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Prijeđite prstom od dna prema gore da biste promijenili aplikaciju"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 8a465e2..23786f2 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Osztott képernyő"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Rögzítés"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Ha váltani szeretne az alkalmazások között, csúsztassa gyorsan az ujját a képernyő aljától felfelé"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Áttekintés"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nincsenek mostanában használt elemek"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Bezárás"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index fdfe818..94178da 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Սահեցրեք ներքևից վերև՝ մյուս հավելվածին անցնելու համար"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ընդհանուր տեղեկություններ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Վերջին տարրեր չկան"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Փակել"</string>
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 786a10c..a1b86ec 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -23,7 +23,6 @@
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pasang pin"</string>
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Geser dari bawah ke atas untuk beralih aplikasi"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ringkasan"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item baru-baru ini"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item yang baru dibuka"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
 </resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index b01a749..3f89411 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -22,8 +22,7 @@
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skipta skjá"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Festa"</string>
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Strjúktu upp til að skipta um forrit"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
-    <skip />
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Yfirlit"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"Engin nýleg atriði"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Loka"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 0da2251..8af4ea8 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Scorri verso l\'alto dalla parte inferiore per cambiare app"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Panoramica"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Chiudi"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index f7e8338..662c467 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"יש להחליק כלפי מעלה מהחלק התחתון כדי לעבור בין אפליקציות"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"מסכים אחרונים"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"סגירה"</string>
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 7e14d2c..0dabd3f 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"アプリを切り替えるには、下から上にスワイプします"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概要"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"閉じる"</string>
 </resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index cf4c661..729175a 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"აპების გადასართავად გადაფურცლეთ ქვედა კიდედან ზემოთ"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"მიმოხილვა"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"დახურვა"</string>
 </resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index f865a04..9894ed3 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Қолданбалар арасында ауысу үшін төменнен жоғары қарай саусақпен сырғытыңыз"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Шолу"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Жабу"</string>
 </resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index a35ab26..71cdbe0 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"អូស​ពី​ក្រោម​ឡើង​លើ ដើម្បី​ប្ដូរ​កម្មវិធី"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ទិដ្ឋភាពរួម"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"បិទ"</string>
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index dc57df1..ab75069 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಿಸಲು ಕೆಳಗಿನಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ಅವಲೋಕನ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"ಮುಚ್ಚಿ"</string>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 36fd122..181314e 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"아래에서 위로 스와이프하여 앱을 전환합니다."</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"최근 사용"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"최근 항목이 없습니다."</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"닫기"</string>
 </resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 060a8dd..7924b11 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Экранды бөлүү"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Кадап коюу"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Колдонмолорду которуштуруу үчүн экранды төмөндөн жогору карай сүрүңүз"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Сереп салуу"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Жабуу"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 1abb856..847dcea 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ປັດຂຶ້ນຈາກລຸ່ມສຸດເພື່ອສະຫຼັບແອັບ"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ພາບຮວມ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"ປິດ"</string>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 0c09a94..c0eeb6f 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Perbraukite aukštyn iš apačios, kad perjungtumėte programas"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Apžvalga"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nėra jokių naujausių elementų"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Uždaryti"</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 72d1cb5..d053798 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Lai pārslēgtu lietotnes, velciet augšup no apakšdaļas."</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pārskats"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nav nesenu vienumu."</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Aizvērt"</string>
 </resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 06bf5d0..74dc5aa 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Повлечете нагоре од дното за да ги смените апликациите"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
 </resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index b936906..b8a4d6e 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ആപ്പുകൾ മാറാൻ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"അവലോകനം"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"അവസാനിപ്പിക്കുക"</string>
 </resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 8b92214..114c050 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Аппыг сэлгэхийн тулд доороос дээш шударна уу"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Тойм"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Хаах"</string>
 </resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 596792d..4d94137 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"विभाजित स्क्रीन"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करा"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"अ‍ॅप्स स्विच करण्यासाठी तळापासून वर स्वाइप करा"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"अवलोकन"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"बंद"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 336aaf6..0e4fa3f 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Leret ke atas dari bawah untuk menukar apl"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikhtisar"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index d71e5fc..e10cd4f 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"အက်ပ်များပြောင်းရန် အောက်ခြေမှ အပေါ်သို့ပွတ်ဆွဲပါ"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"အနှစ်ချုပ်"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"ပိတ်ရန်"</string>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 504f43a..64c47c4 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Sveip opp fra bunnen for å bytte app"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversikt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nylige elementer"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Lukk"</string>
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 7500213..99f90bb 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"अनुप्रयोगहरू बदल्न तलबाट माथितिर स्वाइप गर्नुहोस्"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"परिदृश्य"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"बन्द गर्नुहोस्"</string>
 </resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 2ba24a6..67932b2 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Gesplitst scherm"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Vastzetten"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Veeg omhoog vanaf de onderkant om tussen apps te wisselen"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overzicht"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Geen recente items"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Sluiten"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index fbcb60c..6e0c5a2 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ਐਪਾਂ ਵਿੱਚ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ਰੂਪ-ਰੇਖਾ"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"ਬੰਦ ਕਰੋ"</string>
 </resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 1ad7070..5cfdc68 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Przesuń palcem z dołu ekranu, by przełączać aplikacje"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Przegląd"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Brak ostatnich elementów"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zamknij"</string>
 </resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index a63d329..e86c147 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ecrã dividido"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Deslize rapidamente para cima a partir da parte inferior para alternar entre aplicações."</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Vista geral"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Fechar"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 05d20e0..6a5bea9 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Deslize de baixo para cima para alternar entre apps"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visão geral"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Fechar"</string>
 </resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 4264370..2c05f90 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Glisați de jos în sus pentru a schimba aplicațiile"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recente"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Închideți"</string>
 </resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 47ddff5..f8a38ed 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Разделить экран"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Блокировать"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Чтобы переключить приложение, проведите по экрану снизу вверх"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Обзор"</string>
-    <string name="recents_empty_message" msgid="7040467240571714191">"Недавних приложений нет."</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="recents_empty_message" msgid="7040467240571714191">"Здесь пока ничего нет."</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыть"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index a9b1493..d81344f 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"යෙදුම් මාරු කිරීම සඳහා පහළ සිට ස්වයිප් කරන්න"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"දළ විශ්ලේෂණය"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"වසන්න"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index fe02855..127d73a 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Rozdeliť obrazovku"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Pripnúť"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Aplikácie môžete prepínať potiahnutím prstom zdola nahor"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Prehľad"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Žiadne nedávne položky"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zavrieť"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 72d52a5..9e4b970 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Če želite preklopiti med aplikacijami, z dna zaslona s prstom povlecite navzgor"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Ni nedavnih elementov"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Zapri"</string>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index 954342c..62dbf01 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Rrëshqit larg nga poshtë për të ndryshuar aplikacionet"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Përmbledhja"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Mbyll"</string>
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 51a9586..ae22cfc 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Превуците нагоре да бисте прешли на другу апликацију"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Нема недавних ставки"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 266cf60..1434f27 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Växla mellan appar genom att svepa uppåt från nederkanten"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Översikt"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Listan med de senaste åtgärderna är tom"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Stäng"</string>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index e85fa45..7e311b9 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Telezesha kidole juu kuanzia chini ili ubadilishe programu"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Muhtasari"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Hakuna vipengee vya hivi karibuni"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Funga"</string>
 </resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index de03ae6..e372eea 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"திரைப் பிரிப்பு"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"பின் செய்தல்"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ஆப்ஸிற்கு இடையே மாற்றுவதற்கு, கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்க"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"மேலோட்டப் பார்வை"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"சமீபத்தியவை எதுவுமில்லை"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"மூடும்"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index 108f350..7cd88c8 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"యాప్‌లను మార్చడానికి దిగువ నుండి పైకి స్వైప్ చేయండి"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"అవలోకనం"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"మూసివేయండి"</string>
 </resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 80f91b0..03f77e2 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"เลื่อนขึ้นจากด้านล่างเพื่อสลับแอป"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ภาพรวม"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"ปิด"</string>
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index b28e04e..368a1d1 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Mag-swipe pataas mula sa ibaba para lumipat ng app"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Walang kamakailang item"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Isara"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 1399353..a51acc2 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Uygulamaları değiştirmek için alttan yukarı kaydırın"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Genel bakış"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yeni öğe yok"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Kapat"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 929bbe7..0b0ad16 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Щоб переходити між додатками, проводьте пальцем знизу вгору"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Огляд"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Немає нещодавніх додатків"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Закрити"</string>
 </resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 0271fe4..7285b26 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -22,10 +22,7 @@
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"اسپلٹ اسکرین وضع"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"پن کریں"</string>
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ایپس کو سوئچ کرنے کیلئے نیچے سے اوپر سوائپ کریں"</string>
-    <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (7040467240571714191) -->
-    <skip />
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"مجموعی جائزہ"</string>
+    <string name="recents_empty_message" msgid="7040467240571714191">"کوئی حالیہ آئٹم نہیں"</string>
+    <string name="accessibility_close_task" msgid="5354563209433803643">"بند کریں"</string>
 </resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 91e11d3..fe68d74 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ekranni ikkiga ajratish"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Mahkamlash"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Ilovalarni almashtirish uchun pastdan yuqoriga suring"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Nazar"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Yopish"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 809517a..6db731c 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Chia đôi màn hình"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"Ghim"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Vuốt từ dưới lên để chuyển đổi ứng dụng"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Tổng quan"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Không có mục gần đây nào"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Đóng"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index a44dd2d..08fdf4e 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"分屏"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"从屏幕底部向上滑动即可切换应用"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概览"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"近期没有任何内容"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"关闭"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 3879bc5..d3ac3a2 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -21,9 +21,9 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割畫面"</string>
     <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
-    <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"從螢幕底部向上快速滑動,即可切換應用程式"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概覽"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+    <string name="accessibility_close_task" msgid="5354563209433803643">"關閉"</string>
+    <!-- no translation found for recents_clear_all (5328176793634888831) -->
     <skip />
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index f275168..53efe9a 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"從畫面底部向上滑動以切換應用程式"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"總覽"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"關閉"</string>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 206718e..6e48dc0 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -24,6 +24,5 @@
     <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swayiphela phezulu kusukela phansi ukuze ushintshe izinhlelo zokusebenza"</string>
     <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Buka konke"</string>
     <string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
-    <!-- no translation found for accessibility_close_task (5354563209433803643) -->
-    <skip />
+    <string name="accessibility_close_task" msgid="5354563209433803643">"Vala"</string>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c741913..0199cd9 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -45,4 +45,7 @@
     <!-- Copied from framework resource:
        docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
     <dimen name="multi_window_task_divider_size">10dp</dimen>
+
+    <!-- Width of the space behind the last task in Overview. In the center of it, there is "Clear all" button. -->
+    <dimen name="clear_all_container_width">168dp</dimen>
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 7ba91b3..34cc0b7 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -27,9 +27,6 @@
     <!-- Title for an option to keep an app pinned to the screen until it is unpinned -->
     <string name="recent_task_option_pin">Pin</string>
 
-    <!-- Text that shows above the navigation bar after launching a few apps -->
-    <string name="recents_swipe_up_onboarding">Swipe up from the bottom to switch apps</string>
-
     <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_recent_apps">Overview</string>
 
@@ -38,4 +35,7 @@
 
     <!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
     <string name="accessibility_close_task">Close</string>
+
+    <!-- Recents: Title of a button that clears the task list, i.e. closes all tasks. [CHAR LIMIT=30] -->
+    <string name="recents_clear_all">Clear all</string>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml
index 2655402..30f3100 100644
--- a/quickstep/res/xml/indexable_launcher_prefs.xml
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -20,8 +20,14 @@
         android:key="pref_add_icon_to_home"
         android:title="@string/auto_add_shortcuts_label"
         android:summary="@string/auto_add_shortcuts_description"
-        android:defaultValue="true"
-        />
+        android:defaultValue="true"  />
+
+    <SwitchPreference
+        android:key="pref_allowRotation"
+        android:title="@string/allow_rotation_title"
+        android:summary="@string/allow_rotation_desc"
+        android:defaultValue="@bool/allow_rotation"
+        android:persistent="true" />
 
     <ListPreference
         android:key="pref_override_icon_shape"
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index f919339..29399142 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -15,8 +15,9 @@
  */
 package com.android.launcher3;
 
-import static com.android.systemui.shared.recents.utilities.Utilities
-        .postAtFrontOfQueueAsynchronously;
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -31,53 +32,49 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 @TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner extends AnimatorListenerAdapter
-        implements RemoteAnimationRunnerCompat {
-
-    private static final int REFRESH_RATE_MS = 16;
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
 
     private final Handler mHandler;
+    private final boolean mStartAtFrontOfQueue;
+    private AnimationResult mAnimationResult;
 
-    private Runnable mSysFinishRunnable;
-
-    private AnimatorSet mAnimator;
-
-    public LauncherAnimationRunner(Handler handler) {
+    /**
+     * @param startAtFrontOfQueue If true, the animation start will be posted at the front of the
+     *                            queue to minimize latency.
+     */
+    public LauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue) {
         mHandler = handler;
+        mStartAtFrontOfQueue = startAtFrontOfQueue;
     }
 
     @BinderThread
     @Override
     public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
-        postAtFrontOfQueueAsynchronously(mHandler, () -> {
-            // Finish any previous animation
-            finishSystemAnimation();
-
-            mSysFinishRunnable = runnable;
-            mAnimator = getAnimator(targetCompats);
-            if (mAnimator == null) {
-                finishSystemAnimation();
-                return;
-            }
-            mAnimator.addListener(this);
-            mAnimator.start();
-            // Because t=0 has the app icon in its original spot, we can skip the
-            // first frame and have the same movement one frame earlier.
-            mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
-
-        });
+        Runnable r = () -> {
+            finishExistingAnimation();
+            mAnimationResult = new AnimationResult(runnable);
+            onCreateAnimation(targetCompats, mAnimationResult);
+        };
+        if (mStartAtFrontOfQueue) {
+            postAtFrontOfQueueAsynchronously(mHandler, r);
+        } else {
+            postAsyncCallback(mHandler, r);
+        }
     }
 
+    /**
+     * Called on the UI thread when the animation targets are received. The implementation must
+     * call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run.
+     */
+    @UiThread
+    public abstract void onCreateAnimation(
+            RemoteAnimationTargetCompat[] targetCompats, AnimationResult result);
 
     @UiThread
-    public abstract AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats);
-
-    @UiThread
-    @Override
-    public void onAnimationEnd(Animator animation) {
-        if (animation == mAnimator) {
-            mAnimator = null;
-            finishSystemAnimation();
+    private void finishExistingAnimation() {
+        if (mAnimationResult != null) {
+            mAnimationResult.finish();
+            mAnimationResult = null;
         }
     }
 
@@ -87,20 +84,55 @@
     @BinderThread
     @Override
     public void onAnimationCancelled() {
-        postAtFrontOfQueueAsynchronously(mHandler, () -> {
-            if (mAnimator != null) {
-                mAnimator.removeListener(this);
-                mAnimator.end();
-                mAnimator = null;
-            }
-        });
+        postAsyncCallback(mHandler, this::finishExistingAnimation);
     }
 
-    @UiThread
-    private void finishSystemAnimation() {
-        if (mSysFinishRunnable != null) {
-            mSysFinishRunnable.run();
-            mSysFinishRunnable = null;
+    public static final class AnimationResult {
+
+        private final Runnable mFinishRunnable;
+
+        private AnimatorSet mAnimator;
+        private boolean mFinished = false;
+        private boolean mInitialized = false;
+
+        private AnimationResult(Runnable finishRunnable) {
+            mFinishRunnable = finishRunnable;
+        }
+
+        @UiThread
+        private void finish() {
+            if (!mFinished) {
+                mFinishRunnable.run();
+                mFinished = true;
+            }
+        }
+
+        @UiThread
+        public void setAnimation(AnimatorSet animation) {
+            if (mInitialized) {
+                throw new IllegalStateException("Animation already initialized");
+            }
+            mInitialized = true;
+            mAnimator = animation;
+            if (mAnimator == null) {
+                finish();
+            } else if (mFinished) {
+                // Animation callback was already finished, skip the animation.
+                mAnimator.end();
+            } else {
+                // Start the animation
+                mAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        finish();
+                    }
+                });
+                mAnimator.start();
+
+                // Because t=0 has the app icon in its original spot, we can skip the
+                // first frame and have the same movement one frame earlier.
+                mAnimator.setCurrentPlayTime(SINGLE_FRAME_MS);
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 1620352..bd1cdc6 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -16,8 +16,11 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
@@ -48,6 +51,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
+import android.util.Pair;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
@@ -119,6 +123,18 @@
         }
     };
 
+    private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mLauncher.addForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mLauncher.clearForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
+        }
+    };
+
     public LauncherAppTransitionManagerImpl(Context context) {
         mLauncher = Launcher.getLauncher(context);
         mDragLayer = mLauncher.getDragLayer();
@@ -126,7 +142,6 @@
         mIsRtl = Utilities.isRtl(mLauncher.getResources());
         mDeviceProfile = mLauncher.getDeviceProfile();
 
-
         Resources res = mLauncher.getResources();
         mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
         mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
@@ -147,38 +162,50 @@
     @Override
     public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
         if (hasControlRemoteAppTransitionPermission()) {
-            try {
-                RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler) {
+            RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
+                    true /* startAtFrontOfQueue */) {
 
-                    @Override
-                    public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
-                        AnimatorSet anim = new AnimatorSet();
+                @Override
+                public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                        AnimationResult result) {
+                    AnimatorSet anim = new AnimatorSet();
 
+                    boolean launcherClosing =
+                            launcherIsATargetWithMode(targetCompats, MODE_CLOSING);
 
-                        if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
-                            // Set the state animation first so that any state listeners are called
-                            // before our internal listeners.
-                            mLauncher.getStateManager().setCurrentAnimation(anim);
+                    if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
+                        // Set the state animation first so that any state listeners are called
+                        // before our internal listeners.
+                        mLauncher.getStateManager().setCurrentAnimation(anim);
 
-                            anim.play(getIconAnimator(v));
-                            if (launcherIsATargetWithMode(targetCompats, MODE_CLOSING)) {
-                                anim.play(getLauncherContentAnimator(false /* show */));
-                            }
-                            anim.play(getWindowAnimators(v, targetCompats));
+                        anim.play(getIconAnimator(v));
+                        if (launcherClosing) {
+                            Pair<AnimatorSet, Runnable> launcherContentAnimator =
+                                    getLauncherContentAnimator(false /* show */);
+                            anim.play(launcherContentAnimator.first);
+                            anim.addListener(new AnimatorListenerAdapter() {
+                                @Override
+                                public void onAnimationEnd(Animator animation) {
+                                    launcherContentAnimator.second.run();
+                                }
+                            });
                         }
-                        return anim;
+                        anim.play(getOpeningWindowAnimators(v, targetCompats));
                     }
-                };
 
-                int duration = findTaskViewToLaunch(launcher, v, null) != null
-                        ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION;
-                int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION;
-                return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
-                        runner, duration, statusBarTransitionDelay));
-            } catch (NoClassDefFoundError e) {
-                // Gracefully fall back to default launch options if the user's platform doesn't
-                // have the latest changes.
-            }
+                    if (launcherClosing) {
+                        anim.addListener(mForceInvisibleListener);
+                    }
+
+                    result.setAnimation(anim);
+                }
+            };
+
+            int duration = findTaskViewToLaunch(launcher, v, null) != null
+                    ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION;
+            int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION;
+            return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
+                    runner, duration, statusBarTransitionDelay));
         }
         return getDefaultActivityLaunchOptions(launcher, v);
     }
@@ -222,7 +249,7 @@
                     mLauncher.getStateManager()
                             .createAnimationToNewWorkspace(NORMAL, RECENTS_LAUNCH_DURATION);
             controller.dispatchOnStart();
-            childStateAnimation = controller.getOriginalTarget();
+            childStateAnimation = controller.getTarget();
             launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
             windowAnimEndListener = new AnimatorListenerAdapter() {
                 @Override
@@ -250,8 +277,9 @@
      * @param show If true: Animate the content so that it moves upwards and fades in.
      *             Else: Animate the content so that it moves downwards and fades out.
      */
-    private AnimatorSet getLauncherContentAnimator(boolean show) {
+    private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean show) {
         AnimatorSet launcherAnimator = new AnimatorSet();
+        Runnable endListener;
 
         float[] alphas = show
                 ? new float[] {0, 1}
@@ -271,6 +299,13 @@
             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
             alpha.setDuration(217);
             alpha.setInterpolator(LINEAR);
+            appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            alpha.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    appsView.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+            });
             ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
             transY.setInterpolator(AGGRESSIVE_EASE);
             transY.setDuration(350);
@@ -278,13 +313,11 @@
             launcherAnimator.play(alpha);
             launcherAnimator.play(transY);
 
-            launcherAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    appsView.setAlpha(startAlpha);
-                    appsView.setTranslationY(startY);
-                }
-            });
+            endListener = () -> {
+                appsView.setAlpha(startAlpha);
+                appsView.setTranslationY(startY);
+                appsView.setLayerType(View.LAYER_TYPE_NONE, null);
+            };
         } else {
             mDragLayer.setAlpha(alphas[0]);
             mDragLayer.setTranslationY(trans[0]);
@@ -299,15 +332,14 @@
 
             launcherAnimator.play(dragLayerAlpha);
             launcherAnimator.play(dragLayerTransY);
-            launcherAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mDragLayer.setAlpha(1);
-                    mDragLayer.setTranslationY(0);
-                }
-            });
+            mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+            endListener = () -> {
+                mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
+                mDragLayer.setAlpha(1);
+                mDragLayer.setTranslationY(0);
+            };
         }
-        return launcherAnimator;
+        return new Pair<>(launcherAnimator, endListener);
     }
 
     /**
@@ -422,7 +454,7 @@
     /**
      * @return Animator that controls the window of the opening targets.
      */
-    private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
+    private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
         Rect bounds = new Rect();
         if (v.getParent() instanceof DeepShortcutView) {
             // Deep shortcut views have their icon drawn in a separate view.
@@ -443,7 +475,6 @@
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
             // Fade alpha for the app window.
             FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR);
-
             boolean isFirstFrame = true;
 
             @Override
@@ -490,6 +521,10 @@
                 crop.bottom = (int) (crop.top + cropHeight);
 
                 TransactionCompat t = new TransactionCompat();
+                if (isFirstFrame) {
+                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_OPENING);
+                    isFirstFrame = false;
+                }
                 for (RemoteAnimationTargetCompat target : targets) {
                     if (target.mode == MODE_OPENING) {
                         t.setAlpha(target.leash, mAlpha.value);
@@ -501,15 +536,10 @@
                         t.setWindowCrop(target.leash, crop);
                         t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface));
                     }
-                    if (isFirstFrame) {
-                        t.show(target.leash);
-                    }
                 }
-                t.setEarlyWakeup();
                 t.apply();
 
                 matrix.reset();
-                isFirstFrame = false;
             }
         });
         return appAnimator;
@@ -521,19 +551,14 @@
     private void registerRemoteAnimations() {
         // Unregister this
         if (hasControlRemoteAppTransitionPermission()) {
-            try {
-                RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
-                definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
-                        WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
-                        new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
-                                CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+            RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
+            definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
+                    WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
+                    new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
+                            CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
 
-//      TODO: App controlled transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
-
-                new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
-            } catch (NoClassDefFoundError e) {
-                // Gracefully fall back if the user's platform doesn't have the latest changes
-            }
+            // TODO: Transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+            new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
         }
     }
 
@@ -546,9 +571,18 @@
      *         ie. pressing home, swiping up from nav bar.
      */
     private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
-        return new LauncherAnimationRunner(mHandler) {
+        return new LauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */) {
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                if (!mLauncher.hasBeenResumed()) {
+                    // If launcher is not resumed, wait until new async-frame after resume
+                    mLauncher.setOnResumeCallback(() ->
+                            postAsyncCallback(mHandler, () ->
+                                    onCreateAnimation(targetCompats, result)));
+                    return;
+                }
+
                 AnimatorSet anim = null;
                 RemoteAnimationProvider provider = mRemoteAnimationProvider;
                 if (provider != null) {
@@ -575,8 +609,8 @@
                     }
                 }
 
-                mLauncher.setForceInvisible(false);
-                return anim;
+                mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
+                result.setAnimation(anim);
             }
         };
     }
@@ -602,6 +636,10 @@
             @Override
             public void onUpdate(float percent) {
                 TransactionCompat t = new TransactionCompat();
+                if (isFirstFrame) {
+                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_CLOSING);
+                    isFirstFrame = false;
+                }
                 for (RemoteAnimationTargetCompat app : targets) {
                     if (app.mode == RemoteAnimationTargetCompat.MODE_CLOSING) {
                         t.setAlpha(app.leash, mAlpha.value);
@@ -612,19 +650,10 @@
                         matrix.postTranslate(app.position.x, app.position.y);
                         t.setMatrix(app.leash, matrix);
                     }
-                    if (isFirstFrame) {
-                        int layer = app.mode == RemoteAnimationTargetCompat.MODE_CLOSING
-                                ? Integer.MAX_VALUE
-                                : app.prefixOrderIndex;
-                        t.setLayer(app.leash, layer);
-                        t.show(app.leash);
-                    }
                 }
-                t.setEarlyWakeup();
                 t.apply();
 
                 matrix.reset();
-                isFirstFrame = false;
             }
         });
 
@@ -637,9 +666,16 @@
     private void createLauncherResumeAnimation(AnimatorSet anim) {
         if (mLauncher.isInState(LauncherState.ALL_APPS)
                 || mLauncher.getDeviceProfile().isVerticalBarLayout()) {
-            AnimatorSet contentAnimator = getLauncherContentAnimator(true /* show */);
-            contentAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
-            anim.play(contentAnimator);
+            Pair<AnimatorSet, Runnable> contentAnimator =
+                    getLauncherContentAnimator(true /* show */);
+            contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
+            anim.play(contentAnimator.first);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    contentAnimator.second.run();
+                }
+            });
         } else {
             AnimatorSet workspaceAnimator = new AnimatorSet();
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index d2f5487..7da50c8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -16,7 +16,7 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.APPS_VIEW_SHOWN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
 import android.view.View;
@@ -47,8 +47,8 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)) {
-            launcher.getSharedPrefs().edit().putBoolean(APPS_VIEW_SHOWN, true).apply();
+        if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+            launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
         }
 
         AbstractFloatingView.closeAllOpenViews(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index f98f7a5..496fa96 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.uioverrides;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.views.RecentsView;
@@ -39,11 +38,6 @@
         recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub();
     }
 
-    public void onStateEnabled(Launcher launcher) {
-        super.onStateEnabled(launcher);
-        AbstractFloatingView.closeAllOpenViews(launcher);
-    }
-
     @Override
     public int getVisibleElements(Launcher launcher) {
         return NONE;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
index 3622fc4..a7cf545 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
@@ -12,6 +12,7 @@
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.quickstep.RecentsModel;
 import com.android.quickstep.util.SysuiEventLogger;
 
 /**
@@ -19,6 +20,8 @@
  */
 public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
 
+    private static final String TAG = "LandscapeEdgeSwipeCtrl";
+
     public LandscapeEdgeSwipeController(Launcher l) {
         super(l, SwipeDetector.HORIZONTAL);
     }
@@ -69,6 +72,7 @@
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
         if (mFromState == NORMAL && targetState == OVERVIEW) {
+            RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
             SysuiEventLogger.writeDummyRecentsTransition(0);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 9c7db30..61422e0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,14 +16,17 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
 import android.view.View;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.views.RecentsView;
 
@@ -56,8 +59,13 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
+        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+            launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+        }
+
         RecentsView rv = launcher.getOverviewPanel();
         rv.setOverviewStateEnabled(true);
+        AbstractFloatingView.closeAllOpenViews(launcher);
     }
 
     @Override
@@ -69,6 +77,7 @@
     @Override
     public void onStateTransitionEnd(Launcher launcher) {
         launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
+        DiscoveryBounce.showForOverviewIfNeeded(launcher);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
similarity index 89%
rename from quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
index 30ceb43..e7816be 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
@@ -28,11 +28,13 @@
 import com.android.quickstep.views.RecentsView;
 
 /**
- * Touch controller from going from OVERVIEW to ALL_APPS
+ * Touch controller from going from OVERVIEW to ALL_APPS.
+ *
+ * This is used in landscape mode. It is also used in portrait mode for the fallback recents.
  */
-public class LandscapeStatesTouchController extends PortraitStatesTouchController {
+public class OverviewToAllAppsTouchController extends PortraitStatesTouchController {
 
-    public LandscapeStatesTouchController(Launcher l) {
+    public OverviewToAllAppsTouchController(Launcher l) {
         super(l);
     }
 
@@ -69,4 +71,5 @@
         }
         return fromState;
     }
+
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 012b545..2e95c04 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.SysuiEventLogger;
 import com.android.quickstep.views.RecentsView;
@@ -49,6 +50,8 @@
  */
 public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
 
+    private static final String TAG = "PortraitStatesTouchCtrl";
+
     private static final float TOTAL_DISTANCE_MULTIPLIER = 3f;
     private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER;
 
@@ -131,7 +134,9 @@
             directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
             mStartContainerType = ContainerType.HOTSEAT;
         } else if (mLauncher.isInState(OVERVIEW)) {
-            directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+            boolean canSwipeDownFromOverview = getTargetState(OVERVIEW, false) != OVERVIEW;
+            directionsToDetectScroll = canSwipeDownFromOverview ? SwipeDetector.DIRECTION_BOTH
+                    : SwipeDetector.DIRECTION_POSITIVE;
             mStartContainerType = ContainerType.TASKSWITCHER;
         } else {
             return 0;
@@ -282,6 +287,7 @@
     protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
         super.onSwipeInteractionCompleted(targetState, logAction);
         if (mFromState == NORMAL && targetState == OVERVIEW) {
+            RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG);
             SysuiEventLogger.writeDummyRecentsTransition(0);
         }
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 124ec20..49d4931 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -20,7 +20,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
-import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
+import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
@@ -33,21 +33,24 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.RecentsViewContainer;
 
 @TargetApi(Build.VERSION_CODES.O)
 public class RecentsViewStateController implements StateHandler {
 
     private final Launcher mLauncher;
     private final LauncherRecentsView mRecentsView;
+    private final RecentsViewContainer mRecentsViewContainer;
 
     public RecentsViewStateController(Launcher launcher) {
         mLauncher = launcher;
         mRecentsView = launcher.getOverviewPanel();
+        mRecentsViewContainer = launcher.getOverviewPanelContainer();
     }
 
     @Override
     public void setState(LauncherState state) {
-        mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
+        mRecentsViewContainer.setContentAlpha(state.overviewUi ? 1 : 0);
         float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
         mRecentsView.setAdjacentScale(scaleTranslationYFactor[0]);
         mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
@@ -66,7 +69,7 @@
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
         setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
                 builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
-        setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
+        setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
                 AGGRESSIVE_EASE_IN_OUT);
 
         if (!toState.overviewUi) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 4c9fd5a..63a7984 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 
 import android.animation.Animator;
@@ -33,6 +34,7 @@
 import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 
@@ -45,7 +47,6 @@
     private static final String TAG = "OverviewSwipeController";
 
     private static final float ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS = 0.1f;
-    private static final int SINGLE_FRAME_MS = 16;
 
     // Progress after which the transition is assumed to be a success in case user does not fling
     private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
@@ -117,11 +118,18 @@
                     TaskView view = mRecentsView.getPageAt(i);
                     if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
                             .isEventOverView(view, ev)) {
-                        // The task can be dragged up to dismiss it,
-                        // and down to open if it's the current page.
                         mTaskBeingDragged = view;
-                        directionsToDetectScroll = i == mRecentsView.getCurrentPage()
-                                ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
+                        if (!OverviewInteractionState.getInstance(mActivity)
+                                .isSwipeUpGestureEnabled()) {
+                            // Don't allow swipe down to open if we don't support swipe up
+                            // to enter overview.
+                            directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+                        } else {
+                            // The task can be dragged up to dismiss it,
+                            // and down to open if it's the current page.
+                            directionsToDetectScroll = i == mRecentsView.getCurrentPage()
+                                    ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
+                        }
                         break;
                     }
                 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index c1590f6..01e2bf3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,14 +16,13 @@
 
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.Utilities.getPrefs;
-import static com.android.quickstep.OverviewInteractionState.KEY_SWIPE_UP_ENABLED;
-import static com.android.launcher3.LauncherState.ALL_APPS;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.DeviceProfile;
@@ -39,18 +38,18 @@
 public class UiFactory {
 
     public static TouchController[] createTouchControllers(Launcher launcher) {
-        SharedPreferences prefs = getPrefs(launcher);
-        boolean swipeUpEnabled = prefs.getBoolean(KEY_SWIPE_UP_ENABLED, true);
+        boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
+                .isSwipeUpGestureEnabled();
         if (!swipeUpEnabled) {
             return new TouchController[] {
                     launcher.getDragController(),
-                    new LandscapeStatesTouchController(launcher),
+                    new OverviewToAllAppsTouchController(launcher),
                     new LauncherTaskViewcontroller(launcher)};
         }
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return new TouchController[] {
                     launcher.getDragController(),
-                    new LandscapeStatesTouchController(launcher),
+                    new OverviewToAllAppsTouchController(launcher),
                     new LandscapeEdgeSwipeController(launcher),
                     new LauncherTaskViewcontroller(launcher)};
         } else {
@@ -61,6 +60,10 @@
         }
     }
 
+    public static void setOnTouchControllersChangedListener(Context context, Runnable listener) {
+        OverviewInteractionState.getInstance(context).setOnSwipeUpSettingChangedListener(listener);
+    }
+
     public static StateHandler[] getStateHandler(Launcher launcher) {
         return new StateHandler[] {
                 launcher.getAllAppsController(), launcher.getWorkspace(),
@@ -73,7 +76,8 @@
                 && launcher.hasWindowFocus();
         if (shouldBackButtonBeHidden) {
             // Show the back button if there is a floating view visible.
-            shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenView(launcher) == null;
+            shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher,
+                    TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null;
         }
         OverviewInteractionState.getInstance(launcher)
                 .setBackButtonVisible(!shouldBackButtonBeHidden);
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 95947d7..88cd376 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.os.Looper;
 import android.support.annotation.Nullable;
 import android.support.annotation.UiThread;
 import android.view.View;
@@ -39,16 +38,19 @@
 import com.android.launcher3.LauncherInitListener;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.views.LauncherLayoutListener;
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
+import com.android.quickstep.views.RecentsViewContainer;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.BiPredicate;
+import java.util.function.Consumer;
 
 /**
  * Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
@@ -65,9 +67,9 @@
      */
     boolean onQuickInteractionStart(T activity, boolean activityVisible);
 
-    void executeOnWindowAvailable(T activity, Runnable action);
+    float getTranslationYForQuickScrub(T activity);
 
-    void executeOnNextDraw(T activity, TaskView targetView, Runnable action);
+    void executeOnWindowAvailable(T activity, Runnable action);
 
     void onTransitionCancelled(T activity, boolean activityVisible);
 
@@ -75,11 +77,8 @@
 
     void onSwipeUpComplete(T activity);
 
-    void prepareRecentsUI(T activity, boolean activityVisible);
-
-    AnimatorPlaybackController createControllerForVisibleActivity(T activity);
-
-    AnimatorPlaybackController createControllerForHiddenActivity(T activity, int transitionLength);
+    AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
+            Consumer<AnimatorPlaybackController> callback);
 
     ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
 
@@ -93,12 +92,23 @@
     @UiThread
     boolean switchToRecentsIfVisible();
 
+    Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
+
+    boolean shouldMinimizeSplitScreen();
+
     /**
      * @return {@code true} if recents activity should be started immediately on touchDown,
      *         {@code false} if it should deferred until some threshold is crossed.
      */
     boolean deferStartingActivity(int downHitTarget);
 
+    boolean supportsLongSwipe(T activity);
+
+    /**
+     * Must return a non-null controller is supportsLongSwipe was true.
+     */
+    LongSwipeHelper getLongSwipeController(T activity, RemoteAnimationTargetSet targetSet);
+
     class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
 
         @Override
@@ -119,6 +129,13 @@
         }
 
         @Override
+        public float getTranslationYForQuickScrub(Launcher activity) {
+            LauncherRecentsView recentsView = activity.getOverviewPanel();
+            float transYFactor = FAST_OVERVIEW.getOverviewScaleAndTranslationYFactor(activity)[1];
+            return recentsView.computeTranslationYForFactor(transYFactor);
+        }
+
+        @Override
         public void executeOnWindowAvailable(Launcher activity, Runnable action) {
             if (activity.getWorkspace().runOnOverlayHidden(action)) {
                 // Notify the activity that qiuckscrub has started
@@ -127,22 +144,8 @@
         }
 
         @Override
-        public void executeOnNextDraw(Launcher activity, TaskView targetView, Runnable action) {
-            ViewOnDrawExecutor executor = new ViewOnDrawExecutor() {
-                @Override
-                public void onViewDetachedFromWindow(View v) {
-                    if (!isCompleted()) {
-                        runAllTasks();
-                    }
-                }
-            };
-            executor.attachTo(activity, targetView, false /* waitForLoadAnimation */);
-            executor.execute(action);
-        }
-
-        @Override
         public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
-            LauncherRecentsView.getPageRect(dp, context, outRect);
+            LayoutUtils.calculateLauncherTaskSize(context, dp, outRect);
             if (dp.isVerticalBarLayout()) {
                 Rect targetInsets = dp.getInsets();
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
@@ -162,15 +165,19 @@
         public void onSwipeUpComplete(Launcher activity) {
             // Re apply state in case we did something funky during the transition.
             activity.getStateManager().reapplyState();
+            DiscoveryBounce.showForOverviewIfNeeded(activity);
         }
 
         @Override
-        public void prepareRecentsUI(Launcher activity, boolean activityVisible) {
-            LauncherState startState = activity.getStateManager().getState();
+        public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
+                Consumer<AnimatorPlaybackController> callback) {
+            final LauncherState startState = activity.getStateManager().getState();
+
+            LauncherState resetState = startState;
             if (startState.disableRestore) {
-                startState = activity.getStateManager().getRestState();
+                resetState = activity.getStateManager().getRestState();
             }
-            activity.getStateManager().setRestState(startState);
+            activity.getStateManager().setRestState(resetState);
 
             if (!activityVisible) {
                 // Since the launcher is not visible, we can safely reset the scroll position.
@@ -181,38 +188,51 @@
                 // Optimization, hide the all apps view to prevent layout while initializing
                 activity.getAppsView().getContentView().setVisibility(View.GONE);
             }
+
+            return new AnimationFactory() {
+                @Override
+                public void createActivityController(long transitionLength) {
+                    createActivityControllerInternal(activity, activityVisible, transitionLength,
+                            callback);
+                }
+
+                @Override
+                public void onTransitionCancelled() {
+                    activity.getStateManager().goToState(startState, false /* animate */);
+                }
+            };
         }
 
-        @Override
-        public AnimatorPlaybackController createControllerForVisibleActivity(Launcher activity) {
-            DeviceProfile dp = activity.getDeviceProfile();
-            long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
-            return activity.getStateManager().createAnimationToNewWorkspace(OVERVIEW, accuracy);
-        }
-
-        @Override
-        public AnimatorPlaybackController createControllerForHiddenActivity(
-                Launcher activity, int transitionLength) {
-            AllAppsTransitionController controller = activity.getAllAppsController();
-            AnimatorSet anim = new AnimatorSet();
-            if (activity.getDeviceProfile().isVerticalBarLayout()) {
-                // TODO:
-            } else {
-                float scrollRange = Math.max(controller.getShiftRange(), 1);
-                float progressDelta = (transitionLength / scrollRange);
-
-                float endProgress = OVERVIEW.getVerticalProgress(activity);
-                float startProgress = endProgress + progressDelta;
-                ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
-                        controller, ALL_APPS_PROGRESS, startProgress, endProgress);
-                shiftAnim.setInterpolator(LINEAR);
-                anim.play(shiftAnim);
+        private void createActivityControllerInternal(Launcher activity, boolean wasVisible,
+                long transitionLength, Consumer<AnimatorPlaybackController> callback) {
+            if (wasVisible) {
+                DeviceProfile dp = activity.getDeviceProfile();
+                long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
+                callback.accept(activity.getStateManager()
+                        .createAnimationToNewWorkspace(OVERVIEW, accuracy));
+                return;
             }
 
-            // TODO: Link this animation to state animation, so that it is cancelled
-            // automatically on state change
+            if (activity.getDeviceProfile().isVerticalBarLayout()) {
+                return;
+            }
+
+            AllAppsTransitionController controller = activity.getAllAppsController();
+            AnimatorSet anim = new AnimatorSet();
+
+            float scrollRange = Math.max(controller.getShiftRange(), 1);
+            float progressDelta = (transitionLength / scrollRange);
+
+            float endProgress = OVERVIEW.getVerticalProgress(activity);
+            float startProgress = endProgress + progressDelta;
+            ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
+                    controller, ALL_APPS_PROGRESS, startProgress, endProgress);
+            shiftAnim.setInterpolator(LINEAR);
+            anim.play(shiftAnim);
+
             anim.setDuration(transitionLength * 2);
-            return AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+            activity.getStateManager().setCurrentAnimation(anim);
+            callback.accept(AnimatorPlaybackController.wrap(anim, transitionLength * 2));
         }
 
         @Override
@@ -261,6 +281,30 @@
         public boolean deferStartingActivity(int downHitTarget) {
             return downHitTarget == HIT_TARGET_BACK;
         }
+
+        @Override
+        public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+            return homeBounds;
+        }
+
+        @Override
+        public boolean shouldMinimizeSplitScreen() {
+            return true;
+        }
+
+        @Override
+        public boolean supportsLongSwipe(Launcher activity) {
+            return !activity.getDeviceProfile().isVerticalBarLayout();
+        }
+
+        @Override
+        public LongSwipeHelper getLongSwipeController(Launcher activity,
+                RemoteAnimationTargetSet targetSet) {
+            if (activity.getDeviceProfile().isVerticalBarLayout()) {
+                return null;
+            }
+            return new LongSwipeHelper(activity, targetSet);
+        }
     }
 
     class FallbackActivityControllerHelper implements ActivityControlHelper<RecentsActivity> {
@@ -277,15 +321,13 @@
         }
 
         @Override
-        public void executeOnWindowAvailable(RecentsActivity activity, Runnable action) {
-            action.run();
+        public float getTranslationYForQuickScrub(RecentsActivity activity) {
+            return 0;
         }
 
         @Override
-        public void executeOnNextDraw(RecentsActivity activity, TaskView targetView,
-                Runnable action) {
-            // TODO:
-            new Handler(Looper.getMainLooper()).post(action);
+        public void executeOnWindowAvailable(RecentsActivity activity, Runnable action) {
+            action.run();
         }
 
         @Override
@@ -295,7 +337,7 @@
 
         @Override
         public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
-            FallbackRecentsView.getPageRect(dp, context, outRect);
+            LayoutUtils.calculateFallbackTaskSize(context, dp, outRect);
             if (dp.isVerticalBarLayout()) {
                 Rect targetInsets = dp.getInsets();
                 int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
@@ -311,23 +353,43 @@
         }
 
         @Override
-        public void prepareRecentsUI(RecentsActivity activity, boolean activityVisible) {
-            // TODO:
-        }
+        public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
+                Consumer<AnimatorPlaybackController> callback) {
+            if (activityVisible) {
+                return (transitionLength) -> { };
+            }
 
-        @Override
-        public AnimatorPlaybackController createControllerForVisibleActivity(
-                RecentsActivity activity) {
-            DeviceProfile dp = activity.getDeviceProfile();
-            return createControllerForHiddenActivity(activity, Math.max(dp.widthPx, dp.heightPx));
-        }
+            RecentsViewContainer rv = activity.getOverviewPanelContainer();
+            rv.setContentAlpha(0);
 
-        @Override
-        public AnimatorPlaybackController createControllerForHiddenActivity(
-                RecentsActivity activity, int transitionLength) {
-            // We do not animate anything. Create a empty controller
-            AnimatorSet anim = new AnimatorSet();
-            return AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+            return new AnimationFactory() {
+
+                boolean isAnimatingHome = false;
+
+                @Override
+                public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+                    isAnimatingHome = targets != null && targets.isAnimatingHome();
+                    if (!isAnimatingHome) {
+                        rv.setContentAlpha(1);
+                    }
+                    createActivityController(getSwipeUpDestinationAndLength(
+                            activity.getDeviceProfile(), activity, new Rect()));
+                }
+
+                @Override
+                public void createActivityController(long transitionLength) {
+                    if (!isAnimatingHome) {
+                        return;
+                    }
+
+                    ObjectAnimator anim = ObjectAnimator
+                            .ofFloat(rv, RecentsViewContainer.CONTENT_ALPHA, 0, 1);
+                    anim.setDuration(transitionLength).setInterpolator(LINEAR);
+                    AnimatorSet animatorSet = new AnimatorSet();
+                    animatorSet.play(anim);
+                    callback.accept(AnimatorPlaybackController.wrap(animatorSet, transitionLength));
+                }
+            };
         }
 
         @Override
@@ -378,6 +440,29 @@
             // Always defer starting the activity when using fallback
             return true;
         }
+
+        @Override
+        public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+            // TODO: Remove this once b/77875376 is fixed
+            return target.sourceContainerBounds;
+        }
+
+        @Override
+        public boolean shouldMinimizeSplitScreen() {
+            // TODO: Remove this once b/77875376 is fixed
+            return false;
+        }
+
+        @Override
+        public boolean supportsLongSwipe(RecentsActivity activity) {
+            return false;
+        }
+
+        @Override
+        public LongSwipeHelper getLongSwipeController(RecentsActivity activity,
+                RemoteAnimationTargetSet targetSet) {
+            return null;
+        }
     }
 
     interface LayoutListener {
@@ -398,4 +483,13 @@
         void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
                 Context context, Handler handler, long duration);
     }
+
+    interface AnimationFactory {
+
+        default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
+
+        void createActivityController(long transitionLength);
+
+        default void onTransitionCancelled() { }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
new file mode 100644
index 0000000..4ce18b3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
+import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
+import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
+
+import android.animation.ValueAnimator;
+import android.view.Surface;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+/**
+ * Utility class to handle long swipe from an app.
+ * This assumes the presence of Launcher activity as long swipe is not supported on the
+ * fallback activity.
+ */
+public class LongSwipeHelper {
+
+    private static final float MIN_PROGRESS_TO_ALL_APPS = 0.35f;
+    private static final float SWIPE_DURATION_MULTIPLIER =
+            Math.min(1 / MIN_PROGRESS_TO_ALL_APPS, 1 / (1 - MIN_PROGRESS_TO_ALL_APPS));
+
+    private final Launcher mLauncher;
+    private final RemoteAnimationTargetSet mTargetSet;
+
+    private float mMaxSwipeDistance = 1;
+    private AnimatorPlaybackController mAnimator;
+
+    LongSwipeHelper(Launcher launcher, RemoteAnimationTargetSet targetSet) {
+        mLauncher = launcher;
+        mTargetSet = targetSet;
+        init();
+    }
+
+    private void init() {
+        setTargetAlpha(0, true);
+
+        // Init animations
+        AllAppsTransitionController controller = mLauncher.getAllAppsController();
+        // TODO: Scale it down so that we can reach all-apps in screen space
+        mMaxSwipeDistance = Math.max(1, controller.getProgress() * controller.getShiftRange());
+        mAnimator = mLauncher.getStateManager()
+                .createAnimationToNewWorkspace(ALL_APPS, Math.round(2 * mMaxSwipeDistance));
+        mAnimator.dispatchOnStart();
+    }
+
+    public void onMove(float displacement) {
+        mAnimator.setPlayFraction(displacement / mMaxSwipeDistance);
+    }
+
+    public void destroy() {
+        // TODO: We can probably also hide the task view
+        setTargetAlpha(1, false);
+
+        mLauncher.getStateManager().goToState(OVERVIEW, false);
+    }
+
+    public void end(float velocity, boolean isFling, Runnable callback) {
+        long duration = MAX_SWIPE_DURATION;
+
+        final float currentFraction = mAnimator.getProgressFraction();
+        final boolean toAllApps;
+        float endProgress;
+
+        if (!isFling) {
+            toAllApps = currentFraction > MIN_PROGRESS_TO_ALL_APPS;
+            endProgress = toAllApps ? 1 : 0;
+
+            long expectedDuration = Math.abs(Math.round((endProgress - currentFraction)
+                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
+            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
+        } else {
+            toAllApps = velocity < 0;
+            endProgress = toAllApps ? 1 : 0;
+
+            float minFlingVelocity = mLauncher.getResources()
+                    .getDimension(R.dimen.quickstep_fling_min_velocity);
+            if (Math.abs(velocity) > minFlingVelocity && mMaxSwipeDistance > 0) {
+                float distanceToTravel = (endProgress - currentFraction) * mMaxSwipeDistance;
+
+                // we want the page's snap velocity to approximately match the velocity at
+                // which the user flings, so we scale the duration by a value near to the
+                // derivative of the scroll interpolator at zero, ie. 2.
+                long baseDuration = Math.round(1000 * Math.abs(distanceToTravel / velocity));
+                duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
+            }
+        }
+
+        mAnimator.setEndAction(() -> onSwipeAnimationComplete(toAllApps, isFling, callback));
+        ValueAnimator animator = mAnimator.getAnimationPlayer();
+        animator.setDuration(duration).setInterpolator(DEACCEL);
+        animator.setFloatValues(currentFraction, endProgress);
+        animator.start();
+    }
+
+    private void setTargetAlpha(float alpha, boolean defer) {
+        final Surface surface = getSurface(mLauncher.getDragLayer());
+        final long frameNumber = defer && surface != null ? getNextFrameNumber(surface) : -1;
+        if (defer) {
+            if (frameNumber == -1) {
+                defer = false;
+            } else {
+                mLauncher.getDragLayer().invalidate();
+            }
+        }
+
+        TransactionCompat transaction = new TransactionCompat();
+        for (RemoteAnimationTargetCompat app : mTargetSet.apps) {
+            if (!(app.isNotInRecents
+                    || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
+                transaction.setAlpha(app.leash, alpha);
+                if (defer) {
+                    transaction.deferTransactionUntil(app.leash, surface, frameNumber);
+                }
+            }
+        }
+        transaction.apply();
+    }
+
+    private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
+        mLauncher.getStateManager().goToState(toAllApps ? ALL_APPS : OVERVIEW, false);
+        if (!toAllApps) {
+            DiscoveryBounce.showForOverviewIfNeeded(mLauncher);
+        }
+
+        mLauncher.getUserEventDispatcher().logStateChangeAction(
+                isFling ? Touch.FLING : Touch.SWIPE, Direction.UP,
+                ContainerType.NAVBAR, ContainerType.APP,
+                toAllApps ? ContainerType.ALLAPPS : ContainerType.TASKSWITCHER,
+                0);
+
+        callback.run();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 7a74176..bda3d06 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -59,4 +59,8 @@
     public int getState() {
         return mState;
     }
+
+    public boolean hasStates(int stateMask) {
+        return (mState & stateMask) == stateMask;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 28c950b..6ce9372 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -22,7 +22,7 @@
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_DRAG_SLOP_PX;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -45,9 +45,11 @@
 
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.AssistDataReceiver;
 import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RecentsAnimationListener;
@@ -77,6 +79,7 @@
     private final PointF mLastPos = new PointF();
     private int mActivePointerId = INVALID_POINTER_ID;
     private boolean mPassedInitialSlop;
+    private int mQuickStepDragSlop;
     private float mStartDisplacement;
     private WindowTransformSwipeHandler mInteractionHandler;
     private int mDisplayRotation;
@@ -118,6 +121,7 @@
                 mDownPos.set(ev.getX(), ev.getY());
                 mLastPos.set(mDownPos);
                 mPassedInitialSlop = false;
+                mQuickStepDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
 
                 // Start the window animation on down to give more time for launcher to draw if the
                 // user didn't start the gesture over the back button
@@ -150,7 +154,8 @@
                 }
                 mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
                 float displacement = getDisplacement(ev);
-                if (!mPassedInitialSlop && Math.abs(displacement) > QUICK_STEP_DRAG_SLOP_PX) {
+                if (!mPassedInitialSlop
+                        && Math.abs(displacement) > mQuickStepDragSlop) {
                     mPassedInitialSlop = true;
                     mStartDisplacement = displacement;
 
@@ -229,8 +234,9 @@
                             Rect minimizedHomeBounds) {
                         if (mInteractionHandler == handler) {
                             TraceHelper.partitionSection("RecentsController", "Received");
-                            handler.onRecentsAnimationStart(controller, apps, homeContentInsets,
-                                    minimizedHomeBounds);
+                            handler.onRecentsAnimationStart(controller,
+                                    new RemoteAnimationTargetSet(apps, MODE_CLOSING),
+                                    homeContentInsets, minimizedHomeBounds);
                         } else {
                             TraceHelper.endSection("RecentsController", "Finishing no handler");
                             controller.finish(false /* toHome */);
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 5563aed..43772fb 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -18,15 +18,14 @@
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.systemui.shared.system.ActivityManagerWrapper
-        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.PackageManagerWrapper
-        .ACTION_PREFERRED_ACTIVITY_CHANGED;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
+import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
@@ -43,22 +42,22 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewConfiguration;
-
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.ActivityControlHelper.AnimationFactory;
 import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
 import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
 import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.util.SysuiEventLogger;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
+import com.android.systemui.shared.system.TransactionCompat;
 import java.util.ArrayList;
 
 /**
@@ -205,6 +204,7 @@
 
         private ActivityInitListener mListener;
         private T mActivity;
+        private RecentsView mRecentsView;
 
         public RecentsActivityCommand() {
             mHelper = getActivityControlHelper();
@@ -253,17 +253,21 @@
         private boolean onActivityReady(T activity, Boolean wasVisible) {
             activity.<RecentsView>getOverviewPanel().setCurrentTask(mRunningTaskId);
             AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
-            mHelper.prepareRecentsUI(activity, wasVisible);
+            AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible,
+                    (controller) -> {
+                        controller.dispatchOnStart();
+                        ValueAnimator anim = controller.getAnimationPlayer()
+                                .setDuration(RECENTS_LAUNCH_DURATION);
+                        anim.setInterpolator(FAST_OUT_SLOW_IN);
+                        anim.start();
+                });
+            factory.onRemoteAnimationReceived(null);
             if (wasVisible) {
-                AnimatorPlaybackController controller =
-                        mHelper.createControllerForVisibleActivity(activity);
-                controller.dispatchOnStart();
-                ValueAnimator anim =
-                        controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
-                anim.setInterpolator(FAST_OUT_SLOW_IN);
-                anim.start();
+                factory.createActivityController(RECENTS_LAUNCH_DURATION);
             }
             mActivity = activity;
+            mRecentsView = mActivity.getOverviewPanel();
+            mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
             return false;
         }
 
@@ -271,23 +275,28 @@
             if (mListener != null) {
                 mListener.unregister();
             }
-            RemoteAnimationProvider.showOpeningTarget(targetCompats);
             AnimatorSet anim = new AnimatorSet();
+            anim.addListener(new AnimationSuccessListener() {
+                @Override
+                public void onAnimationSuccess(Animator animator) {
+                    if (mRecentsView != null) {
+                        mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */,
+                                true /* animate */);
+                    }
+                }
+            });
             if (mActivity == null) {
                 Log.e(TAG, "Animation created, before activity");
                 anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
                 return anim;
             }
 
-            RemoteAnimationTargetCompat closingTarget = null;
+            RemoteAnimationTargetSet targetSet =
+                    new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+
             // Use the top closing app to determine the insets for the animation
-            for (RemoteAnimationTargetCompat target : targetCompats) {
-                if (target.mode == MODE_CLOSING) {
-                    closingTarget = target;
-                    break;
-                }
-            }
-            if (closingTarget == null) {
+            RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
+            if (runningTaskTarget == null) {
                 Log.e(TAG, "No closing app");
                 anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
                 return anim;
@@ -302,20 +311,33 @@
             rootView.getLocationOnScreen(loc);
             Rect homeBounds = new Rect(loc[0], loc[1],
                     loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
-            clipHelper.updateSource(homeBounds, closingTarget);
+            clipHelper.updateSource(homeBounds, runningTaskTarget);
 
             Rect targetRect = new Rect();
             mHelper.getSwipeUpDestinationAndLength(
                     mActivity.getDeviceProfile(), mActivity, targetRect);
             clipHelper.updateTargetRect(targetRect);
-
+            clipHelper.prepareAnimation(false /* isOpening */);
 
             ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
             valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
             valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-            valueAnimator.addUpdateListener((v) -> {
-                clipHelper.applyTransform(targetCompats, (float) v.getAnimatedValue());
-            });
+            valueAnimator.addUpdateListener((v) ->
+                    clipHelper.applyTransform(targetSet, (float) v.getAnimatedValue()));
+
+            if (targetSet.isAnimatingHome()) {
+                // If we are animating home, fade in the opening targets
+                RemoteAnimationTargetSet openingSet =
+                        new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
+
+                TransactionCompat transaction = new TransactionCompat();
+                valueAnimator.addUpdateListener((v) -> {
+                    for (RemoteAnimationTargetCompat app : openingSet.apps) {
+                        transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
+                    }
+                    transaction.apply();
+                });
+            }
             anim.play(valueAnimator);
             return anim;
         }
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 22b1757..8923608 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -15,19 +15,20 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.Utilities.getPrefs;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
+import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
 
+import android.content.ContentResolver;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.database.ContentObserver;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.support.annotation.WorkerThread;
 import android.util.Log;
 
@@ -47,7 +48,7 @@
  *
  * @see com.android.systemui.shared.system.NavigationBarCompat.InteractionType and associated flags.
  */
-public class OverviewInteractionState implements OnSharedPreferenceChangeListener {
+public class OverviewInteractionState {
 
     private static final String TAG = "OverviewFlags";
 
@@ -70,12 +71,12 @@
         return INSTANCE;
     }
 
-    public static final String KEY_SWIPE_UP_ENABLED = "pref_enable_quickstep";
-
     private static final int MSG_SET_PROXY = 200;
     private static final int MSG_SET_BACK_BUTTON_VISIBLE = 201;
     private static final int MSG_SET_SWIPE_UP_ENABLED = 202;
 
+    private final SwipeUpGestureEnabledSettingObserver mSwipeUpSettingObserver;
+
     private final Handler mUiHandler;
     private final Handler mBgHandler;
 
@@ -84,23 +85,19 @@
     private boolean mBackButtonVisible = true;
     private boolean mSwipeUpEnabled = true;
 
+    private Runnable mOnSwipeUpSettingChangedListener;
+
     private OverviewInteractionState(Context context) {
         mUiHandler = new Handler(this::handleUiMessage);
         mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
 
-        SharedPreferences prefs = getPrefs(context);
-        prefs.registerOnSharedPreferenceChangeListener(this);
-        onSharedPreferenceChanged(prefs, KEY_SWIPE_UP_ENABLED);
+        mSwipeUpSettingObserver = new SwipeUpGestureEnabledSettingObserver(mUiHandler,
+                context.getContentResolver());
+        mSwipeUpSettingObserver.register();
     }
 
-    @Override
-    public void onSharedPreferenceChanged(SharedPreferences prefs, String s) {
-        if (KEY_SWIPE_UP_ENABLED.equals(s)) {
-            mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
-            boolean swipeUpEnabled = prefs.getBoolean(s, true);
-            mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED,
-                    swipeUpEnabled ? 1 : 0, 0).sendToTarget();
-        }
+    public boolean isSwipeUpGestureEnabled() {
+        return mSwipeUpEnabled;
     }
 
     public void setBackButtonVisible(boolean visible) {
@@ -128,12 +125,19 @@
                 break;
             case MSG_SET_SWIPE_UP_ENABLED:
                 mSwipeUpEnabled = msg.arg1 != 0;
+                if (mOnSwipeUpSettingChangedListener != null) {
+                    mOnSwipeUpSettingChangedListener.run();
+                }
                 break;
         }
         applyFlags();
         return true;
     }
 
+    public void setOnSwipeUpSettingChangedListener(Runnable listener) {
+        mOnSwipeUpSettingChangedListener = listener;
+    }
+
     @WorkerThread
     private void applyFlags() {
         if (mISystemUiProxy == null) {
@@ -152,4 +156,32 @@
             Log.w(TAG, "Unable to update overview interaction flags", e);
         }
     }
+
+    private class SwipeUpGestureEnabledSettingObserver extends ContentObserver {
+        private Handler mHandler;
+        private ContentResolver mResolver;
+
+        SwipeUpGestureEnabledSettingObserver(Handler handler, ContentResolver resolver) {
+            super(handler);
+            mHandler = handler;
+            mResolver = resolver;
+        }
+
+        public void register() {
+            mResolver.registerContentObserver(Settings.Secure.getUriFor(SWIPE_UP_SETTING_NAME),
+                    false, this);
+            mSwipeUpEnabled = getValue();
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+            mHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
+            mHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, getValue() ? 1 : 0, 0).sendToTarget();
+        }
+
+        private boolean getValue() {
+            return Settings.Secure.getInt(mResolver, SWIPE_UP_SETTING_NAME, 0) == 1;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 9ec9f52..b780a3e 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -29,7 +29,6 @@
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,6 +37,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAnimationRunner;
@@ -51,6 +51,7 @@
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.views.RecentsViewContainer;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -65,6 +66,7 @@
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
     private RecentsRootView mRecentsRootView;
     private FallbackRecentsView mFallbackRecentsView;
+    private RecentsViewContainer mOverviewPanelContainer;
 
     private Configuration mOldConfig;
 
@@ -73,16 +75,12 @@
         super.onCreate(savedInstanceState);
 
         mOldConfig = new Configuration(getResources().getConfiguration());
-        // In case we are reusing IDP, create a copy so that we dont conflict with Launcher
-        // activity.
-        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        setDeviceProfile(appState != null
-                ? appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this)
-                : new InvariantDeviceProfile(this).getDeviceProfile(this));
+        initDeviceProfile();
 
         setContentView(R.layout.fallback_recents_activity);
         mRecentsRootView = findViewById(R.id.drag_layer);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
+        mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
 
         mRecentsRootView.setup();
 
@@ -103,20 +101,19 @@
 
     @Override
     public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
-        mOldConfig.setTo(newConfig);
         onHandleConfigChanged();
         super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
     }
 
+    public void onRootViewSizeChanged() {
+        if (isInMultiWindowModeCompat()) {
+            onHandleConfigChanged();
+        }
+    }
+
     private void onHandleConfigChanged() {
         mUserEventDispatcher = null;
-
-        // In case we are reusing IDP, create a copy so that we dont conflict with Launcher
-        // activity.
-        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
-        setDeviceProfile(appState != null
-                ? appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this)
-                : new InvariantDeviceProfile(this).getDeviceProfile(this));
+        initDeviceProfile();
 
         AbstractFloatingView.closeOpenViews(this, true,
                 AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
@@ -124,7 +121,24 @@
 
         mRecentsRootView.setup();
         mRecentsRootView.dispatchInsets();
-        mRecentsRootView.requestLayout();
+    }
+
+    private void initDeviceProfile() {
+        // In case we are reusing IDP, create a copy so that we dont conflict with Launcher
+        // activity.
+        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+        if (isInMultiWindowModeCompat()) {
+            InvariantDeviceProfile idp = appState == null
+                    ? new InvariantDeviceProfile(this) : appState.getInvariantDeviceProfile();
+            DeviceProfile dp = idp.getDeviceProfile(this);
+            mDeviceProfile = mRecentsRootView == null ? dp.copy(this)
+                    : dp.getMultiWindowProfile(this, mRecentsRootView.getLastKnownSize());
+        } else {
+            // If we are reusing the Invariant device profile, make a copy.
+            mDeviceProfile = appState == null
+                    ? new InvariantDeviceProfile(this).getDeviceProfile(this)
+                    : appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this);
+        }
     }
 
     @Override
@@ -142,6 +156,10 @@
         return (T) mFallbackRecentsView;
     }
 
+    public RecentsViewContainer getOverviewPanelContainer() {
+        return mOverviewPanelContainer;
+    }
+
     @Override
     public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
         return null;
@@ -154,11 +172,13 @@
         }
 
         final TaskView taskView = (TaskView) v;
-        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler) {
+        RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler,
+                true /* startAtFrontOfQueue */) {
 
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
-                return composeRecentsLaunchAnimator(taskView, targetCompats);
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                result.setAnimation(composeRecentsLaunchAnimator(taskView, targetCompats));
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
@@ -198,12 +218,23 @@
 
     @Override
     protected void onStart() {
+        // Set the alpha to 1 before calling super, as it may get set back to 0 due to
+        // onActivityStart callback.
+        mFallbackRecentsView.setContentAlpha(1);
         super.onStart();
         UiFactory.onStart(this);
         mFallbackRecentsView.resetTaskVisuals();
     }
 
     @Override
+    protected void onStop() {
+        super.onStop();
+
+        // Workaround for b/78520668, explicitly trim memory once UI is hidden
+        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+    }
+
+    @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
         UiFactory.onTrimMemory(this, level);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 12f8d52..4ba9e02 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -16,9 +16,9 @@
 package com.android.quickstep;
 
 import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.systemui.shared.system.BackgroundExecutor;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 /**
  * Wrapper around RecentsAnimationController to help with some synchronization
@@ -26,17 +26,17 @@
 public class RecentsAnimationWrapper {
 
     public RecentsAnimationControllerCompat controller;
-    public RemoteAnimationTargetCompat[] targets;
+    public RemoteAnimationTargetSet targetSet;
 
     private boolean mInputConsumerEnabled = false;
     private boolean mBehindSystemBars = true;
     private boolean mSplitScreenMinimized = false;
 
     public synchronized void setController(
-            RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] targets) {
+            RecentsAnimationControllerCompat controller, RemoteAnimationTargetSet targetSet) {
         TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
         this.controller = controller;
-        this.targets = targets;
+        this.targetSet = targetSet;
 
         if (mInputConsumerEnabled) {
             enableInputConsumer();
@@ -115,4 +115,15 @@
             }
         });
     }
+
+    public void hideCurrentInputMethod() {
+        BackgroundExecutor.get().submit(() -> {
+            synchronized (this) {
+                TraceHelper.partitionSection("RecentsController", "Hiding currentinput method");
+                if (controller != null) {
+                    controller.hideCurrentInputMethod();
+                }
+            }
+        });
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 4652f2d..7676a70 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -26,8 +26,10 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.support.annotation.WorkerThread;
+import android.util.Log;
 import android.util.LruCache;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityManager;
@@ -85,7 +87,8 @@
     private int mTaskChangeId;
     private ISystemUiProxy mSystemUiProxy;
     private boolean mClearAssistCacheOnStackChange = true;
-    private final boolean mPreloadTasksInBackground;
+    private final boolean mIsLowRamDevice;
+    private boolean mPreloadTasksInBackground;
     private final AccessibilityManager mAccessibilityManager;
 
     private RecentsModel(Context context) {
@@ -93,7 +96,7 @@
 
         ActivityManager activityManager =
                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
-        mPreloadTasksInBackground = !activityManager.isLowRamDevice();
+        mIsLowRamDevice = activityManager.isLowRamDevice();
         mMainThreadExecutor = new MainThreadExecutor();
 
         Resources res = context.getResources();
@@ -158,6 +161,10 @@
         return requestId;
     }
 
+    public void setPreloadTasksInBackground(boolean preloadTasksInBackground) {
+        mPreloadTasksInBackground = preloadTasksInBackground && !mIsLowRamDevice;
+    }
+
     @Override
     public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
         mTaskChangeId++;
@@ -234,6 +241,19 @@
         mRecentsTaskLoader.onTrimMemory(level);
     }
 
+    public void onOverviewShown(boolean fromHome, String tag) {
+        if (mSystemUiProxy == null) {
+            return;
+        }
+        try {
+            mSystemUiProxy.onOverviewShown(fromHome);
+        } catch (RemoteException e) {
+            Log.w(tag,
+                    "Failed to notify SysUI of overview shown from " + (fromHome ? "home" : "app")
+                            + ": ", e);
+        }
+    }
+
     @WorkerThread
     public void preloadAssistData(int taskId, Bundle data) {
         mMainThreadExecutor.execute(() -> {
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 2ebf252..7c69a8d 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -101,13 +101,9 @@
         }
     }
 
-    public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener,
-            DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener {
+    public static class SplitScreen extends TaskSystemShortcut {
 
         private Handler mHandler;
-        private RecentsView mRecentsView;
-        private TaskView mTaskView;
-        private BaseDraggingActivity mActivity;
 
         public SplitScreen() {
             super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
@@ -125,11 +121,35 @@
             if (!task.isDockable) {
                 return null;
             }
-            mActivity = activity;
-            mRecentsView = activity.getOverviewPanel();
-            mTaskView = taskView;
+            final RecentsView recentsView = activity.getOverviewPanel();
+
             final TaskThumbnailView thumbnailView = taskView.getThumbnail();
             return (v -> {
+                final View.OnLayoutChangeListener onLayoutChangeListener =
+                        new View.OnLayoutChangeListener() {
+                            @Override
+                            public void onLayoutChange(View v, int l, int t, int r, int b,
+                                    int oldL, int oldT, int oldR, int oldB) {
+                                taskView.getRootView().removeOnLayoutChangeListener(this);
+                                recentsView.removeIgnoreResetTask(taskView);
+
+                                // Start animating in the side pages once launcher has been resized
+                                recentsView.dismissTask(taskView, false, false);
+                            }
+                        };
+
+                final DeviceProfile.OnDeviceProfileChangeListener onDeviceProfileChangeListener =
+                        new DeviceProfile.OnDeviceProfileChangeListener() {
+                            @Override
+                            public void onDeviceProfileChanged(DeviceProfile dp) {
+                                activity.removeOnDeviceProfileChangeListener(this);
+                                if (dp.isMultiWindowMode) {
+                                    taskView.getRootView().addOnLayoutChangeListener(
+                                            onLayoutChangeListener);
+                                }
+                            }
+                        };
+
                 AbstractFloatingView.closeOpenViews(activity, true,
                         AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
 
@@ -145,15 +165,14 @@
 
                     // Add a device profile change listener to kick off animating the side tasks
                     // once we enter multiwindow mode and relayout
-                    activity.addOnDeviceProfileChangeListener(this);
+                    activity.addOnDeviceProfileChangeListener(onDeviceProfileChangeListener);
 
                     final Runnable animStartedListener = () -> {
                         // Hide the task view and wait for the window to be resized
                         // TODO: Consider animating in launcher and do an in-place start activity
                         //       afterwards
-                        mRecentsView.addIgnoreResetTask(mTaskView);
-                        mTaskView.setAlpha(0f);
-                        mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
+                        recentsView.addIgnoreResetTask(taskView);
+                        taskView.setAlpha(0f);
                     };
 
                     final int[] position = new int[2];
@@ -179,35 +198,12 @@
                 }
             });
         }
-
-        @Override
-        public boolean onPreDraw() {
-            mTaskView.getViewTreeObserver().removeOnPreDrawListener(this);
-            WindowManagerWrapper.getInstance().endProlongedAnimations();
-            return true;
-        }
-
-        @Override
-        public void onDeviceProfileChanged(DeviceProfile dp) {
-            mActivity.removeOnDeviceProfileChangeListener(this);
-            if (dp.isMultiWindowMode) {
-                mTaskView.getRootView().addOnLayoutChangeListener(this);
-            }
-        }
-
-        @Override
-        public void onLayoutChange(View v, int l, int t, int r, int b,
-                int oldL, int oldT, int oldR, int oldB) {
-            mTaskView.getRootView().removeOnLayoutChangeListener(this);
-            mRecentsView.removeIgnoreResetTask(mTaskView);
-
-            // Start animating in the side pages once launcher has been resized
-            mRecentsView.dismissTask(mTaskView, false, false);
-        }
     }
 
     public static class Pin extends TaskSystemShortcut {
 
+        private static final String TAG = Pin.class.getSimpleName();
+
         private Handler mHandler;
 
         public Pin() {
@@ -237,6 +233,8 @@
                         } catch (RemoteException e) {
                             Log.w(TAG, "Failed to start screen pinning: ", e);
                         }
+                    } else {
+                        Log.w(TAG, taskView.getLaunchTaskFailedMsg());
                     }
                 };
                 taskView.launchTask(true, resultCallback, mHandler);
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index c66f00f..559236d 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
 import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
@@ -159,7 +160,6 @@
 
             @Override
             public void onUpdate(float percent) {
-
                 final Surface surface = getSurface(v);
                 final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
                 if (frameNumber == -1) {
@@ -182,6 +182,10 @@
                 crop.set(tw.winCrop);
 
                 TransactionCompat t = new TransactionCompat();
+                if (isFirstFrame) {
+                    RemoteAnimationProvider.prepareTargetsForFirstFrame(targets, t, MODE_OPENING);
+                    isFirstFrame = false;
+                }
                 for (RemoteAnimationTargetCompat target : targets) {
                     if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
                         t.setAlpha(target.leash, mTaskAlpha.value);
@@ -196,15 +200,10 @@
                             t.deferTransactionUntil(target.leash, surface, frameNumber);
                         }
                     }
-                    if (isFirstFrame) {
-                        t.show(target.leash);
-                    }
                 }
-                t.setEarlyWakeup();
                 t.apply();
 
                 matrix.reset();
-                isFirstFrame = false;
             }
         });
         return appAnimator;
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 33b922d..25649fa 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -22,6 +22,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.systemui.shared.system.ActivityManagerWrapper
+        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
 
 import android.annotation.TargetApi;
@@ -39,13 +41,12 @@
 import android.view.Choreographer;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
-import android.view.View;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -99,8 +100,6 @@
         public void onBind(ISystemUiProxy iSystemUiProxy) {
             mISystemUiProxy = iSystemUiProxy;
             mRecentsModel.setSystemUiProxy(mISystemUiProxy);
-            RemoteRunnable.executeSafely(() -> mISystemUiProxy.setRecentsOnboardingText(
-                    getResources().getString(R.string.recents_swipe_up_onboarding)));
             mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
         }
 
@@ -176,6 +175,7 @@
         super.onCreate();
         mAM = ActivityManagerWrapper.getInstance();
         mRecentsModel = RecentsModel.getInstance(this);
+        mRecentsModel.setPreloadTasksInBackground(true);
         mMainThreadExecutor = new MainThreadExecutor();
         mOverviewCommandHelper = new OverviewCommandHelper(this);
         mMainThreadChoreographer = Choreographer.getInstance();
@@ -250,7 +250,7 @@
 
         private final ActivityControlHelper<T> mActivityHelper;
         private final T mActivity;
-        private final View mTarget;
+        private final BaseDragLayer mTarget;
         private final int[] mLocationOnScreen = new int[2];
         private final PointF mDownPos = new PointF();
         private final int mTouchSlop;
@@ -258,7 +258,6 @@
 
         private boolean mTrackingStarted = false;
         private boolean mInvalidated = false;
-        private boolean mHadWindowFocusOnDown;
 
         private float mLastProgress = 0;
         private boolean mStartPending = false;
@@ -283,8 +282,7 @@
             if (action == ACTION_DOWN) {
                 mTrackingStarted = false;
                 mDownPos.set(ev.getX(), ev.getY());
-                mHadWindowFocusOnDown = mTarget.hasWindowFocus();
-            } else if (!mTrackingStarted && mHadWindowFocusOnDown) {
+            } else if (!mTrackingStarted) {
                 switch (action) {
                     case ACTION_POINTER_UP:
                     case ACTION_POINTER_DOWN:
@@ -295,7 +293,6 @@
                     case ACTION_MOVE: {
                         float displacement = ev.getY() - mDownPos.y;
                         if (Math.abs(displacement) >= mTouchSlop) {
-                            mTrackingStarted = true;
                             mTarget.getLocationOnScreen(mLocationOnScreen);
 
                             // Send a down event only when mTouchSlop is crossed.
@@ -303,6 +300,7 @@
                             down.setAction(ACTION_DOWN);
                             sendEvent(down);
                             down.recycle();
+                            mTrackingStarted = true;
                         }
                     }
                 }
@@ -321,17 +319,32 @@
             int flags = ev.getEdgeFlags();
             ev.setEdgeFlags(flags | EDGE_NAV_BAR);
             ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
-            mTarget.dispatchTouchEvent(ev);
+            if (!mTrackingStarted) {
+                mTarget.onInterceptTouchEvent(ev);
+            }
+            mTarget.onTouchEvent(ev);
             ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
             ev.setEdgeFlags(flags);
         }
 
         @Override
+        public void onQuickStep(float eventX, float eventY, long eventTime) {
+            if (mInvalidated) {
+                return;
+            }
+            mActivityHelper.onQuickstepGestureStarted(mActivity, true);
+            ActivityManagerWrapper.getInstance()
+                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        }
+
+        @Override
         public void updateTouchTracking(int interactionType) {
             if (mInvalidated) {
                 return;
             }
             if (interactionType == INTERACTION_QUICK_SCRUB) {
+                ActivityManagerWrapper.getInstance()
+                        .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
                 mStartPending = true;
 
                 Runnable action = () -> {
@@ -344,7 +357,6 @@
                         mQuickScrubController.onQuickScrubEnd();
                         mEndPending = false;
                     }
-
                 };
 
                 mActivityHelper.executeOnWindowAvailable(mActivity, action);
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index fe9f0c3..ec50e67 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -15,28 +15,30 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.Utilities.postAsyncCallback;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
 import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.systemui.shared.recents.utilities.Utilities
-        .postAtFrontOfQueueAsynchronously;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
-import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.support.annotation.AnyThread;
 import android.support.annotation.UiThread;
 import android.support.annotation.WorkerThread;
 import android.util.Log;
@@ -47,22 +49,23 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.ActivityControlHelper.AnimationFactory;
 import com.android.quickstep.ActivityControlHelper.LayoutListener;
 import com.android.quickstep.TouchConsumer.InteractionType;
 import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.util.SysuiEventLogger;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -72,7 +75,7 @@
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowCallbacksCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.util.StringJoiner;
@@ -98,16 +101,28 @@
     private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
     private static final int STATE_GESTURE_STARTED = 1 << 8;
     private static final int STATE_GESTURE_CANCELLED = 1 << 9;
+    private static final int STATE_GESTURE_COMPLETED = 1 << 10;
 
     // States for quick switch/scrub
-    private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 10;
-    private static final int STATE_QUICK_SCRUB_START = 1 << 11;
-    private static final int STATE_QUICK_SCRUB_END = 1 << 12;
+    private static final int STATE_CURRENT_TASK_FINISHED = 1 << 11;
+    private static final int STATE_QUICK_SCRUB_START = 1 << 12;
+    private static final int STATE_QUICK_SCRUB_END = 1 << 13;
+
+    private static final int STATE_CAPTURE_SCREENSHOT = 1 << 14;
+    private static final int STATE_SCREENSHOT_CAPTURED = 1 << 15;
 
     private static final int LAUNCHER_UI_STATES =
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
             | STATE_LAUNCHER_STARTED;
 
+    private static final int LONG_SWIPE_ENTER_STATE =
+            STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_STARTED
+                    | STATE_APP_CONTROLLER_RECEIVED;
+
+    private static final int LONG_SWIPE_START_STATE =
+            STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_STARTED
+                    | STATE_APP_CONTROLLER_RECEIVED | STATE_SCREENSHOT_CAPTURED;
+
     // For debugging, keep in sync with above states
     private static final String[] STATES = new String[] {
             "STATE_LAUNCHER_PRESENT",
@@ -120,16 +135,20 @@
             "STATE_HANDLER_INVALIDATED",
             "STATE_GESTURE_STARTED",
             "STATE_GESTURE_CANCELLED",
-            "STATE_SWITCH_TO_SCREENSHOT_COMPLETE",
-            "STATE_QUICK_SWITCH",
+            "STATE_GESTURE_COMPLETED",
+            "STATE_CURRENT_TASK_FINISHED",
             "STATE_QUICK_SCRUB_START",
-            "STATE_QUICK_SCRUB_END"
+            "STATE_QUICK_SCRUB_END",
+            "STATE_CAPTURE_SCREENSHOT",
+            "STATE_SCREENSHOT_CAPTURED",
     };
 
-    private static final long MAX_SWIPE_DURATION = 200;
-    private static final long MIN_SWIPE_DURATION = 80;
+    public static final long MAX_SWIPE_DURATION = 350;
+    public static final long MIN_SWIPE_DURATION = 80;
 
     private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
+    private static final float SWIPE_DURATION_MULTIPLIER =
+            Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
 
     private final ClipAnimationHelper mClipAnimationHelper = new ClipAnimationHelper();
 
@@ -144,7 +163,7 @@
     // visible.
     private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
 
-    private final MainThreadExecutor mMainExecutor = new MainThreadExecutor();
+    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
 
     private final Context mContext;
     private final int mRunningTaskId;
@@ -158,12 +177,12 @@
     private LayoutListener mLayoutListener;
     private RecentsView mRecentsView;
     private QuickScrubController mQuickScrubController;
+    private AnimationFactory mAnimationFactory = (t) -> { };
 
     private Runnable mLauncherDrawnCallback;
 
     private boolean mWasLauncherAlreadyVisible;
 
-    private float mCurrentDisplacement;
     private boolean mGestureStarted;
     private int mLogAction = Touch.SWIPE;
     private float mCurrentQuickScrubProgress;
@@ -177,6 +196,11 @@
     private final long mTouchTimeMs;
     private long mLauncherFrameDrawnTime;
 
+    private boolean mBgLongSwipeMode = false;
+    private boolean mUiLongSwipeMode = false;
+    private float mLongSwipeDisplacement = 0;
+    private LongSwipeHelper mLongSwipeController;
+
     WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs,
             ActivityControlHelper<T> controller) {
         mContext = context;
@@ -186,10 +210,10 @@
         mActivityInitListener = mActivityControlHelper
                 .createActivityInitListener(this::onActivityInit);
 
+        initStateCallbacks();
         // Register the input consumer on the UI thread, to ensure that it runs after any pending
         // unregister calls
-        mMainExecutor.execute(mInputConsumer::registerInputConsumer);
-        initStateCallbacks();
+        executeOnUiThread(mInputConsumer::registerInputConsumer);
     }
 
     private void initStateCallbacks() {
@@ -206,22 +230,30 @@
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
                 this::launcherFrameDrawn);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
-                this::notifyGestureStarted);
+                this::notifyGestureStartedAsync);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
                         | STATE_GESTURE_CANCELLED,
                 this::resetStateForAnimationCancel);
 
+        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
+                this::sendRemoteAnimationsToAnimationFactory);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                         | STATE_SCALED_CONTROLLER_APP,
                 this::resumeLastTask);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                         | STATE_ACTIVITY_MULTIPLIER_COMPLETE
-                        | STATE_SCALED_CONTROLLER_RECENTS,
+                        | STATE_CAPTURE_SCREENSHOT,
                 this::switchToScreenshot);
+
+        mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED
+                        | STATE_SCALED_CONTROLLER_RECENTS,
+                this::finishCurrentTransitionToHome);
+
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                         | STATE_ACTIVITY_MULTIPLIER_COMPLETE
                         | STATE_SCALED_CONTROLLER_RECENTS
-                        | STATE_SWITCH_TO_SCREENSHOT_COMPLETE,
+                        | STATE_CURRENT_TASK_FINISHED
+                        | STATE_GESTURE_COMPLETED,
                 this::setupLauncherUiAfterSwipeUpAnimation);
 
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
@@ -230,21 +262,34 @@
         mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                 this::invalidateHandlerWithLauncher);
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED
+                | STATE_SCALED_CONTROLLER_APP,
+                this::notifyTransitionCancelled);
 
         mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START,
                 this::onQuickScrubStart);
         mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START
                 | STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub);
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_SWITCH_TO_SCREENSHOT_COMPLETE
+        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_CURRENT_TASK_FINISHED
                 | STATE_QUICK_SCRUB_END, this::switchToFinalAppAfterQuickScrub);
+
+        mStateCallback.addCallback(LONG_SWIPE_ENTER_STATE, this::checkLongSwipeCanEnter);
+        mStateCallback.addCallback(LONG_SWIPE_START_STATE, this::checkLongSwipeCanStart);
+    }
+
+    private void executeOnUiThread(Runnable action) {
+        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
+            action.run();
+        } else {
+            postAsyncCallback(mMainThreadHandler, action);
+        }
     }
 
     private void setStateOnUiThread(int stateFlag) {
-        Handler handler = mMainExecutor.getHandler();
-        if (Looper.myLooper() == handler.getLooper()) {
+        if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
             mStateCallback.setState(stateFlag);
         } else {
-            postAtFrontOfQueueAsynchronously(handler, () -> mStateCallback.setState(stateFlag));
+            postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
         }
     }
 
@@ -288,7 +333,11 @@
         mActivity = activity;
         // Override the visibility of the activity until the gesture actually starts and we swipe
         // up, or until we transition home and the home animation is composed
-        mActivity.setForceInvisible(true);
+        if (alreadyOnHome) {
+            mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+        } else {
+            mActivity.addForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+        }
 
         mRecentsView = activity.getOverviewPanel();
         mQuickScrubController = mRecentsView.getQuickScrubController();
@@ -307,23 +356,18 @@
         if (mActivity != activity) {
             return;
         }
-        if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return;
         }
 
-        mActivityControlHelper.prepareRecentsUI(mActivity, mWasLauncherAlreadyVisible);
+        mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
+                mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
         AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible);
 
         if (mWasLauncherAlreadyVisible) {
-            mLauncherTransitionController = mActivityControlHelper
-                    .createControllerForVisibleActivity(activity);
-            mLauncherTransitionController.dispatchOnStart();
-            mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
-
             mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
         } else {
             TraceHelper.beginSection("WTS-init");
-            // TODO: Implement a better animation for fading in
             View rootView = activity.getRootView();
             rootView.setAlpha(0);
             rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
@@ -343,6 +387,7 @@
         }
 
         mRecentsView.showTask(mRunningTaskId);
+        mRecentsView.setRunningTaskHidden(true);
         mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
         mLayoutListener.open();
         mStateCallback.setState(STATE_LAUNCHER_STARTED);
@@ -371,9 +416,13 @@
         mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
     }
 
+    private void sendRemoteAnimationsToAnimationFactory() {
+        mAnimationFactory.onRemoteAnimationReceived(mRecentsAnimationWrapper.targetSet);
+    }
+
     private void initializeLauncherAnimationController() {
         mLayoutListener.setHandler(this);
-        onLauncherLayoutChanged();
+        buildAnimationController();
 
         final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs;
         SysuiEventLogger.writeDummyRecentsTransition(transitionDelay);
@@ -394,32 +443,48 @@
         }
         mInteractionType = interactionType;
 
-        setStateOnUiThread(STATE_QUICK_SCRUB_START);
+        setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
 
         // Start the window animation without waiting for launcher.
-        animateToProgress(1f, QUICK_SCRUB_START_DURATION);
+        animateToProgress(1f, QUICK_SCRUB_START_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
     }
 
     @WorkerThread
     public void updateDisplacement(float displacement) {
-        mCurrentDisplacement = displacement;
+        // We are moving in the negative x/y direction
+        displacement = -displacement;
+        if (displacement > mTransitionDragLength) {
+            mCurrentShift.updateValue(1);
 
-        float translation = Utilities.boundToRange(-mCurrentDisplacement, 0, mTransitionDragLength);
-        float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
-        mCurrentShift.updateValue(shift);
+            if (!mBgLongSwipeMode) {
+                mBgLongSwipeMode = true;
+                executeOnUiThread(this::onLongSwipeEnabledUi);
+            }
+            mLongSwipeDisplacement = displacement - mTransitionDragLength;
+            executeOnUiThread(this::onLongSwipeDisplacementUpdated);
+        } else {
+            if (mBgLongSwipeMode) {
+                mBgLongSwipeMode = false;
+                executeOnUiThread(this::onLongSwipeDisabledUi);
+            }
+            float translation = Math.max(displacement, 0);
+            float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
+            mCurrentShift.updateValue(shift);
+        }
     }
 
     /**
      * Called by {@link #mLayoutListener} when launcher layout changes
      */
-    public void onLauncherLayoutChanged() {
+    public void buildAnimationController() {
         initTransitionEndpoints(mActivity.getDeviceProfile());
+        mAnimationFactory.createActivityController(mTransitionDragLength);
+    }
 
-        if (!mWasLauncherAlreadyVisible) {
-            mLauncherTransitionController = mActivityControlHelper
-                    .createControllerForHiddenActivity(mActivity, mTransitionDragLength);
-            mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
-        }
+    private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
+        mLauncherTransitionController = anim;
+        mLauncherTransitionController.dispatchOnStart();
+        mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
     }
 
     @WorkerThread
@@ -431,74 +496,60 @@
                 Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
                         ? ACCEL_2 : LINEAR;
                 float interpolated = interpolator.getInterpolation(shift);
-                mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targets, interpolated);
+                mClipAnimationHelper.applyTransform(
+                        mRecentsAnimationWrapper.targetSet, interpolated);
+
+                // TODO: This logic is spartanic!
+                boolean passedThreshold = shift > 0.12f;
+                mRecentsAnimationWrapper.setAnimationTargetsBehindSystemBars(!passedThreshold);
+                if (mActivityControlHelper.shouldMinimizeSplitScreen()) {
+                    mRecentsAnimationWrapper
+                            .setSplitScreenMinimizedForTransaction(passedThreshold);
+                }
             }
         }
 
-        if (mLauncherTransitionController != null) {
-            Runnable runOnUi = () -> {
-                if (mLauncherTransitionController == null) {
-                    return;
-                }
-                mLauncherTransitionController.setPlayFraction(shift);
+        executeOnUiThread(this::updateFinalShiftUi);
+    }
 
-                // Make sure the window follows the first task if it moves, e.g. during quick scrub.
-                View firstTask = mRecentsView.getPageAt(0);
-                // The first task may be null if we are swiping up from a task that does not
-                // appear in the list (ie. the assistant)
-                if (firstTask != null) {
-                    int scrollForFirstTask = mRecentsView.getScrollForPage(0);
-                    int offsetFromFirstTask = (scrollForFirstTask - mRecentsView.getScrollX());
-                    mClipAnimationHelper.offsetTarget(firstTask.getScaleX(),
-                            offsetFromFirstTask + firstTask.getTranslationX(),
-                            mRecentsView.getTranslationY());
-                }
-                if (mRecentsAnimationWrapper.controller != null) {
-                    // TODO: This logic is spartanic!
-                    boolean passedThreshold = shift > 0.12f;
-                    mRecentsAnimationWrapper.setAnimationTargetsBehindSystemBars(!passedThreshold);
-                    mRecentsAnimationWrapper.setSplitScreenMinimizedForTransaction(passedThreshold);
-                }
-            };
-            if (Looper.getMainLooper() == Looper.myLooper()) {
-                runOnUi.run();
-            } else {
-                // The fling operation completed even before the launcher was drawn
-                mMainExecutor.execute(runOnUi);
-            }
+    private void updateFinalShiftUi() {
+        if (mLauncherTransitionController == null) {
+            return;
         }
+        mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
     }
 
     public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) {
-        if (apps != null) {
-            // Use the top closing app to determine the insets for the animation
-            for (RemoteAnimationTargetCompat target : apps) {
-                if (target.mode == MODE_CLOSING) {
-                    DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
-                    final Rect homeStackBounds;
+            RemoteAnimationTargetSet targets, Rect homeContentInsets, Rect minimizedHomeBounds) {
+        LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+        InvariantDeviceProfile idp = appState == null ?
+                new InvariantDeviceProfile(mContext) : appState.getInvariantDeviceProfile();
+        DeviceProfile dp = idp.getDeviceProfile(mContext);
+        final Rect overviewStackBounds;
+        RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mRunningTaskId);
 
-                    if (minimizedHomeBounds != null) {
-                        homeStackBounds = minimizedHomeBounds;
-                        dp = dp.getMultiWindowProfile(mContext,
-                                new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
-                        dp.updateInsets(homeContentInsets);
-                    } else {
-                        homeStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
-                        // TODO: Workaround for an existing issue where the home content insets are
-                        // not valid immediately after rotation, just use the stable insets for now
-                        Rect insets = new Rect();
-                        WindowManagerWrapper.getInstance().getStableInsets(insets);
-                        dp = dp.copy(mContext);
-                        dp.updateInsets(insets);
-                    }
-
-                    mClipAnimationHelper.updateSource(homeStackBounds, target);
-                    initTransitionEndpoints(dp);
-                }
-            }
+        if (minimizedHomeBounds != null && runningTaskTarget != null) {
+            overviewStackBounds = mActivityControlHelper
+                    .getOverviewWindowBounds(minimizedHomeBounds, runningTaskTarget);
+            dp = dp.getMultiWindowProfile(mContext,
+                    new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
+            dp.updateInsets(homeContentInsets);
+        } else {
+            overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
+            // If we are not in multi-window mode, home insets should be same as system insets.
+            Rect insets = new Rect();
+            WindowManagerWrapper.getInstance().getStableInsets(insets);
+            dp = dp.copy(mContext);
+            dp.updateInsets(insets);
         }
-        mRecentsAnimationWrapper.setController(controller, apps);
+
+        if (runningTaskTarget != null) {
+            mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
+        }
+        mClipAnimationHelper.prepareAnimation(false /* isOpening */);
+        initTransitionEndpoints(dp);
+
+        mRecentsAnimationWrapper.setController(controller, targets);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
     }
 
@@ -509,9 +560,10 @@
     }
 
     public void onGestureStarted() {
-        notifyGestureStarted();
+        notifyGestureStartedAsync();
         setStateOnUiThread(STATE_GESTURE_STARTED);
         mGestureStarted = true;
+        mRecentsAnimationWrapper.hideCurrentInputMethod();
         mRecentsAnimationWrapper.enableInputConsumer();
         ActivityManagerWrapper.getInstance().closeSystemWindows(
                 CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
@@ -521,12 +573,13 @@
      * Notifies the launcher that the swipe gesture has started. This can be called multiple times
      * on both background and UI threads
      */
-    private void notifyGestureStarted() {
+    @AnyThread
+    private void notifyGestureStartedAsync() {
         final T curActivity = mActivity;
         if (curActivity != null) {
             // Once the gesture starts, we can no longer transition home through the button, so
             // reset the force override of the activity visibility
-            mActivity.setForceInvisible(false);
+            mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
             mActivityControlHelper.onQuickstepGestureStarted(
                     curActivity, mWasLauncherAlreadyVisible);
         }
@@ -534,30 +587,44 @@
 
     @WorkerThread
     public void onGestureEnded(float endVelocity) {
-        Resources res = mContext.getResources();
-        float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity);
-        boolean isFling = Math.abs(endVelocity) > flingThreshold;
+        float flingThreshold = mContext.getResources()
+                .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+        boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
+        setStateOnUiThread(STATE_GESTURE_COMPLETED);
 
+        mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
+
+        if (mBgLongSwipeMode) {
+            executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling));
+        } else {
+            handleNormalGestureEnd(endVelocity, isFling);
+        }
+    }
+
+    private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
         long duration = MAX_SWIPE_DURATION;
         final float endShift;
         if (!isFling) {
-            endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
-            mLogAction = Touch.SWIPE;
+            endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted ? 1 : 0;
+            long expectedDuration = Math.abs(Math.round((endShift - mCurrentShift.value)
+                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
+            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
         } else {
             endShift = endVelocity < 0 ? 1 : 0;
-            float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity);
+            float minFlingVelocity = mContext.getResources()
+                    .getDimension(R.dimen.quickstep_fling_min_velocity);
             if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
                 float distanceToTravel = (endShift - mCurrentShift.value) * mTransitionDragLength;
 
                 // we want the page's snap velocity to approximately match the velocity at
                 // which the user flings, so we scale the duration by a value near to the
-                // derivative of the scroll interpolator at zero, ie. 5.
-                duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
+                // derivative of the scroll interpolator at zero, ie. 2.
+                long baseDuration = Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
+                duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
             }
-            mLogAction = Touch.FLING;
         }
 
-        animateToProgress(endShift, duration);
+        animateToProgress(endShift, duration, DEACCEL);
     }
 
     private void doLogGesture(boolean toLauncher) {
@@ -577,15 +644,16 @@
     }
 
     /** Animates to the given progress, where 0 is the current app and 1 is overview. */
-    private void animateToProgress(float progress, long duration) {
+    private void animateToProgress(float progress, long duration, Interpolator interpolator) {
         mIsGoingToHome = Float.compare(progress, 1) == 0;
         ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
-        anim.setInterpolator(Interpolators.SCROLL);
+        anim.setInterpolator(interpolator);
         anim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
-                setStateOnUiThread(mIsGoingToHome ?
-                        STATE_SCALED_CONTROLLER_RECENTS : STATE_SCALED_CONTROLLER_APP);
+                setStateOnUiThread(mIsGoingToHome
+                        ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT)
+                        : STATE_SCALED_CONTROLLER_APP);
             }
         });
         anim.start();
@@ -620,9 +688,14 @@
         mLauncherTransitionController = null;
         mLayoutListener.finish();
 
+        mRecentsView.setRunningTaskHidden(false);
         mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
     }
 
+    private void notifyTransitionCancelled() {
+        mAnimationFactory.onTransitionCancelled();
+    }
+
     private void resetStateForAnimationCancel() {
         boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
         mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
@@ -636,40 +709,38 @@
 
     private void switchToScreenshot() {
         boolean finishTransitionPosted = false;
-        final Runnable finishTransitionRunnable = () -> {
-            synchronized (mRecentsAnimationWrapper) {
-                mRecentsAnimationWrapper.finish(true /* toHome */,
-                        () -> setStateOnUiThread(STATE_SWITCH_TO_SCREENSHOT_COMPLETE));
-            }
-        };
-
         synchronized (mRecentsAnimationWrapper) {
             if (mRecentsAnimationWrapper.controller != null) {
-                TransactionCompat transaction = new TransactionCompat();
-                for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
-                    if (app.mode == MODE_CLOSING) {
-                        // Update the screenshot of the task
-                        ThumbnailData thumbnail =
-                                mRecentsAnimationWrapper.controller.screenshotTask(app.taskId);
-                        TaskView taskView = mRecentsView.updateThumbnail(app.taskId, thumbnail);
-                        if (taskView != null) {
-                            taskView.setAlpha(1);
-                            // Defer finishing the animation until the next launcher frame with the
-                            // new thumbnail
-                            mActivityControlHelper.executeOnNextDraw(mActivity, taskView,
-                                    finishTransitionRunnable);
-                            finishTransitionPosted = true;
+                // Update the screenshot of the task
+                ThumbnailData thumbnail =
+                        mRecentsAnimationWrapper.controller.screenshotTask(mRunningTaskId);
+                final TaskView taskView = mRecentsView.updateThumbnail(mRunningTaskId, thumbnail);
+                mRecentsView.setRunningTaskHidden(false);
+                if (taskView != null) {
+                    // Defer finishing the animation until the next launcher frame with the
+                    // new thumbnail
+                    finishTransitionPosted = new WindowCallbacksCompat(taskView) {
+
+                        @Override
+                        public void onPostDraw(Canvas canvas) {
+                            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+                            detach();
                         }
-                    }
+                    }.attach();
                 }
-                transaction.apply();
             }
         }
         if (!finishTransitionPosted) {
-            // If we haven't posted the transition end runnable, run it now
-            finishTransitionRunnable.run();
+            // If we haven't posted a draw callback, set the state immediately.
+            setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
         }
-        doLogGesture(true /* toLauncher */);
+    }
+
+    private void finishCurrentTransitionToHome() {
+        synchronized (mRecentsAnimationWrapper) {
+            mRecentsAnimationWrapper.finish(true /* toHome */,
+                    () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+        }
     }
 
     private void setupLauncherUiAfterSwipeUpAnimation() {
@@ -681,18 +752,40 @@
 
         // Animate the first icon.
         mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
-
         mRecentsView.setSwipeDownShouldLaunchApp(true);
 
+        RecentsModel.getInstance(mContext).onOverviewShown(false, TAG);
+
+        doLogGesture(true /* toLauncher */);
         reset();
     }
 
     private void onQuickScrubStart() {
-        mActivityControlHelper.onQuickInteractionStart(mActivity, mWasLauncherAlreadyVisible);
+        if (mLauncherTransitionController != null) {
+            mLauncherTransitionController.getAnimationPlayer().end();
+            mLauncherTransitionController = null;
+        }
+
+        mActivityControlHelper.onQuickInteractionStart(mActivity, false);
         mQuickScrubController.onQuickScrubStart(false);
 
         // Inform the last progress in case we skipped before.
         mQuickScrubController.onQuickScrubProgress(mCurrentQuickScrubProgress);
+
+        // Make sure the window follows the first task if it moves, e.g. during quick scrub.
+        TaskView firstTask = mRecentsView.getPageAt(0);
+        // The first task may be null if we are swiping up from a task that does not
+        // appear in the list (i.e. the assistant)
+        if (firstTask != null) {
+            int scrollForFirstTask = mRecentsView.getScrollForPage(0);
+            int scrollForSecondTask = mRecentsView.getChildCount() > 1
+                    ? mRecentsView.getScrollForPage(1) : scrollForFirstTask;
+            int offsetFromFirstTask = scrollForFirstTask - scrollForSecondTask;
+            float interpolation = offsetFromFirstTask / (mRecentsView.getWidth() / 2);
+            mClipAnimationHelper.offsetTarget(
+                    firstTask.getCurveScaleForInterpolation(interpolation), offsetFromFirstTask,
+                    mActivityControlHelper.getTranslationYForQuickScrub(mActivity));
+        }
     }
 
     private void onFinishedTransitionToQuickScrub() {
@@ -743,4 +836,64 @@
     public void setGestureEndCallback(Runnable gestureEndCallback) {
         mGestureEndCallback = gestureEndCallback;
     }
+
+    // Handling long swipe
+    private void onLongSwipeEnabledUi() {
+        mUiLongSwipeMode = true;
+        checkLongSwipeCanEnter();
+        checkLongSwipeCanStart();
+    }
+
+    private void onLongSwipeDisabledUi() {
+        mUiLongSwipeMode = false;
+
+        if (mLongSwipeController != null) {
+            mLongSwipeController.destroy();
+
+            // Rebuild animations
+            buildAnimationController();
+        }
+    }
+
+    private void onLongSwipeDisplacementUpdated() {
+        if (!mUiLongSwipeMode || mLongSwipeController == null) {
+            return;
+        }
+
+        mLongSwipeController.onMove(mLongSwipeDisplacement);
+    }
+
+    private void checkLongSwipeCanEnter() {
+        if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
+                || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
+            return;
+        }
+
+        // We are entering long swipe mode, make sure the screen shot is captured.
+        mStateCallback.setState(STATE_CAPTURE_SCREENSHOT);
+
+    }
+
+    private void checkLongSwipeCanStart() {
+        if (!mUiLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
+                || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
+            return;
+        }
+
+        mLongSwipeController = mActivityControlHelper.getLongSwipeController(
+                mActivity, mRecentsAnimationWrapper.targetSet);
+        onLongSwipeDisplacementUpdated();
+    }
+
+    private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
+        if (!mUiLongSwipeMode || mLongSwipeController == null) {
+            handleNormalGestureEnd(velocity, isFling);
+            return;
+        }
+
+        finishCurrentTransitionToHome();
+        mLongSwipeController.end(velocity, isFling,
+                () -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
+
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 89422af..fb4aa02 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -18,7 +18,6 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.support.annotation.AnyThread;
 import android.util.AttributeSet;
 import android.view.View;
 
@@ -36,7 +35,6 @@
     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setOverviewStateEnabled(true);
-        updateEmptyMessage();
     }
 
     @Override
@@ -64,11 +62,12 @@
 
     @Override
     protected void getTaskSize(DeviceProfile dp, Rect outRect) {
-        LayoutUtils.calculateTaskSize(getContext(), dp, 0, outRect);
+        LayoutUtils.calculateFallbackTaskSize(getContext(), dp, outRect);
     }
 
-    @AnyThread
-    public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
-        LayoutUtils.calculateTaskSize(context, grid, 0, outRect);
+    @Override
+    public boolean shouldUseMultiWindowTaskSizeStrategy() {
+        // Just use the activity task size for multi-window as well.
+        return false;
     }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
index 1dc6fcf..878a593 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -17,6 +17,7 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 
@@ -31,6 +32,8 @@
 
     private final RecentsActivity mActivity;
 
+    private final Point mLastKnownSize = new Point(10, 10);
+
     public RecentsRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mActivity = (RecentsActivity) BaseActivity.fromContext(context);
@@ -39,10 +42,27 @@
                 | SYSTEM_UI_FLAG_LAYOUT_STABLE);
     }
 
+    public Point getLastKnownSize() {
+        return mLastKnownSize;
+    }
+
     public void setup() {
         mControllers = new TouchController[] { new RecentsTaskController(mActivity) };
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Check size changes before the actual measure, to avoid multiple measure calls.
+        int width = MeasureSpec.getSize(widthMeasureSpec);
+        int height = MeasureSpec.getSize(heightMeasureSpec);
+        if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
+            mLastKnownSize.set(width, height);
+            mActivity.onRootViewSizeChanged();
+        }
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
     @TargetApi(23)
     @Override
     protected boolean fitSystemWindows(Rect insets) {
@@ -64,6 +84,7 @@
     }
 
     public void dispatchInsets() {
+        mActivity.getDeviceProfile().updateInsets(mInsets);
         super.setInsets(mInsets);
     }
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 493e9e2..8b9903d 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -15,17 +15,32 @@
  */
 package com.android.quickstep.util;
 
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.SCROLL;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
+import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Matrix.ScaleToFit;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.RemoteException;
 
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.utilities.RectFEvaluator;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
 
 /**
  * Utility class to handle window clip animation
@@ -40,8 +55,8 @@
     private final RectF mSourceRect = new RectF();
     // The bounds of the task view in launcher window coordinates
     private final RectF mTargetRect = new RectF();
-    // Doesn't change after initialized, used as an anchor when changing mTargetRect
-    private final RectF mInitialTargetRect = new RectF();
+    // Set when the final window destination is changed, such as offsetting for quick scrub
+    private final PointF mTargetOffset = new PointF();
     // The insets to be used for clipping the app window, which can be larger than mSourceInsets
     // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
     // app window coordinates.
@@ -54,11 +69,18 @@
     private final Rect mClipRect = new Rect();
     private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
     private final Matrix mTmpMatrix = new Matrix();
+    private final RectF mTmpRectF = new RectF();
 
+    private float mTargetScale = 1f;
+
+    // Whether to boost the opening animation target layers, or the closing
+    private int mBoostModeTargetLayers = -1;
+    // Wether or not applyTransform has been called yet since prepareAnimation()
+    private boolean mIsFirstFrame = true;
 
     public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
         mHomeStackBounds.set(homeStackBounds);
-        mSourceInsets.set(target.getContentInsets());
+        mSourceInsets.set(target.contentInsets);
         mSourceStackBounds.set(target.sourceContainerBounds);
 
         // TODO: Should sourceContainerBounds already have this offset?
@@ -73,8 +95,6 @@
         mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
                 mHomeStackBounds.top - mSourceStackBounds.top);
 
-        mInitialTargetRect.set(mTargetRect);
-
         // Calculate the clip based on the target rect (since the content insets and the
         // launcher insets may differ, so the aspect ratio of the target rect can differ
         // from the source rect. The difference between the target rect (scaled to the
@@ -91,12 +111,21 @@
         mSourceRect.set(scaledTargetRect);
     }
 
-    public void applyTransform(RemoteAnimationTargetCompat[] targets, float progress) {
+    public void prepareAnimation(boolean isOpening) {
+        mIsFirstFrame = true;
+        mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
+    }
+
+    public void applyTransform(RemoteAnimationTargetSet targetSet, float progress) {
         RectF currentRect;
-        synchronized (mTargetRect) {
-            currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
+        mTmpRectF.set(mTargetRect);
+        Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
+        currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
+
+        synchronized (mTargetOffset) {
             // Stay lined up with the center of the target, since it moves for quick scrub.
-            currentRect.offset(mTargetRect.centerX() - currentRect.centerX(), 0);
+            currentRect.offset(mTargetOffset.x * SCROLL.getInterpolation(progress),
+                    mTargetOffset.y  * LINEAR.getInterpolation(progress));
         }
 
         mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
@@ -106,30 +135,113 @@
         mClipRect.bottom = (int)
                 (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress));
 
-        mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
-
         TransactionCompat transaction = new TransactionCompat();
-        for (RemoteAnimationTargetCompat app : targets) {
-            if (app.mode == MODE_CLOSING) {
+        if (mIsFirstFrame) {
+            RemoteAnimationProvider.prepareTargetsForFirstFrame(targetSet.unfilteredApps,
+                    transaction, mBoostModeTargetLayers);
+            mIsFirstFrame = false;
+        }
+        for (RemoteAnimationTargetCompat app : targetSet.apps) {
+            if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
                 mTmpMatrix.postTranslate(app.position.x, app.position.y);
                 transaction.setMatrix(app.leash, mTmpMatrix)
                         .setWindowCrop(app.leash, mClipRect);
-                if (app.isNotInRecents) {
-                    transaction.setAlpha(app.leash, 1 - progress);
-                }
+            }
 
-                transaction.show(app.leash);
+            if (app.isNotInRecents
+                    || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                transaction.setAlpha(app.leash, 1 - progress);
             }
         }
-        transaction.setEarlyWakeup();
         transaction.apply();
     }
 
     public void offsetTarget(float scale, float offsetX, float offsetY) {
-        synchronized (mTargetRect) {
-            mTargetRect.set(mInitialTargetRect);
-            Utilities.scaleRectFAboutCenter(mTargetRect, scale);
-            mTargetRect.offset(offsetX, offsetY);
+        synchronized (mTargetOffset) {
+            mTargetScale = scale;
+            mTargetOffset.set(offsetX, offsetY);
         }
     }
+
+    public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
+        BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
+        BaseDragLayer dl = activity.getDragLayer();
+
+        int[] pos = new int[2];
+        dl.getLocationOnScreen(pos);
+        mHomeStackBounds.set(0, 0, dl.getWidth(), dl.getHeight());
+        mHomeStackBounds.offset(pos[0], pos[1]);
+
+        if (rv.shouldUseMultiWindowTaskSizeStrategy()) {
+            updateStackBoundsToMultiWindowTaskSize(activity);
+        } else {
+            mSourceStackBounds.set(mHomeStackBounds);
+            mSourceInsets.set(activity.getDeviceProfile().getInsets());
+        }
+
+        Rect targetRect = new Rect();
+        dl.getDescendantRectRelativeToSelf(ttv, targetRect);
+        updateTargetRect(targetRect);
+
+        // Transform the clip relative to the target rect.
+        float scale = mTargetRect.width() / mSourceRect.width();
+        mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale;
+        mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale;
+        mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale;
+        mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale;
+    }
+
+    private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) {
+        ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
+        if (sysUiProxy != null) {
+            try {
+                mSourceStackBounds.set(sysUiProxy.getNonMinimizedSplitScreenSecondaryBounds());
+                return;
+            } catch (RemoteException e) {
+                // Use half screen size
+            }
+        }
+
+        // Assume that the task size is half screen size (minus the insets and the divider size)
+        DeviceProfile fullDp = activity.getDeviceProfile().getFullScreenProfile();
+        // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+        // account for system insets
+        int taskWidth = fullDp.availableWidthPx;
+        int taskHeight = fullDp.availableHeightPx;
+        int halfDividerSize = activity.getResources()
+                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+
+        Rect insets = new Rect();
+        WindowManagerWrapper.getInstance().getStableInsets(insets);
+        if (fullDp.isLandscape) {
+            taskWidth = taskWidth / 2 - halfDividerSize;
+        } else {
+            taskHeight = taskHeight / 2 - halfDividerSize;
+        }
+
+        mSourceStackBounds.set(0, 0, taskWidth, taskHeight);
+        // Align the task to bottom right (probably not true for seascape).
+        mSourceStackBounds.offset(insets.left + fullDp.availableWidthPx - taskWidth,
+                insets.top + fullDp.availableHeightPx - taskHeight);
+    }
+
+
+    public void drawForProgress(TaskThumbnailView ttv, Canvas canvas, float progress) {
+        RectF currentRect =  mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
+        canvas.translate(mSourceStackBounds.left - mHomeStackBounds.left,
+                mSourceStackBounds.top - mHomeStackBounds.top);
+        mTmpMatrix.setRectToRect(mTargetRect, currentRect, ScaleToFit.FILL);
+
+        canvas.concat(mTmpMatrix);
+        canvas.translate(mTargetRect.left, mTargetRect.top);
+
+        float insetProgress = (1 - progress);
+        ttv.drawOnCanvas(canvas,
+                -mSourceWindowClipInsets.left * insetProgress,
+                -mSourceWindowClipInsets.top * insetProgress,
+                ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
+                ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
+                ttv.getCornerRadius() * progress);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index f29f9e4..aeaebea 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -15,39 +15,63 @@
  */
 package com.android.quickstep.util;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.graphics.RectF;
+import android.support.annotation.AnyThread;
+import android.support.annotation.IntDef;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 
+import java.lang.annotation.Retention;
+
 public class LayoutUtils {
 
+    private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1;
+    private static final int MULTI_WINDOW_STRATEGY_DEVICE_PROFILE = 2;
+
+    @Retention(SOURCE)
+    @IntDef({MULTI_WINDOW_STRATEGY_HALF_SCREEN, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE})
+    private @interface MultiWindowStrategy {}
+
     public static void calculateLauncherTaskSize(Context context, DeviceProfile dp, Rect outRect) {
         float extraSpace = dp.isVerticalBarLayout() ? 0 : dp.hotseatBarSizePx;
-        calculateTaskSize(context, dp, extraSpace, outRect);
+        calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect);
     }
 
+    public static void calculateFallbackTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+        calculateTaskSize(context, dp, 0, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE, outRect);
+    }
+
+    @AnyThread
     public static void calculateTaskSize(Context context, DeviceProfile dp,
-            float extraVerticalSpace, Rect outRect) {
+            float extraVerticalSpace, @MultiWindowStrategy int multiWindowStrategy, Rect outRect) {
         float taskWidth, taskHeight, paddingHorz;
         Resources res = context.getResources();
         Rect insets = dp.getInsets();
 
         if (dp.isMultiWindowMode) {
-            DeviceProfile fullDp = dp.getFullScreenProfile();
-            // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
-            // account for system insets
-            taskWidth = fullDp.availableWidthPx;
-            taskHeight = fullDp.availableHeightPx;
-            float halfDividerSize = res.getDimension(R.dimen.multi_window_task_divider_size) / 2;
+            if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
+                DeviceProfile fullDp = dp.getFullScreenProfile();
+                // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+                // account for system insets
+                taskWidth = fullDp.availableWidthPx;
+                taskHeight = fullDp.availableHeightPx;
+                float halfDividerSize = res.getDimension(R.dimen.multi_window_task_divider_size)
+                        / 2;
 
-            if (fullDp.isLandscape) {
-                taskWidth = taskWidth / 2 - halfDividerSize;
+                if (fullDp.isLandscape) {
+                    taskWidth = taskWidth / 2 - halfDividerSize;
+                } else {
+                    taskHeight = taskHeight / 2 - halfDividerSize;
+                }
             } else {
-                taskHeight = taskHeight / 2 - halfDividerSize;
+                // multiWindowStrategy == MULTI_WINDOW_STRATEGY_DEVICE_PROFILE
+                taskWidth = dp.widthPx;
+                taskHeight = dp.heightPx;
             }
             paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
         } else {
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 2ffcae3..7fc3efb 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -31,25 +31,35 @@
     AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
 
     default ActivityOptions toActivityOptions(Handler handler, long duration) {
-        LauncherAnimationRunner runner = new LauncherAnimationRunner(handler) {
+        LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
+                false /* startAtFrontOfQueue */) {
+
             @Override
-            public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
-                return createWindowAnimation(targetCompats);
+            public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+                    AnimationResult result) {
+                result.setAnimation(createWindowAnimation(targetCompats));
             }
         };
         return ActivityOptionsCompat.makeRemoteAnimation(
                 new RemoteAnimationAdapterCompat(runner, duration, 0));
     }
 
-    static void showOpeningTarget(RemoteAnimationTargetCompat[] targetCompats) {
-        TransactionCompat t = new TransactionCompat();
-        for (RemoteAnimationTargetCompat target : targetCompats) {
-            int layer = target.mode == RemoteAnimationTargetCompat.MODE_CLOSING
+    /**
+     * Prepares the given {@param targets} for a remote animation, and should be called with the
+     * transaction from the first frame of animation.
+     *
+     * @param boostModeTargets The mode indicating which targets to boost in z-order above other
+     *                         targets.
+     */
+    static void prepareTargetsForFirstFrame(RemoteAnimationTargetCompat[] targets,
+            TransactionCompat t, int boostModeTargets) {
+        for (RemoteAnimationTargetCompat target : targets) {
+            int layer = target.mode == boostModeTargets
                     ? Integer.MAX_VALUE
                     : target.prefixOrderIndex;
             t.setLayer(target.leash, layer);
             t.show(target.leash);
         }
-        t.apply();
+        t.setEarlyWakeup();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
new file mode 100644
index 0000000..04b8be5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+package com.android.quickstep.util;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.ArrayList;
+
+/**
+ * Holds a collection of RemoteAnimationTargets, filtered by different properties.
+ */
+public class RemoteAnimationTargetSet {
+
+    public final RemoteAnimationTargetCompat[] unfilteredApps;
+    public final RemoteAnimationTargetCompat[] apps;
+
+    public RemoteAnimationTargetSet(RemoteAnimationTargetCompat[] apps, int targetMode) {
+        ArrayList<RemoteAnimationTargetCompat> filteredApps = new ArrayList<>();
+        if (apps != null) {
+            for (RemoteAnimationTargetCompat target : apps) {
+                if (target.mode == targetMode) {
+                    filteredApps.add(target);
+                }
+            }
+        }
+
+        this.unfilteredApps = apps;
+        this.apps = filteredApps.toArray(new RemoteAnimationTargetCompat[filteredApps.size()]);
+    }
+
+    public RemoteAnimationTargetCompat findTask(int taskId) {
+        for (RemoteAnimationTargetCompat target : apps) {
+            if (target.taskId == taskId) {
+                return target;
+            }
+        }
+        return null;
+    }
+
+    public boolean isAnimatingHome() {
+        for (RemoteAnimationTargetCompat target : apps) {
+            if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
new file mode 100644
index 0000000..e1910a6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+package com.android.quickstep.util;
+
+import android.animation.TimeInterpolator;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.util.FloatProperty;
+import android.widget.ImageView;
+
+import com.android.launcher3.Utilities;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+
+public class TaskViewDrawable extends Drawable {
+
+    public static FloatProperty<TaskViewDrawable> PROGRESS =
+            new FloatProperty<TaskViewDrawable>("progress") {
+                @Override
+                public void setValue(TaskViewDrawable taskViewDrawable, float v) {
+                    taskViewDrawable.setProgress(v);
+                }
+
+                @Override
+                public Float get(TaskViewDrawable taskViewDrawable) {
+                    return taskViewDrawable.mProgress;
+                }
+            };
+
+    private static final TimeInterpolator ICON_SIZE_INTERPOLATOR =
+            (t) -> (Math.max(t, 0.3f) - 0.3f) / 0.7f;
+
+    private final RecentsView mParent;
+    private final ImageView mIconView;
+    private final int[] mIconPos;
+
+    private final TaskThumbnailView mThumbnailView;
+
+    private final ClipAnimationHelper mClipAnimationHelper;
+
+    private float mProgress = 1;
+
+    public TaskViewDrawable(TaskView tv, RecentsView parent) {
+        mParent = parent;
+        mIconView = tv.getIconView();
+        mIconPos = new int[2];
+        Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
+
+        mThumbnailView = tv.getThumbnail();
+        mClipAnimationHelper = new ClipAnimationHelper();
+        mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView, parent);
+    }
+
+    public void setProgress(float progress) {
+        mProgress = progress;
+        mParent.invalidate();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.save();
+        canvas.translate(mParent.getScrollX(), mParent.getScrollY());
+        mClipAnimationHelper.drawForProgress(mThumbnailView, canvas, mProgress);
+        canvas.restore();
+
+        canvas.save();
+        canvas.translate(mIconPos[0], mIconPos[1]);
+        float scale = ICON_SIZE_INTERPOLATOR.getInterpolation(mProgress);
+        canvas.scale(scale, scale, mIconView.getWidth() / 2, mIconView.getHeight() / 2);
+        mIconView.draw(canvas);
+        canvas.restore();
+    }
+
+    @Override
+    public void setAlpha(int i) { }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) { }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
index ac34d90..c149de5 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
@@ -54,7 +54,7 @@
     @Override
     public void setInsets(Rect insets) {
         if (mHandler != null) {
-            mHandler.onLauncherLayoutChanged();
+            mHandler.buildAnimationController();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 4b4af3f..90749eb 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -27,7 +27,6 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.os.Build;
-import android.support.annotation.AnyThread;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.View;
@@ -36,6 +35,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.util.LayoutUtils;
 
 /**
@@ -87,7 +87,11 @@
 
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
-        setTranslationY(mTranslationYFactor * (getPaddingBottom() - getPaddingTop()));
+        setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
+    }
+
+    public float computeTranslationYForFactor(float translationYFactor) {
+        return translationYFactor * (getPaddingBottom() - getPaddingTop());
     }
 
     @Override
@@ -115,6 +119,12 @@
     public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
         AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
 
+        if (!OverviewInteractionState.getInstance(mActivity).isSwipeUpGestureEnabled()) {
+            // Hotseat doesn't move when opening recents with the button,
+            // so don't animate it here either.
+            return anim;
+        }
+
         float allAppsProgressOffscreen = ALL_APPS_PROGRESS_OFF_SCREEN;
         LauncherState state = mActivity.getStateManager().getState();
         if ((state.getVisibleElements(mActivity) & ALL_APPS_HEADER_EXTRA) != 0) {
@@ -132,8 +142,16 @@
         LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
     }
 
-    @AnyThread
-    public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
-        LayoutUtils.calculateLauncherTaskSize(context, grid, outRect);
+    @Override
+    protected void onTaskLaunched(boolean success) {
+        if (success) {
+            mActivity.getStateManager().goToState(NORMAL, false /* animate */);
+        }
+        super.onTaskLaunched(success);
+    }
+
+    @Override
+    public boolean shouldUseMultiWindowTaskSizeStrategy() {
+        return mActivity.isInMultiWindowModeCompat();
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 74e5959..51b787b 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -30,8 +30,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -43,6 +41,7 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -66,10 +65,10 @@
 import com.android.launcher3.util.Themes;
 import com.android.quickstep.OverviewCallbacks;
 import com.android.quickstep.QuickScrubController;
-import com.android.quickstep.RecentsAnimationInterpolator;
 import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.TaskViewDrawable;
 import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
 import com.android.systemui.shared.recents.model.RecentsTaskLoader;
 import com.android.systemui.shared.recents.model.Task;
@@ -79,29 +78,18 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 /**
  * A list of recent tasks.
  */
 @TargetApi(Build.VERSION_CODES.P)
-public abstract class RecentsView<T extends BaseActivity>
-        extends PagedView implements OnSharedPreferenceChangeListener, Insettable {
+public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable {
+
+    private static final String TAG = RecentsView.class.getSimpleName();
 
     private final Rect mTempRect = new Rect();
 
-    public static final FloatProperty<RecentsView> CONTENT_ALPHA =
-            new FloatProperty<RecentsView>("contentAlpha") {
-        @Override
-        public void setValue(RecentsView recentsView, float v) {
-            recentsView.setContentAlpha(v);
-        }
-
-        @Override
-        public Float get(RecentsView recentsView) {
-            return recentsView.mContentAlpha;
-        }
-    };
-
     public static final FloatProperty<RecentsView> ADJACENT_SCALE =
             new FloatProperty<RecentsView>("adjacentScale") {
         @Override
@@ -114,9 +102,11 @@
             return recentsView.mAdjacentScale;
         }
     };
-    private static final String PREF_FLIP_RECENTS = "pref_flip_recents";
+    public static final boolean FLIP_RECENTS = true;
     private static final int DISMISS_TASK_DURATION = 300;
 
+    private static final float[] sTempFloatArray = new float[3];
+
     protected final T mActivity;
     private final QuickScrubController mQuickScrubController;
     private final float mFastFlingVelocity;
@@ -160,6 +150,7 @@
 
     // Only valid until the launcher state changes to NORMAL
     private int mRunningTaskId = -1;
+    private boolean mRunningTaskTileHidden;
     private Task mTmpRunningTask;
 
     private boolean mFirstTaskIconScaledDown = false;
@@ -179,6 +170,8 @@
     // Keeps track of task views whose visual state should not be reset
     private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
 
+    private View mClearAllButton;
+
     // Variables for empty state
     private final Drawable mEmptyIcon;
     private final CharSequence mEmptyMessage;
@@ -188,6 +181,14 @@
     private boolean mShowEmptyMessage;
     private Layout mEmptyTextLayout;
 
+    private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
+            (inMultiWindowMode) -> {
+        if (!inMultiWindowMode && mOverviewStateEnabled) {
+            // TODO: Re-enable layout transitions for addition of the unpinned task
+            reloadIfNeeded();
+        }
+    };
+
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
@@ -200,7 +201,11 @@
         mQuickScrubController = new QuickScrubController(mActivity, this);
         mModel = RecentsModel.getInstance(context);
 
-        onSharedPreferenceChanged(Utilities.getPrefs(context), PREF_FLIP_RECENTS);
+        mIsRtl = Utilities.isRtl(getResources());
+        if (FLIP_RECENTS) {
+            mIsRtl = !mIsRtl;
+        }
+        setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
         mEmptyIcon.setCallback(this);
@@ -212,17 +217,7 @@
         mEmptyMessagePadding = getResources()
                 .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
         setWillNotDraw(false);
-    }
-
-    @Override
-    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
-        if (s.equals(PREF_FLIP_RECENTS)) {
-            mIsRtl = Utilities.isRtl(getResources());
-            if (sharedPreferences.getBoolean(PREF_FLIP_RECENTS, false)) {
-                mIsRtl = !mIsRtl;
-            }
-            setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
-        }
+        updateEmptyMessage();
     }
 
     public boolean isRtl() {
@@ -230,14 +225,11 @@
     }
 
     public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
-        for (int i = 0; i < getChildCount(); i++) {
-            final TaskView taskView = (TaskView) getChildAt(i);
-            if (taskView.getTask().key.id == taskId) {
-                taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
-                return taskView;
-            }
+        TaskView taskView = getTaskView(taskId);
+        if (taskView != null) {
+            taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
         }
-        return null;
+        return taskView;
     }
 
     @Override
@@ -250,14 +242,14 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         updateTaskStackListenerState();
-        Utilities.getPrefs(getContext()).registerOnSharedPreferenceChangeListener(this);
+        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         updateTaskStackListenerState();
-        Utilities.getPrefs(getContext()).unregisterOnSharedPreferenceChangeListener(this);
+        mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
     }
 
     @Override
@@ -310,11 +302,86 @@
         }
     }
 
+    private float calculateClearAllButtonAlpha() {
+        final int childCount = getChildCount();
+        if (mClearAllButton.getVisibility() != View.VISIBLE || childCount == 0) return 0;
+
+        // Current visible coordinate of the end of the oldest task.
+        final View lastChild = getChildAt(childCount - 1);
+        final int carouselCurrentEnd =
+                (mIsRtl ? lastChild.getLeft() : lastChild.getRight()) - getScrollX();
+
+        // As the end (let's call it E aka carouselCurrentEnd) of the carousel moves over Clear
+        // all button, the button changes trasparency.
+        // fullAlphaX and zeroAlphaX are the points of the 100% and 0% alpha correspondingly.
+        // Alpha changes linearly between 100% and 0% as E moves through this range. It doesn't
+        // change outside of the range.
+
+        // Once E hits the border of the Clear-All button that looks towards the most recent
+        // task, the whole button is uncovered, and it should have alpha 100%.
+        final float fullAlphaX = mIsRtl ?
+                mClearAllButton.getX() + mClearAllButton.getWidth() :
+                mClearAllButton.getX();
+
+        // X coordinate of the carousel scrolled as far as possible in the direction towards the
+        // button. Logically, the button is "behind" the least recent task. This is the
+        // coordinate of the end of the least recent task in the carousel just after opening,
+        // with the most recent task in the center, and the rest of tasks go from that point
+        // towards and potentially behind the button.
+        final int carouselMotionLimit = getScrollForPage(childCount - 1) - getScrollForPage(0) +
+                (mIsRtl ?
+                        getPaddingLeft() + mInsets.left :
+                        getWidth() - getPaddingRight() - mInsets.right);
+
+        // The carousel might not be able to ever cover a part of the Clear-all button. Then
+        // always show the button as 100%. Technically, this check also prevents dividing by zero
+        // or getting a negative transparency ratio.
+        if (mIsRtl ? carouselMotionLimit >= fullAlphaX : carouselMotionLimit <= fullAlphaX) {
+            return 1;
+        }
+
+        // If the carousel is able to cover the button completely, we make the button completely
+        // transparent when E hits the border of the button farthest from the most recent task.
+        // Or, the carousel may not be able to move that far towards the button so it completely
+        // covers the it. Then we set the motion limit position of the carousel as the point
+        // where the button reaches 0 alpha.
+        final float zeroAlphaX = mIsRtl ?
+                Math.max(mClearAllButton.getX(), carouselMotionLimit) :
+                Math.min(mClearAllButton.getX() + mClearAllButton.getWidth(), carouselMotionLimit);
+
+        return Utilities.boundToRange(
+                (zeroAlphaX - carouselCurrentEnd) /
+                        (zeroAlphaX - fullAlphaX), 0, 1);
+    }
+
+    private void updateClearAllButtonAlpha() {
+        if (mClearAllButton != null) {
+            mClearAllButton.setAlpha(calculateClearAllButtonAlpha() * mContentAlpha);
+        }
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        updateClearAllButtonAlpha();
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        super.onTouchEvent(ev);
-        // Do not let touch escape to siblings below this view.
-        return true;
+        if (ev.getAction() == MotionEvent.ACTION_DOWN && mTouchState == TOUCH_STATE_REST
+                && mScroller.isFinished() && mClearAllButton.getVisibility() == View.VISIBLE) {
+            mClearAllButton.getHitRect(mTempRect);
+            mTempRect.offset(-getLeft(), -getTop());
+            if (mTempRect.contains((int) ev.getX(), (int) ev.getY())) {
+                // If nothing is in motion, let the Clear All button process the event.
+                return false;
+            }
+        }
+
+        if (ev.getAction() == MotionEvent.ACTION_UP && mShowEmptyMessage) {
+            onAllTasksRemoved();
+        }
+        return super.onTouchEvent(ev);
     }
 
     private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
@@ -374,6 +441,9 @@
                 taskView.resetVisualProperties();
             }
         }
+        if (mRunningTaskTileHidden) {
+            setRunningTaskHidden(mRunningTaskTileHidden);
+        }
 
         updateCurveProperties();
         // Update the set of visible task's data
@@ -410,6 +480,10 @@
 
     protected abstract void getTaskSize(DeviceProfile dp, Rect outRect);
 
+    public void getTaskSize(Rect outRect) {
+        getTaskSize(mActivity.getDeviceProfile(), outRect);
+    }
+
     @Override
     protected boolean computeScrollHelper() {
         boolean scrolling = super.computeScrollHelper();
@@ -507,12 +581,13 @@
         mHasVisibleTaskData.clear();
     }
 
-
     protected abstract void onAllTasksRemoved();
 
     public void reset() {
-        unloadVisibleTaskData();
         mRunningTaskId = -1;
+        mRunningTaskTileHidden = false;
+
+        unloadVisibleTaskData();
         setCurrentPage(0);
 
         OverviewCallbacks.get(getContext()).onResetOverview();
@@ -549,12 +624,17 @@
                     new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false);
             taskView.bind(mTmpRunningTask);
         }
-        setCurrentTask(mRunningTaskId);
+        setCurrentTask(runningTaskId);
+    }
 
-        // Hide the task that we are animating into, ignore if there is no associated task (ie. the
-        // assistant)
-        if (getPageAt(mCurrentPage) != null) {
-            getPageAt(mCurrentPage).setAlpha(0);
+    /**
+     * Hides the tile associated with {@link #mRunningTaskId}
+     */
+    public void setRunningTaskHidden(boolean isHidden) {
+        mRunningTaskTileHidden = isHidden;
+        TaskView runningTask = getTaskView(mRunningTaskId);
+        if (runningTask != null) {
+            runningTask.setAlpha(isHidden ? 0 : mContentAlpha);
         }
     }
 
@@ -562,7 +642,13 @@
      * Similar to {@link #showTask(int)} but does not put any restrictions on the first tile.
      */
     public void setCurrentTask(int runningTaskId) {
-        mRunningTaskId = runningTaskId;
+        if (mRunningTaskTileHidden) {
+            setRunningTaskHidden(false);
+            mRunningTaskId = runningTaskId;
+            setRunningTaskHidden(true);
+        } else {
+            mRunningTaskId = runningTaskId;
+        }
         setCurrentPage(0);
 
         // Load the tasks (if the loading is already
@@ -603,9 +689,9 @@
         TaskView firstTask = (TaskView) getChildAt(0);
         if (firstTask != null) {
             if (animate) {
-                firstTask.animateIconToScale(scale);
+                firstTask.animateIconToScaleAndDim(scale);
             } else {
-                firstTask.setIconScale(scale);
+                firstTask.setIconScaleAndDim(scale);
             }
         }
     }
@@ -643,8 +729,23 @@
         mIgnoreResetTaskViews.remove(taskView);
     }
 
+    private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
+        addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
+        addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+                duration, LINEAR, anim);
+    }
+
+    private void removeTask(Task task, PendingAnimation.OnEndListener onEndListener) {
+        if (task != null) {
+            ActivityManagerWrapper.getInstance().removeTask(task.key.id);
+            mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
+                    onEndListener.logAction, Direction.UP,
+                    TaskUtils.getComponentKeyForTask(task.key));
+        }
+    }
+
     public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
-            boolean removeTask, long duration) {
+            boolean shouldRemoveTask, long duration) {
         if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
             throw new IllegalStateException("Another pending animation is still running");
         }
@@ -676,9 +777,7 @@
             View child = getChildAt(i);
             if (child == taskView) {
                 if (animateTaskView) {
-                    addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
-                    addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
-                            duration, LINEAR, anim);
+                    addDismissedTaskAnimations(taskView, anim, duration);
                 }
             } else {
                 // If we just take newScroll - oldScroll, everything to the right of dragged task
@@ -723,14 +822,8 @@
         mPendingAnimation = pendingAnimation;
         mPendingAnimation.addEndListener((onEndListener) -> {
            if (onEndListener.isSuccess) {
-               if (removeTask) {
-                   Task task = taskView.getTask();
-                   if (task != null) {
-                       ActivityManagerWrapper.getInstance().removeTask(task.key.id);
-                       mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
-                               onEndListener.logAction, Direction.UP,
-                               TaskUtils.getComponentKeyForTask(task.key));
-                   }
+               if (shouldRemoveTask) {
+                   removeTask(taskView.getTask(), onEndListener);
                }
                int pageToSnapTo = mCurrentPage;
                if (draggedIndex < pageToSnapTo) {
@@ -749,6 +842,33 @@
         return pendingAnimation;
     }
 
+    public PendingAnimation createAllTasksDismissAnimation(long duration) {
+        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+            throw new IllegalStateException("Another pending animation is still running");
+        }
+        AnimatorSet anim = new AnimatorSet();
+        PendingAnimation pendingAnimation = new PendingAnimation(anim);
+
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            addDismissedTaskAnimations(getChildAt(i), anim, duration);
+        }
+
+        mPendingAnimation = pendingAnimation;
+        mPendingAnimation.addEndListener((onEndListener) -> {
+            if (onEndListener.isSuccess) {
+                while (getChildCount() != 0) {
+                    TaskView taskView = getPageAt(getChildCount() - 1);
+                    removeTask(taskView.getTask(), onEndListener);
+                    removeView(taskView);
+                }
+                onAllTasksRemoved();
+            }
+            mPendingAnimation = null;
+        });
+        return pendingAnimation;
+    }
+
     private static void addAnim(ObjectAnimator anim, long duration,
             TimeInterpolator interpolator, AnimatorSet set) {
         anim.setDuration(duration).setInterpolator(interpolator);
@@ -772,9 +892,7 @@
         }
     }
 
-    public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
-        PendingAnimation pendingAnim = createTaskDismissAnimation(taskView, animateTaskView,
-                removeTask, DISMISS_TASK_DURATION);
+    private void runDismissAnimation(PendingAnimation pendingAnim) {
         AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
                 pendingAnim.anim, DISMISS_TASK_DURATION);
         controller.dispatchOnStart();
@@ -783,6 +901,15 @@
         controller.start();
     }
 
+    public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
+        runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
+                DISMISS_TASK_DURATION));
+    }
+
+    public void dismissAllTasks() {
+        runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
+    }
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -817,21 +944,23 @@
         snapToPageRelative(1);
     }
 
-    public void setContentAlpha(float alpha) {
-        if (mContentAlpha == alpha) {
-            return;
-        }
+    public float getContentAlpha() {
+        return mContentAlpha;
+    }
 
+    public void setContentAlpha(float alpha) {
         mContentAlpha = alpha;
         for (int i = getChildCount() - 1; i >= 0; i--) {
-            getChildAt(i).setAlpha(alpha);
+            TaskView child = getPageAt(i);
+            if (!mRunningTaskTileHidden || child.getTask().key.id != mRunningTaskId) {
+                getChildAt(i).setAlpha(alpha);
+            }
         }
 
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
         mEmptyIcon.setAlpha(alphaInt);
-
-        setVisibility(alpha > 0 ? VISIBLE : GONE);
+        updateClearAllButtonAlpha();
     }
 
     public void setAdjacentScale(float adjacentScale) {
@@ -869,11 +998,10 @@
     private float[] getAdjacentScaleAndTranslation(TaskView currTask, TaskView adjacentTask,
             float currTaskToScale, float currTaskToTranslationY) {
         float displacement = currTask.getWidth() * (currTaskToScale - currTask.getCurveScale());
-        return new float[] {
-                currTaskToScale * adjacentTask.getCurveScale(),
-                mIsRtl ? -displacement : displacement,
-                currTaskToTranslationY
-        };
+        sTempFloatArray[0] = currTaskToScale * adjacentTask.getCurveScale();
+        sTempFloatArray[1] = mIsRtl ? -displacement : displacement;
+        sTempFloatArray[2] = currTaskToTranslationY;
+        return sTempFloatArray;
     }
 
     @Override
@@ -899,6 +1027,9 @@
         mShowEmptyMessage = isEmpty;
         updateEmptyStateUi(hasSizeChanged);
         invalidate();
+        if (mClearAllButton != null) {
+            updateClearAllButtonVisibility();
+        }
     }
 
     @Override
@@ -911,10 +1042,18 @@
         boolean hasValidSize = getWidth() > 0 && getHeight() > 0;
         if (sizeChanged && hasValidSize) {
             mEmptyTextLayout = null;
-        }
-
-        if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
             mLastMeasureSize.set(getWidth(), getHeight());
+        }
+        updateClearAllButtonVisibility();
+
+        if (!mShowEmptyMessage) return;
+
+        // The icon needs to be centered. Need to scoll to horizontal 0 because with Clear-All
+        // space on the right, it's not guaranteed that after deleting all tasks, the horizontal
+        // scroll position will be zero.
+        scrollTo(0, 0);
+
+        if (hasValidSize && mEmptyTextLayout == null) {
             int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
             mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
                     mEmptyMessagePaint, availableWidth)
@@ -937,8 +1076,13 @@
 
     protected void maybeDrawEmptyMessage(Canvas canvas) {
         if (mShowEmptyMessage && mEmptyTextLayout != null) {
-            mEmptyIcon.draw(canvas);
+            // Offset to center in the visible (non-padded) part of RecentsView
+            mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
+                    mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
             canvas.save();
+            canvas.translate((mTempRect.left - mTempRect.right) / 2,
+                    (mTempRect.top - mTempRect.bottom) / 2);
+            mEmptyIcon.draw(canvas);
             canvas.translate(mEmptyMessagePadding,
                     mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
             mEmptyTextLayout.draw(canvas);
@@ -1016,23 +1160,30 @@
             return new PendingAnimation(anim);
         }
 
-        final RecentsAnimationInterpolator recentsInterpolator = tv.getRecentsInterpolator();
-        ValueAnimator targetViewAnim = ValueAnimator.ofFloat(0, 1);
-        targetViewAnim.addUpdateListener((animation) -> {
-            float percent = animation.getAnimatedFraction();
-            TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
-            tv.setScaleX(tw.taskScale);
-            tv.setScaleY(tw.taskScale);
-            tv.setTranslationX(tw.taskX);
-            tv.setTranslationY(tw.taskY);
-        });
-        anim.play(targetViewAnim);
+        tv.setVisibility(INVISIBLE);
+        TaskViewDrawable drawable = new TaskViewDrawable(tv, this);
+        getOverlay().add(drawable);
+
+        ObjectAnimator drawableAnim =
+                ObjectAnimator.ofFloat(drawable, TaskViewDrawable.PROGRESS, 1, 0);
+        drawableAnim.setInterpolator(LINEAR);
+
+        anim.play(drawableAnim);
         anim.setDuration(duration);
 
+        Consumer<Boolean> onTaskLaunchFinish = (result) -> {
+            onTaskLaunched(result);
+            tv.setVisibility(VISIBLE);
+            getOverlay().remove(drawable);
+            if (!result) {
+                Log.w(TAG, tv.getLaunchTaskFailedMsg());
+            }
+        };
+
         mPendingAnimation = new PendingAnimation(anim);
         mPendingAnimation.addEndListener((onEndListener) -> {
             if (onEndListener.isSuccess) {
-                tv.launchTask(false);
+                tv.launchTask(false, onTaskLaunchFinish, getHandler());
                 Task task = tv.getTask();
                 if (task != null) {
                     mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
@@ -1040,13 +1191,19 @@
                             TaskUtils.getComponentKeyForTask(task.key));
                 }
             } else {
-                resetTaskVisuals();
+                onTaskLaunchFinish.accept(false);
             }
             mPendingAnimation = null;
         });
         return mPendingAnimation;
     }
 
+    public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
+
+    protected void onTaskLaunched(boolean success) {
+        resetTaskVisuals();
+    }
+
     @Override
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
@@ -1060,4 +1217,35 @@
     protected String getCurrentPageDescription() {
         return "";
     }
+
+    private int additionalScrollForClearAllButton() {
+        return (int) getResources().getDimension(
+                R.dimen.clear_all_container_width) - getPaddingEnd();
+    }
+
+    @Override
+    protected int computeMaxScrollX() {
+        if (getChildCount() == 0) {
+            return super.computeMaxScrollX();
+        }
+
+        // Allow a clear_all_container_width-sized gap after the last task.
+        return super.computeMaxScrollX() + (mIsRtl ? 0 : additionalScrollForClearAllButton());
+    }
+
+    @Override
+    protected int offsetForPageScrolls() {
+        return mIsRtl ? additionalScrollForClearAllButton() : 0;
+    }
+
+    private void updateClearAllButtonVisibility() {
+        if (mClearAllButton == null) return;
+        mClearAllButton.setVisibility(mShowEmptyMessage ? GONE : VISIBLE);
+        updateClearAllButtonAlpha();
+    }
+
+    public void setClearAllButton(View clearAllButton) {
+        mClearAllButton = clearAllButton;
+        updateClearAllButtonVisibility();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
new file mode 100644
index 0000000..15925b5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -0,0 +1,90 @@
+package com.android.quickstep.views;
+
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.R;
+
+public class RecentsViewContainer extends InsettableFrameLayout {
+    public static final FloatProperty<RecentsViewContainer> CONTENT_ALPHA =
+            new FloatProperty<RecentsViewContainer>("contentAlpha") {
+                @Override
+                public void setValue(RecentsViewContainer view, float v) {
+                    view.setContentAlpha(v);
+                }
+
+                @Override
+                public Float get(RecentsViewContainer view) {
+                    return view.mRecentsView.getContentAlpha();
+                }
+            };
+
+    private final Rect mTempRect = new Rect();
+
+    private RecentsView mRecentsView;
+    private View mClearAllButton;
+
+    public RecentsViewContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mClearAllButton = findViewById(R.id.clear_all_button);
+        mClearAllButton.setOnClickListener((v) -> {
+            mRecentsView.mActivity.getUserEventDispatcher()
+                    .logActionOnControl(TAP, CLEAR_ALL_BUTTON);
+            mRecentsView.dismissAllTasks();
+        });
+
+        mRecentsView = (RecentsView) findViewById(R.id.overview_panel);
+        final InsettableFrameLayout.LayoutParams params =
+                (InsettableFrameLayout.LayoutParams) mClearAllButton.getLayoutParams();
+        params.gravity = Gravity.TOP | (RecentsView.FLIP_RECENTS ? Gravity.START : Gravity.END);
+        mClearAllButton.setLayoutParams(params);
+        mClearAllButton.forceHasOverlappingRendering(false);
+        mRecentsView.setClearAllButton(mClearAllButton);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        mRecentsView.getTaskSize(mTempRect);
+
+        mClearAllButton.setTranslationX(
+                (mRecentsView.isRtl() ? 1 : -1) *
+                        (getResources().getDimension(R.dimen.clear_all_container_width)
+                                - mClearAllButton.getMeasuredWidth()) / 2);
+        mClearAllButton.setTranslationY(
+                mTempRect.top + (mTempRect.height() - mClearAllButton.getMeasuredHeight()) / 2
+                        - mClearAllButton.getTop());
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        super.onTouchEvent(ev);
+        // Do not let touch escape to siblings below this view. This prevents scrolling of the
+        // workspace while in Recents.
+        return true;
+    }
+
+    public void setContentAlpha(float alpha) {
+        if (alpha == mRecentsView.getContentAlpha()) {
+            return;
+        }
+        mRecentsView.setContentAlpha(alpha);
+        setVisibility(alpha > 0 ? VISIBLE : GONE);
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 58b7db7..c724930 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep.views;
 
+import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -28,6 +30,8 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Property;
 import android.view.View;
 
 import com.android.launcher3.BaseActivity;
@@ -46,6 +50,19 @@
 
     private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
 
+    public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
+            new FloatProperty<TaskThumbnailView>("dimAlpha") {
+                @Override
+                public void setValue(TaskThumbnailView thumbnail, float dimAlpha) {
+                    thumbnail.setDimAlpha(dimAlpha);
+                }
+
+                @Override
+                public Float get(TaskThumbnailView thumbnailView) {
+                    return thumbnailView.mDimAlpha;
+                }
+            };
+
     private final float mCornerRadius;
 
     private final BaseActivity mActivity;
@@ -111,6 +128,8 @@
 
     /**
      * Sets the alpha of the dim layer on top of this view.
+     *
+     * If dimAlpha is 0, no dimming is applied; if dimAlpha is 1, the thumbnail will be black.
      */
     public void setDimAlpha(float dimAlpha) {
         mDimAlpha = dimAlpha;
@@ -126,30 +145,34 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
+        drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
+    }
+
+    public float getCornerRadius() {
+        return mCornerRadius;
+    }
+
+    public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
+            float cornerRadius) {
+        // Always draw the background since the snapshots may be translucent
+        canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
         if (mTask == null) {
             return;
         }
-        int width = getMeasuredWidth();
-        int height = getMeasuredHeight();
-        if (mClipBottom > 0 && !mTask.isLocked) {
-            canvas.save();
-            canvas.clipRect(0, 0, width, mClipBottom);
-
-            canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius, mPaint);
-            canvas.restore();
-            canvas.save();
-            canvas.clipRect(0, mClipBottom, width, height);
-            canvas.drawRoundRect(0, 0, width, height, mCornerRadius, mCornerRadius,
-                    mBackgroundPaint);
-            canvas.restore();
-        } else {
-            canvas.drawRoundRect(0, 0, width, height, mCornerRadius,
-                    mCornerRadius, mTask.isLocked ? mBackgroundPaint : mPaint);
+        if (!mTask.isLocked) {
+            if (mClipBottom > 0) {
+                canvas.save();
+                canvas.clipRect(x, y, width, mClipBottom);
+                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
+                canvas.restore();
+            } else {
+                canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
+            }
         }
     }
 
     private void updateThumbnailPaintFilter() {
-        int mul = (int) (mDimAlpha * 255);
+        int mul = (int) ((1 - mDimAlpha) * 255);
         if (mBitmapShader != null) {
             LightingColorFilter filter = getLightingColorFilter(mul);
             mPaint.setColorFilter(filter);
@@ -167,9 +190,9 @@
         if (mBitmapShader != null && mThumbnailData != null) {
             float scale = mThumbnailData.scale;
             Rect thumbnailInsets  = mThumbnailData.insets;
-            float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
+            final float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
                     (thumbnailInsets.left + thumbnailInsets.right) * scale;
-            float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
+            final float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
                     (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
 
             final float thumbnailScale;
@@ -185,7 +208,8 @@
                 // Rotate the screenshot if not in multi-window mode
                 rotate = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
                         configuration.orientation != mThumbnailData.orientation &&
-                        !mActivity.isInMultiWindowModeCompat();
+                        !mActivity.isInMultiWindowModeCompat() &&
+                        mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
                 // Scale the screenshot to always fit the width of the card.
                 thumbnailScale = rotate
                         ? getMeasuredWidth() / thumbnailHeight
@@ -216,7 +240,8 @@
             mMatrix.postScale(thumbnailScale, thumbnailScale);
             mBitmapShader.setLocalMatrix(mMatrix);
 
-            float bitmapHeight = Math.max(thumbnailHeight * thumbnailScale, 0);
+            float bitmapHeight = Math.max((rotate ? thumbnailWidth : thumbnailHeight)
+                    * thumbnailScale, 0);
             if (Math.round(bitmapHeight) < getMeasuredHeight()) {
                 mClipBottom = bitmapHeight;
             }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index f04acaf..a9b24e5 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -16,6 +16,11 @@
 
 package com.android.quickstep.views;
 
+import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.app.ActivityOptions;
 import android.content.Context;
@@ -25,6 +30,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -54,6 +60,8 @@
  */
 public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks {
 
+    private static final String TAG = TaskView.class.getSimpleName();
+
     /** A curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
     private static final TimeInterpolator CURVE_INTERPOLATOR
             = x -> (float) -Math.cos(x * Math.PI) / 2f + .5f;
@@ -75,6 +83,8 @@
     private TaskThumbnailView mSnapshotView;
     private ImageView mIconView;
     private float mCurveScale;
+    private float mCurveDimAlpha;
+    private Animator mDimAlphaAnim;
 
     public TaskView(Context context) {
         this(context, null);
@@ -124,8 +134,16 @@
         return mSnapshotView;
     }
 
+    public ImageView getIconView() {
+        return mIconView;
+    }
+
     public void launchTask(boolean animate) {
-        launchTask(animate, null, null);
+        launchTask(animate, (result) -> {
+            if (!result) {
+                Log.w(TAG, getLaunchTaskFailedMsg());
+            }
+        }, getHandler());
     }
 
     public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
@@ -166,14 +184,27 @@
         // Do nothing
     }
 
-    public void animateIconToScale(float scale) {
+    public void animateIconToScaleAndDim(float scale) {
         mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
+        mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA, scale * mCurveDimAlpha);
+        mDimAlphaAnim.setDuration(SCALE_ICON_DURATION);
+        mDimAlphaAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mDimAlphaAnim = null;
+            }
+        });
+        mDimAlphaAnim.start();
     }
 
-    protected void setIconScale(float iconScale) {
+    protected void setIconScaleAndDim(float iconScale) {
         mIconView.animate().cancel();
         mIconView.setScaleX(iconScale);
         mIconView.setScaleY(iconScale);
+        if (mDimAlphaAnim != null) {
+            mDimAlphaAnim.cancel();
+        }
+        mSnapshotView.setDimAlpha(iconScale * mCurveDimAlpha);
     }
 
     public void resetVisualProperties() {
@@ -190,13 +221,25 @@
         float curveInterpolation =
                 CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
 
-        mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
+        mCurveDimAlpha = curveInterpolation * MAX_PAGE_SCRIM_ALPHA;
+        if (mDimAlphaAnim == null && mIconView.getScaleX() > 0) {
+            mSnapshotView.setDimAlpha(mCurveDimAlpha);
+        }
 
-        mCurveScale = 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
+        mCurveScale = getCurveScaleForCurveInterpolation(curveInterpolation);
         setScaleX(mCurveScale);
         setScaleY(mCurveScale);
     }
 
+    public float getCurveScaleForInterpolation(float linearInterpolation) {
+        float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
+        return getCurveScaleForCurveInterpolation(curveInterpolation);
+    }
+
+    private float getCurveScaleForCurveInterpolation(float curveInterpolation) {
+        return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
+    }
+
     public float getCurveScale() {
         return mCurveScale;
     }
@@ -281,4 +324,12 @@
 
         return super.performAccessibilityAction(action, arguments);
     }
+
+    public String getLaunchTaskFailedMsg() {
+        String msg = "Failed to launch task";
+        if (mTask != null) {
+            msg += " (task=" + mTask.key.baseIntent + " userId=" + mTask.key.userId + ")";
+        }
+        return msg;
+    }
 }
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 450d107..5e7b117 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -31,8 +31,6 @@
 
     <include layout="@layout/all_apps_floating_header" />
 
-    <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
-     platform bug, which prevents using custom attributes in <include> tag -->
     <include
         android:id="@id/search_container_all_apps"
         layout="@layout/search_container_all_apps"/>
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index a4acf06..6556adf 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -40,7 +40,7 @@
             launcher:pageIndicator="@+id/page_indicator" />
 
         <include
-            android:id="@+id/overview_panel"
+            android:id="@+id/overview_panel_container"
             layout="@layout/overview_panel"
             android:visibility="gone" />
 
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 02dd1c9..8826da8 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home-instellings"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Tuis-instellings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Kennisgewingkolle"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aan"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 8598a8b..f50e5fe 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -87,7 +87,7 @@
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"عطَّل المشرف هذه الميزة"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"نقاط الإشعارات"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"قيد التشغيل"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"قيد الإيقاف"</string>
+    <string name="icon_badging_desc_off" msgid="5503319969924580241">"غير مفعّل"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"يلزم تمكين الوصول إلى الإشعارات"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"لعرض نقاط الإشعارات، يجب تشغيل إشعارات التطبيق في <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"تغيير الإعدادات"</string>
@@ -96,7 +96,7 @@
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"للتطبيقات الجديدة"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"تغيير شكل الرمز"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"على الشاشة الرئيسية"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"استخدام الإعداد الافتراضي للنظام"</string>
+    <string name="icon_shape_system_default" msgid="1709762974822753030">"استخدام الإعداد التلقائي للنظام"</string>
     <string name="icon_shape_square" msgid="633575066111622774">"مربّع"</string>
     <string name="icon_shape_squircle" msgid="5658049910802669495">"رمز دائري مربّع"</string>
     <string name="icon_shape_circle" msgid="6550072265930144217">"دائرة"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 3c04905..5f61c69 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Настройки за Google Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Настройки за началния екран"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Точки за известия"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Включено"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 08a4533..fa88665 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -30,7 +30,7 @@
     <string name="home_screen" msgid="806512411299847073">"Početni ekran"</string>
     <string name="custom_actions" msgid="3747508247759093328">"Prilagođene akcije"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite &amp; i držite da biste uzeli dodatak."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite &amp; i držite da biste uzeli vidžet ili koristite prilagođene radnje."</string>
+    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dodirnite dvaput &amp; i držite da biste uzeli vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i držite da postavite ručno"</string>
@@ -41,7 +41,7 @@
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
     <string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
     <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i držite da uzmete prečicu."</string>
-    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvaput dodirnite i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dodirnite dvaput i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Omiljeno"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"Spisak aplikacija"</string>
@@ -80,7 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Dodaci"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Postavke za Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog ekrana"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Tačke za obavještenja"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 27b2979..de153f4 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -50,7 +50,7 @@
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Inici"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Suprimeix"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Dades de l\'aplicació"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informació de l\'aplicació"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instal·la"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configuració de la pantalla d\'inici"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuració de pantalla d\'inici"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Punts de notificació"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activat"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 18eec84..c5bae5b 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχικής σελίδας"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχ. Οθ."</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Κουκκίδες ειδοποίησης"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Ενεργή"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 989cd61..0410f35 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configuración de Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuración de página principal"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificación"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activada"</string>
@@ -92,7 +92,7 @@
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para nuevas apps"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los íconos"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"en la pantalla principal"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar el sistema predeterminado"</string>
+    <string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados del sistema"</string>
     <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
     <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
     <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5b948da..6f045e1 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -79,10 +79,10 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Ajustes de Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Ajustes de la pantalla de inicio"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificación"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activada"</string>
+    <string name="icon_badging_title" msgid="874121399231955394">"Burbujas de notificación"</string>
+    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activado"</string>
     <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivada"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Se necesita acceso a las notificaciones"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar burbujas de notificación, activa las notificaciones de <xliff:g id="NAME">%1$s</xliff:g>"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 2946076..715e396 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Avalehe seaded"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Avaekraani seaded"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Märguandetäpid"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Sees"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index c3099bd..e25af96 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -81,13 +81,13 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Jakinarazteko biribiltxoak"</string>
+    <string name="icon_badging_title" msgid="874121399231955394">"Jakinarazpen-biribiltxoak"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktibatuta"</string>
     <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desaktibatuta"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Jakinarazpenetarako sarbidea behar da"</string>
-    <string name="msg_missing_notification_access" msgid="281113995110910548">"Jakinarazteko biribiltxoak ikusteko, aktibatu <xliff:g id="NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak"</string>
+    <string name="msg_missing_notification_access" msgid="281113995110910548">"Jakinarazpen-biribiltxoak ikusteko, aktibatu <xliff:g id="NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Aldatu ezarpenak"</string>
-    <string name="icon_badging_service_title" msgid="2309733118428242174">"Erakutsi jakinarazteko biribiltxoak"</string>
+    <string name="icon_badging_service_title" msgid="2309733118428242174">"Erakutsi jakinarazpen-biribiltxoak"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrietan"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"Aldatu ikonoaren forma"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index e1b7581..283d4b7 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres de l\'écran d\'accueil"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Pastilles de notification"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activé"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 0cfdfa8..3817f07 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Configuración de inicio"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Configuración da pantalla de Inicio"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificacións"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activado"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index f98859d..798c061 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -89,7 +89,7 @@
     <string name="title_change_settings" msgid="1376365968844349552">"सेटिंग बदलें"</string>
     <string name="icon_badging_service_title" msgid="2309733118428242174">"नए नोटिफ़िकेशन बताने वाला गोल निशान दिखाएं"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीन में आइकॉन जोड़ें"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नए ऐप के लिए"</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नए ऐप्लिकेशन के लिए"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"आइकॉन का आकार बदलें"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"होम स्‍क्रीन पर"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टम डिफ़ॉल्ट का उपयोग करें"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 4972e81..5f33a99 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"A Home beállításai"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Kezdőoldal beállításai"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Értesítési pöttyök"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Bekapcsolva"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index e0e7403..eedf7fd 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -39,7 +39,7 @@
     <string name="all_apps_loading_message" msgid="5813968043155271636">"Колдонмолор жүктөлүүдө…"</string>
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" сурамына дал келген колдонмолор табылган жок"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"Көбүрөөк колдонмолорду издөө"</string>
-    <string name="notifications_header" msgid="1404149926117359025">"Эскертмелер"</string>
+    <string name="notifications_header" msgid="1404149926117359025">"Билдирмелер"</string>
     <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Кыска жолду тандоо үчүн басып туруңуз."</string>
     <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
     <string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
@@ -81,7 +81,7 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Башкы беттин жөндөөлөрү"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
-    <string name="icon_badging_title" msgid="874121399231955394">"Эскертме белгилери"</string>
+    <string name="icon_badging_title" msgid="874121399231955394">"Билдирмелер белгилери"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Күйүк"</string>
     <string name="icon_badging_desc_off" msgid="5503319969924580241">"Өчүк"</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Эскертмелерге уруксат берилиши керек"</string>
@@ -92,7 +92,7 @@
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"Сүрөтчөнүн формасын өзгөртүү"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Башкы экранда"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Тутум сушунтаган демейкисин колдонуу"</string>
+    <string name="icon_shape_system_default" msgid="1709762974822753030">"Демейки тутум жөндөөлөрү колдонулат"</string>
     <string name="icon_shape_square" msgid="633575066111622774">"Чарчы"</string>
     <string name="icon_shape_squircle" msgid="5658049910802669495">"Бурчтары жумуру төрт бурчтук"</string>
     <string name="icon_shape_circle" msgid="6550072265930144217">"Тегерек"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 0e2f9c8..8506b29 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -28,9 +28,9 @@
     <string name="safemode_widget_error" msgid="4863470563535682004">"विजेट सुरक्षित मोडमध्ये अक्षम झाले"</string>
     <string name="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नाही"</string>
     <string name="home_screen" msgid="806512411299847073">"होम स्क्रीन"</string>
-    <string name="custom_actions" msgid="3747508247759093328">"सानुकूल क्रिया"</string>
+    <string name="custom_actions" msgid="3747508247759093328">"कस्टम क्रिया"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
-    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा सानुकूल क्रिया वापरा."</string>
+    <string name="long_accessible_way_to_add" msgid="4289502106628154155">"एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा कस्टम क्रिया वापरा."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
     <string name="add_item_request_drag_hint" msgid="5899764264480397019">"स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
@@ -73,7 +73,7 @@
     <string name="workspace_new_page" msgid="257366611030256142">"नवीन मुख्य स्क्रीन पृष्ठ"</string>
     <string name="folder_opened" msgid="94695026776264709">"फोल्डर उघडले, <xliff:g id="WIDTH">%1$d</xliff:g> बाय <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डर बंद करण्यासाठी टॅप करा"</string>
-    <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे जतन करण्यासाठी टॅप करा"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे सेव्ह करण्यासाठी टॅप करा"</string>
     <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बंद"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -102,7 +102,7 @@
     <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
     <string name="abandoned_search" msgid="891119232568284442">"शोधा"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"हा अॅप इंस्टॉल केलेला नाही"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता."</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. तुम्ही ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 458b6dd..ed24914 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -54,9 +54,9 @@
     <string name="install_drop_target_label" msgid="2539096853673231757">"Installeren"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op de homepage lezen"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op startscherm lezen"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"De app toestaan de instellingen en snelkoppelingen op de homepage te lezen."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"instellingen en snelkoppelingen op de homepage schrijven"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"instellingen en snelkoppelingen op startscherm zetten"</string>
     <string name="permdesc_write_settings" msgid="5440712911516509985">"De app toestaan de instellingen en snelkoppelingen op de homepage te wijzigen."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> mag niet bellen"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"Probleem bij het laden van widget"</string>
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Map: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Instellingen voor homepage"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Instellingen voor startscherm"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Meldingsstipjes"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Aan"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 0d3224d..c17b50c 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -50,7 +50,7 @@
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ecrã principal"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"Inf. da aplicação"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicação"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador."</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index d04ca5c..9aee3c9 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -84,14 +84,14 @@
     <string name="settings_button_text" msgid="8873672322605444408">"Настройки главного экрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Значки уведомлений"</string>
-    <string name="icon_badging_desc_on" msgid="2627952638544674079">"ВКЛ"</string>
-    <string name="icon_badging_desc_off" msgid="5503319969924580241">"ВЫКЛ"</string>
+    <string name="icon_badging_desc_on" msgid="2627952638544674079">"Вкл."</string>
+    <string name="icon_badging_desc_off" msgid="5503319969924580241">"Выкл."</string>
     <string name="title_missing_notification_access" msgid="7503287056163941064">"Нет доступа к уведомлениям"</string>
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Чтобы показывать значки уведомлений, включите уведомления в приложении \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Изменить настройки"</string>
     <string name="icon_badging_service_title" msgid="2309733118428242174">"Показывать значки уведомлений"</string>
     <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавлять значки"</string>
-    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Добавлять значки установленных приложений на главный экран."</string>
+    <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Добавлять значки установленных приложений на главный экран"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"Изменить форму значков"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на главном экране"</string>
     <string name="icon_shape_system_default" msgid="1709762974822753030">"Использовать системные настройки по умолчанию"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 4d90dee..77246fb 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -81,7 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia služby Home"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia plochy"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Bodky upozornení"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Zapnuté"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 61d7f8b..f074ed4 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -94,7 +94,7 @@
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Kwa ajili ya programu mpya"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"Badilisha umbo la aikoni"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"kwenye Skrini ya mwanzo"</string>
-    <string name="icon_shape_system_default" msgid="1709762974822753030">"Tumia umbo chaguo-msingi la mfumo"</string>
+    <string name="icon_shape_system_default" msgid="1709762974822753030">"Tumia umbo chaguomsingi la mfumo"</string>
     <string name="icon_shape_square" msgid="633575066111622774">"Mraba"</string>
     <string name="icon_shape_squircle" msgid="5658049910802669495">"Mstatili wenye pembe duara"</string>
     <string name="icon_shape_circle" msgid="6550072265930144217">"Mduara"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 13358c0..d507be4 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -40,10 +40,8 @@
     <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
     <string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
     <string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
-    <!-- no translation found for long_press_shortcut_to_add (4524750017792716791) -->
-    <skip />
-    <!-- no translation found for long_accessible_way_to_add_shortcut (3327314059613154633) -->
-    <skip />
+    <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ஷார்ட்கட்டைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்."</string>
+    <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
     <string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
     <string name="all_apps_button_label" msgid="8130441508702294465">"பயன்பாடுகளின் பட்டியல்"</string>
@@ -52,7 +50,7 @@
     <string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
     <string name="remove_drop_target_label" msgid="7812859488053230776">"அகற்று"</string>
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
-    <string name="app_info_drop_target_label" msgid="692894985365717661">"ஆப்ஸ் தகவல்"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"பயன்பாட்டுத் தகவல்"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"நிறுவு"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index e9f36a8..72dbd9e 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home sozlamalari"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Bosh ekran sozlamalari"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Bildirishnoma belgilari"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Yoniq"</string>
@@ -88,7 +88,7 @@
     <string name="msg_missing_notification_access" msgid="281113995110910548">"Bildirishnoma belgilarini ko‘rsatish uchun <xliff:g id="NAME">%1$s</xliff:g> ilovasida bildirishnomalarni yoqing"</string>
     <string name="title_change_settings" msgid="1376365968844349552">"Sozlamalarni o‘zgartirish"</string>
     <string name="icon_badging_service_title" msgid="2309733118428242174">"Bildirishnoma belgilarini ko‘rsatish"</string>
-    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka qo‘shish"</string>
+    <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka chiqarish"</string>
     <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
     <string name="icon_shape_override_label" msgid="2977264953998281004">"Ikonka shaklini o‘zgartirish"</string>
     <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Bosh ekranda"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 65a5ecc..c11c2e6 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt trang chủ"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt màn hình chính"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"Dấu chấm thông báo"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"Đang bật"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 4828006..a96e411 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -79,7 +79,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"主螢幕設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
     <string name="icon_badging_title" msgid="874121399231955394">"通知圓點"</string>
     <string name="icon_badging_desc_on" msgid="2627952638544674079">"開啟"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index a40afe1..9d1bb41 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -147,6 +147,7 @@
     <item type="id" name="search_container_all_apps" />
 
 <!-- Recents -->
+    <item type="id" name="overview_panel"/>
     <integer name="config_recentsMaxThumbnailCacheSize">6</integer>
     <integer name="config_recentsMaxIconCacheSize">12</integer>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bcb90e3..4fbd806 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -179,6 +179,10 @@
     <string name="msg_disabled_by_admin">Disabled by your admin</string>
 
     <!-- Strings for settings -->
+    <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+    <string name="allow_rotation_title">Allow Home screen rotation</string>
+    <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
+    <string name="allow_rotation_desc">When phone is rotated</string>
     <!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
     <string name="icon_badging_title">Notification dots</string>
     <!-- Text to indicate that the system icon badging setting is on [CHAR LIMIT=100] -->
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 7bb19f3..3bba73a 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -34,8 +34,14 @@
         android:title="@string/auto_add_shortcuts_label"
         android:summary="@string/auto_add_shortcuts_description"
         android:defaultValue="true"
-        android:persistent="true"
-        />
+        android:persistent="true" />
+
+    <SwitchPreference
+        android:key="pref_allowRotation"
+        android:title="@string/allow_rotation_title"
+        android:summary="@string/allow_rotation_desc"
+        android:defaultValue="@bool/allow_rotation"
+        android:persistent="true" />
 
     <ListPreference
         android:key="pref_override_icon_shape"
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index b0c5baf..097c341 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -70,6 +70,9 @@
     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
             | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP;
 
+    // Usually we show the back button when a floating view is open. Instead, hide for these types.
+    public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP;
+
     protected boolean mIsOpen;
 
     public AbstractFloatingView(Context context, AttributeSet attrs) {
@@ -94,6 +97,7 @@
         handleClose(animate);
         BaseActivity.fromContext(getContext()).getUserEventDispatcher()
                 .resetElapsedContainerMillis("container closed");
+        mIsOpen = false;
     }
 
     protected abstract void handleClose(boolean animate);
@@ -170,6 +174,11 @@
     }
 
     public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
-        return getOpenView(activity, TYPE_ALL);
+        return getTopOpenViewWithType(activity, TYPE_ALL);
+    }
+
+    public static AbstractFloatingView getTopOpenViewWithType(BaseDraggingActivity activity,
+            @FloatingViewType int type) {
+        return getOpenView(activity, type);
     }
 }
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index ae631a4..d133472 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -16,11 +16,15 @@
 
 package com.android.launcher3;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.app.Activity;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.graphics.Point;
+import android.support.annotation.IntDef;
 import android.view.Display;
 import android.view.View.AccessibilityDelegate;
 
@@ -28,21 +32,51 @@
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.SystemUiController;
 
+import java.lang.annotation.Retention;
 import java.util.ArrayList;
 
 public abstract class BaseActivity extends Activity {
 
+    public static final int INVISIBLE_BY_STATE_HANDLER = 1 << 0;
+    public static final int INVISIBLE_BY_APP_TRANSITIONS = 1 << 1;
+    public static final int INVISIBLE_ALL =
+            INVISIBLE_BY_STATE_HANDLER | INVISIBLE_BY_APP_TRANSITIONS;
+
+    @Retention(SOURCE)
+    @IntDef(
+            flag = true,
+            value = {INVISIBLE_BY_STATE_HANDLER, INVISIBLE_BY_APP_TRANSITIONS})
+    public @interface InvisibilityFlags{}
+
     private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
+    private final ArrayList<MultiWindowModeChangedListener> mMultiWindowModeChangedListeners =
+            new ArrayList<>();
 
     protected DeviceProfile mDeviceProfile;
     protected UserEventDispatcher mUserEventDispatcher;
     protected SystemUiController mSystemUiController;
 
-    private boolean mStarted;
+    private static final int ACTIVITY_STATE_STARTED = 1 << 0;
+    private static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+    /**
+     * State flag indicating if the user is active or the actitvity when to background as a result
+     * of user action.
+     * @see #isUserActive()
+     */
+    private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2;
+
+    @Retention(SOURCE)
+    @IntDef(
+            flag = true,
+            value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE})
+    public @interface ActivityFlags{}
+
+    @ActivityFlags
+    private int mActivityFlags;
+
     // When the recents animation is running, the visibility of the Launcher is managed by the
     // animation
-    private boolean mForceInvisible;
-    private boolean mUserActive;
+    @InvisibilityFlags private int mForceInvisible;
 
     public DeviceProfile getDeviceProfile() {
         return mDeviceProfile;
@@ -84,35 +118,65 @@
 
     @Override
     protected void onStart() {
-        mStarted = true;
+        mActivityFlags |= ACTIVITY_STATE_STARTED;
         super.onStart();
     }
 
     @Override
     protected void onResume() {
-        mUserActive = true;
+        mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE;
         super.onResume();
     }
 
     @Override
     protected void onUserLeaveHint() {
-        mUserActive = false;
+        mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE;
         super.onUserLeaveHint();
     }
 
     @Override
+    public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+        super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+        for (int i = mMultiWindowModeChangedListeners.size() - 1; i >= 0; i--) {
+            mMultiWindowModeChangedListeners.get(i).onMultiWindowModeChanged(isInMultiWindowMode);
+        }
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+
+        // Needed for activities that auto-enter PiP, which will not trigger a remote animation to
+        // be created
+        clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
+    }
+
+    @Override
     protected void onStop() {
-        mStarted = false;
-        mForceInvisible = false;
+        mActivityFlags &= ~ACTIVITY_STATE_STARTED & ~ACTIVITY_STATE_USER_ACTIVE;
+        mForceInvisible = 0;
         super.onStop();
     }
 
+    @Override
+    protected void onPause() {
+        mActivityFlags &= ~ACTIVITY_STATE_RESUMED;
+        super.onPause();
+    }
+
     public boolean isStarted() {
-        return mStarted;
+        return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
+    }
+
+    /**
+     * isResumed in already defined as a hidden final method in Activity.java
+     */
+    public boolean hasBeenResumed() {
+        return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
     }
 
     public boolean isUserActive() {
-        return mUserActive;
+        return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
     }
 
     public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
@@ -129,20 +193,33 @@
         }
     }
 
+    public void addMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
+        mMultiWindowModeChangedListeners.add(listener);
+    }
+
+    public void removeMultiWindowModeChangedListener(MultiWindowModeChangedListener listener) {
+        mMultiWindowModeChangedListeners.remove(listener);
+    }
+
     /**
      * Used to set the override visibility state, used only to handle the transition home with the
      * recents animation.
      * @see LauncherAppTransitionManagerImpl.getWallpaperOpenRunner()
      */
-    public void setForceInvisible(boolean invisible) {
-        mForceInvisible = invisible;
+    public void addForceInvisibleFlag(@InvisibilityFlags int flag) {
+        mForceInvisible |= flag;
     }
 
+    public void clearForceInvisibleFlag(@InvisibilityFlags int flag) {
+        mForceInvisible &= ~flag;
+    }
+
+
     /**
      * @return Wether this activity should be considered invisible regardless of actual visibility.
      */
     public boolean isForceInvisible() {
-        return mForceInvisible;
+        return mForceInvisible != 0;
     }
 
     /**
@@ -157,4 +234,8 @@
             mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
         }
     }
+
+    public interface MultiWindowModeChangedListener {
+        void onMultiWindowModeChanged(boolean isInMultiWindowMode);
+    }
 }
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 41bfcb7..fb7c0ce 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -65,27 +65,6 @@
 
     private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
 
-    private final BaseDraggingActivity mActivity;
-    private Drawable mIcon;
-    private final boolean mCenterVertically;
-
-    private final CheckLongPressHelper mLongPressHelper;
-    private final StylusEventHelper mStylusEventHelper;
-    private final float mSlop;
-
-    private final boolean mLayoutHorizontal;
-    private final int mIconSize;
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private int mTextColor;
-    private boolean mIsIconVisible = true;
-
-    private BadgeInfo mBadgeInfo;
-    private BadgeRenderer mBadgeRenderer;
-    private int mBadgeColor;
-    private float mBadgeScale;
-    private boolean mForceHideBadge;
-    private Point mTempSpaceForBadgeOffset = new Point();
-    private Rect mTempIconBounds = new Rect();
 
     private static final Property<BubbleTextView, Float> BADGE_SCALE_PROPERTY
             = new Property<BubbleTextView, Float>(Float.TYPE, "badgeScale") {
@@ -101,19 +80,45 @@
         }
     };
 
-    public static final Property<BubbleTextView, Integer> TEXT_ALPHA_PROPERTY
-            = new Property<BubbleTextView, Integer>(Integer.class, "textAlpha") {
+    public static final Property<BubbleTextView, Float> TEXT_ALPHA_PROPERTY
+            = new Property<BubbleTextView, Float>(Float.class, "textAlpha") {
         @Override
-        public Integer get(BubbleTextView bubbleTextView) {
-            return bubbleTextView.getTextAlpha();
+        public Float get(BubbleTextView bubbleTextView) {
+            return bubbleTextView.mTextAlpha;
         }
 
         @Override
-        public void set(BubbleTextView bubbleTextView, Integer alpha) {
+        public void set(BubbleTextView bubbleTextView, Float alpha) {
             bubbleTextView.setTextAlpha(alpha);
         }
     };
 
+    private final BaseDraggingActivity mActivity;
+    private Drawable mIcon;
+    private final boolean mCenterVertically;
+
+    private final CheckLongPressHelper mLongPressHelper;
+    private final StylusEventHelper mStylusEventHelper;
+    private final float mSlop;
+
+    private final boolean mLayoutHorizontal;
+    private final int mIconSize;
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mIsIconVisible = true;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mTextColor;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private float mTextAlpha = 1;
+
+    private BadgeInfo mBadgeInfo;
+    private BadgeRenderer mBadgeRenderer;
+    private int mBadgeColor;
+    private float mBadgeScale;
+    private boolean mForceHideBadge;
+    private Point mTempSpaceForBadgeOffset = new Point();
+    private Rect mTempIconBounds = new Rect();
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mStayPressed;
     @ViewDebug.ExportedProperty(category = "launcher")
@@ -166,7 +171,7 @@
 
         setEllipsize(TruncateAt.END);
         setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
-
+        setTextAlpha(1f);
     }
 
     @Override
@@ -404,13 +409,17 @@
     @Override
     public void setTextColor(int color) {
         mTextColor = color;
-        super.setTextColor(color);
+        super.setTextColor(getModifiedColor());
     }
 
     @Override
     public void setTextColor(ColorStateList colors) {
         mTextColor = colors.getDefaultColor();
-        super.setTextColor(colors);
+        if (Float.compare(mTextAlpha, 1) == 0) {
+            super.setTextColor(colors);
+        } else {
+            super.setTextColor(getModifiedColor());
+        }
     }
 
     public boolean shouldTextBeVisible() {
@@ -421,19 +430,21 @@
     }
 
     public void setTextVisibility(boolean visible) {
-        if (visible) {
-            super.setTextColor(mTextColor);
-        } else {
-            setTextAlpha(0);
+        setTextAlpha(visible ? 1 : 0);
+    }
+
+    private void setTextAlpha(float alpha) {
+        mTextAlpha = alpha;
+        super.setTextColor(getModifiedColor());
+    }
+
+    private int getModifiedColor() {
+        if (mTextAlpha == 0) {
+            // Special case to prevent text shadows in high contrast mode
+            return Color.TRANSPARENT;
         }
-    }
-
-    public void setTextAlpha(int alpha) {
-        super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
-    }
-
-    private int getTextAlpha() {
-        return Color.alpha(getCurrentTextColor());
+        return ColorUtils.setAlphaComponent(
+                mTextColor, Math.round(Color.alpha(mTextColor) * mTextAlpha));
     }
 
     /**
@@ -441,8 +452,8 @@
      * @param fadeIn Whether the text should fade in or fade out.
      */
     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
-        int toAlpha = shouldTextBeVisible() && fadeIn ? Color.alpha(mTextColor) : 0;
-        return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, toAlpha);
+        float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;
+        return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);
     }
 
     @Override
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 3873a81..1b91e88 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -174,10 +174,6 @@
         return getBounds().height();
     }
 
-    public Bitmap getBitmap() {
-        return mBitmap;
-    }
-
     @Override
     public boolean isStateful() {
         return true;
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index 4eac4a4..e7ca121 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -24,7 +24,8 @@
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.TraceHelper;
+
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 
 /*
  *  This is a helper class that listens to updates from the corresponding animation.
@@ -36,7 +37,6 @@
     private static final String TAG = "FirstFrameAnimatorHlpr";
     private static final boolean DEBUG = false;
     private static final int MAX_DELAY = 1000;
-    private static final int IDEAL_FRAME_DURATION = 16;
     private final View mTarget;
     private long mStartFrame;
     private long mStartTime = -1;
@@ -109,9 +109,9 @@
             // prevents a large jump in the animation due to an expensive first frame
             } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
                        !mAdjustedSecondFrameTime &&
-                       currentTime > mStartTime + IDEAL_FRAME_DURATION &&
-                       currentPlayTime > IDEAL_FRAME_DURATION) {
-                animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
+                       currentTime > mStartTime + SINGLE_FRAME_MS &&
+                       currentPlayTime > SINGLE_FRAME_MS) {
+                animation.setCurrentPlayTime(SINGLE_FRAME_MS);
                 mAdjustedSecondFrameTime = true;
             } else {
                 if (frameNum > 1) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 90c55c9..2c08169 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -81,7 +81,6 @@
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.dynamicui.WallpaperColorInfo;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.FolderIconPreviewVerifier;
 import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -207,6 +206,8 @@
     // UI and state for the overview panel
     private View mOverviewPanel;
 
+    private View mOverviewPanelContainer;
+
     @Thunk boolean mWorkspaceLoading = true;
 
     private OnResumeCallback mOnResumeCallback;
@@ -359,7 +360,7 @@
             dispatchDeviceProfileChanged();
 
             getRootView().dispatchInsets();
-            getStateManager().reapplyState();
+            getStateManager().reapplyState(true /* cancelCurrentAnimation */);
 
             // Recreate touch controllers
             mDragLayer.setup(mDragController);
@@ -736,6 +737,8 @@
         NotificationListener.removeNotificationsChangedListener();
         getStateManager().moveToRestState();
 
+        // Workaround for b/78520668, explicitly trim memory once UI is hidden
+        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
     }
 
     @Override
@@ -786,7 +789,7 @@
         // Refresh shortcuts if the permission changed.
         mModel.refreshShortcutsIfRequired();
 
-        DiscoveryBounce.showIfNeeded(this);
+        DiscoveryBounce.showForHomeIfNeeded(this);
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
@@ -914,6 +917,7 @@
         mWorkspace = mDragLayer.findViewById(R.id.workspace);
         mWorkspace.initParentViews(mDragLayer);
         mOverviewPanel = findViewById(R.id.overview_panel);
+        mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
         mHotseat = findViewById(R.id.hotseat);
         mDragHandleIndicator = findViewById(R.id.drag_indicator);
         mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
@@ -923,7 +927,9 @@
                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
 
         // Setup the drag layer
-        mDragLayer.setup(mDragController);
+        Runnable setupDragLayer = () -> mDragLayer.setup(mDragController);
+        UiFactory.setOnTouchControllersChangedListener(this, setupDragLayer);
+        setupDragLayer.run();
 
         mWorkspace.setup(mDragController);
         // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -1192,6 +1198,10 @@
         return (T) mOverviewPanel;
     }
 
+    public <T extends View> T getOverviewPanelContainer() {
+        return (T) mOverviewPanelContainer;
+    }
+
     public DropTargetBar getDropTargetBar() {
         return mDropTargetBar;
     }
@@ -1270,7 +1280,7 @@
             }
 
             if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onHomeIntent();
+                mLauncherCallbacks.onHomeIntent(internalStateHandled);
             }
         }
 
@@ -1327,6 +1337,8 @@
         unregisterReceiver(mReceiver);
         mWorkspace.removeFolderListeners();
 
+        UiFactory.setOnTouchControllersChangedListener(this, null);
+
         // Stop callbacks from LauncherModel
         // It's possible to receive onDestroy after a new Launcher activity has
         // been created. In this case, don't interfere with the new Launcher.
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 35faaea..6aef658 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -50,7 +50,7 @@
     void onAttachedToWindow();
     void onDetachedFromWindow();
     void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
-    void onHomeIntent();
+    void onHomeIntent(boolean internalStateHandled);
     boolean handleBackPressed();
     void onTrimMemory(int level);
 
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index b1273b6..ad1456a2 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,5 +1,8 @@
 package com.android.launcher3;
 
+import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW;
+
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -13,9 +16,6 @@
 
 import com.android.launcher3.util.Themes;
 
-import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
-import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW;
-
 public class LauncherRootView extends InsettableFrameLayout {
 
     private final Launcher mLauncher;
@@ -82,7 +82,7 @@
             }
         }
         if (resetState) {
-            mLauncher.getStateManager().reapplyState();
+            mLauncher.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
         }
 
         return true; // I'll take it from here
@@ -100,6 +100,7 @@
     }
 
     public void dispatchInsets() {
+        mLauncher.getDeviceProfile().updateInsets(mInsets);
         super.setInsets(mInsets);
     }
 
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index d5e6a9d..d196c37 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -121,7 +121,7 @@
      * @see #goToState(LauncherState, boolean, Runnable)
      */
     public void goToState(LauncherState state) {
-        goToState(state, mLauncher.isStarted() /* animated */, 0, null);
+        goToState(state, !mLauncher.isForceInvisible() && mLauncher.isStarted() /* animated */);
     }
 
     /**
@@ -157,6 +157,13 @@
     }
 
     public void reapplyState() {
+        reapplyState(false);
+    }
+
+    public void reapplyState(boolean cancelCurrentAnimation) {
+        if (cancelCurrentAnimation) {
+            cancelAnimation();
+        }
         if (mConfig.mCurrentAnimation == null) {
             for (StateHandler handler : getStateHandlers()) {
                 handler.setState(mState);
@@ -173,7 +180,7 @@
                     onCompleteRunnable.run();
                 }
                 return;
-            } else if (!mConfig.userControlled && animated) {
+            } else if (!mConfig.userControlled && animated && mConfig.mTargetState == state) {
                 // We are running the same animation as requested
                 if (onCompleteRunnable != null) {
                     mConfig.mCurrentAnimation.addListener(new AnimationSuccessListener() {
@@ -280,7 +287,7 @@
                 onStateTransitionEnd(state);
             }
         });
-        mConfig.setAnimation(animation);
+        mConfig.setAnimation(animation, state);
         return mConfig.mCurrentAnimation;
     }
 
@@ -370,7 +377,7 @@
         if (reapplyNeeded) {
             reapplyState();
         }
-        mConfig.setAnimation(anim);
+        mConfig.setAnimation(anim, null);
     }
 
     private class StartAnimRunnable implements Runnable {
@@ -401,11 +408,13 @@
         private PropertySetter mProperSetter;
 
         private AnimatorSet mCurrentAnimation;
+        private LauncherState mTargetState;
 
         public void reset() {
             duration = 0;
             userControlled = false;
             mProperSetter = null;
+            mTargetState = null;
 
             if (mCurrentAnimation != null) {
                 mCurrentAnimation.setDuration(0);
@@ -429,8 +438,9 @@
             }
         }
 
-        public void setAnimation(AnimatorSet animation) {
+        public void setAnimation(AnimatorSet animation, LauncherState targetState) {
             mCurrentAnimation = animation;
+            mTargetState = targetState;
             mCurrentAnimation.addListener(this);
         }
     }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 15bf76d..8311ab9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -203,7 +203,6 @@
         if (mPageIndicatorViewId > -1) {
             mPageIndicator = parent.findViewById(mPageIndicatorViewId);
             mPageIndicator.setMarkersCount(getChildCount());
-            mPageIndicator.setPageDescription(getPageIndicatorDescription());
         }
     }
 
@@ -310,7 +309,6 @@
 
     private void updatePageIndicator() {
         if (mPageIndicator != null) {
-            mPageIndicator.setPageDescription(getPageIndicatorDescription());
             mPageIndicator.setActiveMarker(getNextPage());
         }
     }
@@ -609,18 +607,18 @@
         final int endIndex = mIsRtl ? -1 : childCount;
         final int delta = mIsRtl ? -1 : 1;
 
-        int verticalPadding = getPaddingTop() + getPaddingBottom();
+        final int verticalCenter = (getPaddingTop() + getMeasuredHeight() + mInsets.top
+                - mInsets.bottom - getPaddingBottom()) / 2;
 
-        int scrollOffsetLeft = mInsets.left + getPaddingLeft();
-        int childLeft = scrollOffsetLeft;
+        final int scrollOffsetLeft = mInsets.left + getPaddingLeft();
         boolean pageScrollChanged = false;
 
-        for (int i = startIndex; i != endIndex; i += delta) {
+        for (int i = startIndex, childLeft = scrollOffsetLeft + offsetForPageScrolls();
+                i != endIndex;
+                i += delta) {
             final View child = getPageAt(i);
             if (scrollLogic.shouldIncludeView(child)) {
-                int childTop = getPaddingTop() + mInsets.top;
-                childTop += (getMeasuredHeight() - mInsets.top - mInsets.bottom - verticalPadding
-                        - child.getMeasuredHeight()) / 2;
+                final int childTop = verticalCenter - child.getMeasuredHeight() / 2;
                 final int childWidth = child.getMeasuredWidth();
 
                 if (layoutChildren) {
@@ -659,6 +657,10 @@
         }
     }
 
+    protected int offsetForPageScrolls() {
+        return 0;
+    }
+
     public void setPageSpacing(int pageSpacing) {
         mPageSpacing = pageSpacing;
         requestLayout();
@@ -695,7 +697,11 @@
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
         int page = indexToPage(indexOfChild(child));
         if (page != mCurrentPage || !mScroller.isFinished()) {
-            snapToPage(page);
+            if (immediate) {
+                setCurrentPage(page);
+            } else {
+                snapToPage(page);
+            }
             return true;
         }
         return false;
@@ -1537,10 +1543,6 @@
         return false;
     }
 
-    protected String getPageIndicatorDescription() {
-        return getCurrentPageDescription();
-    }
-
     protected boolean canAnnouncePageDescription() {
         return true;
     }
@@ -1558,11 +1560,6 @@
         return mDownMotionY;
     }
 
-    @Override
-    public boolean onHoverEvent(android.view.MotionEvent event) {
-        return true;
-    }
-
     protected interface ComputePageScrollsLogic {
 
         boolean shouldIncludeView(View view);
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 024b4eb..7870af9 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -52,7 +52,7 @@
 
     private final Alarm mCacheExpireAlarm;
 
-    private int mCurrentAccessibilityAction = -1;
+    protected int mCurrentAccessibilityAction = -1;
     public SecondaryDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -70,7 +70,7 @@
         setupUi(UNINSTALL);
     }
 
-    private void setupUi(int action) {
+    protected void setupUi(int action) {
         if (action == mCurrentAccessibilityAction) {
             return;
         }
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index c9bd32b..32c198a 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
+import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
+
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.AlertDialog;
@@ -59,6 +62,7 @@
     private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
 
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+    private static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
     private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
@@ -123,6 +127,16 @@
                     getPreferenceScreen().removePreference(iconShapeOverride);
                 }
             }
+
+            // Setup allow rotation preference
+            Preference rotationPref = findPreference(ALLOW_ROTATION_PREFERENCE_KEY);
+            if (getResources().getBoolean(R.bool.allow_rotation)) {
+                // Launcher supports rotation by default. No need to show this setting.
+                getPreferenceScreen().removePreference(rotationPref);
+            } else {
+                // Initialize the UI once
+                rotationPref.setDefaultValue(getAllowRotationDefaultValue());
+            }
         }
 
         @Override
@@ -273,9 +287,13 @@
         @Override
         public void onClick(DialogInterface dialogInterface, int i) {
             ComponentName cn = new ComponentName(getActivity(), NotificationListener.class);
+            Bundle showFragmentArgs = new Bundle();
+            showFragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString());
+
             Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)
                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-                    .putExtra(":settings:fragment_args_key", cn.flattenToString());
+                    .putExtra(EXTRA_FRAGMENT_ARG_KEY, cn.flattenToString())
+                    .putExtra(EXTRA_SHOW_FRAGMENT_ARGS, showFragmentArgs);
             getActivity().startActivity(intent);
         }
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ba96d4a..006dc95 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -35,6 +35,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
 import android.os.PowerManager;
 import android.os.TransactionTooLargeException;
 import android.support.v4.os.BuildCompat;
@@ -101,6 +103,8 @@
     public static final boolean ATLEAST_LOLLIPOP_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
 
+    public static final int SINGLE_FRAME_MS = 16;
+
     /**
      * Indicates if the device has a debug build. Should only be used to store additional info or
      * add extra logging and not for changing the app behavior.
@@ -575,4 +579,12 @@
         return hashSet;
     }
 
+    /**
+     * Utility method to post a runnable on the handler, skipping the synchronization barriers.
+     */
+    public static void postAsyncCallback(Handler handler, Runnable callback) {
+        Message msg = Message.obtain(handler, callback);
+        msg.setAsynchronous(true);
+        handler.sendMessage(msg);
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index a658d58..7af4bf9 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -338,7 +338,8 @@
         int previewWidth;
         int previewHeight;
 
-        if (widgetPreviewExists) {
+        if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0
+                && drawable.getIntrinsicHeight() > 0) {
             previewWidth = drawable.getIntrinsicWidth();
             previewHeight = drawable.getIntrinsicHeight();
         } else {
@@ -358,8 +359,8 @@
             scale = maxPreviewWidth / (float) (previewWidth);
         }
         if (scale != 1f) {
-            previewWidth = (int) (scale * previewWidth);
-            previewHeight = (int) (scale * previewHeight);
+            previewWidth = Math.max((int)(scale * previewWidth), 1);
+            previewHeight = Math.max((int)(scale * previewHeight), 1);
         }
 
         // If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 1e2e3b1..b5a603e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -476,7 +476,7 @@
         super.onViewAdded(child);
     }
 
-    boolean isTouchActive() {
+    public boolean isTouchActive() {
         return mTouchState != TOUCH_STATE_REST;
     }
 
@@ -973,19 +973,9 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        switch (ev.getAction() & MotionEvent.ACTION_MASK) {
-        case MotionEvent.ACTION_DOWN:
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
             mXDown = ev.getX();
             mYDown = ev.getY();
-            break;
-        case MotionEvent.ACTION_POINTER_UP:
-        case MotionEvent.ACTION_UP:
-            if (mTouchState == TOUCH_STATE_REST) {
-                final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
-                if (currentPage != null) {
-                    onWallpaperTap(ev);
-                }
-            }
         }
         return super.onInterceptTouchEvent(ev);
     }
@@ -1442,7 +1432,7 @@
         }
     }
 
-    protected void onWallpaperTap(MotionEvent ev) {
+    public void onWallpaperTap(MotionEvent ev) {
         final int[] position = mTempXY;
         getLocationOnScreen(position);
 
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 4398f6e..e1cb4b8 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -94,6 +94,10 @@
                 launcher.getText(R.string.action_deep_shortcut)));
     }
 
+    public void addAccessibilityAction(int action, int actionLabel) {
+        mActions.put(action, new AccessibilityAction(action, mLauncher.getText(actionLabel)));
+    }
+
     @Override
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(host, info);
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index b2e35a4..69068c6 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -76,4 +76,9 @@
             super.determineScrollingStart(ev);
         }
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index a7447b7..a6c1346 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -415,4 +415,8 @@
                 y + mEmptySearchBackground.getIntrinsicHeight());
     }
 
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index fddafb2..f73916c 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -17,35 +17,44 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
 import android.app.ActivityManager;
-import android.content.Context;
+import android.os.Handler;
 import android.view.MotionEvent;
+import android.view.animation.PathInterpolator;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.states.InternalStateHandler;
 
 /**
- * Floating view responsible for showing discovery bounce animation
+ * Abstract base class of floating view responsible for showing discovery bounce animation
  */
 public class DiscoveryBounce extends AbstractFloatingView {
 
-    public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
+    private static final long DELAY_MS = 200;
+
+    public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
+    public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
 
     private final Launcher mLauncher;
     private final Animator mDiscoBounceAnimation;
 
-    public DiscoveryBounce(Launcher launcher) {
+    public DiscoveryBounce(Launcher launcher, Animator animator) {
         super(launcher, null);
         mLauncher = launcher;
 
-        mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher,
-                R.animator.discovery_bounce);
+        mDiscoBounceAnimation = animator;
         AllAppsTransitionController controller = mLauncher.getAllAppsController();
         mDiscoBounceAnimation.setTarget(controller);
         mDiscoBounceAnimation.addListener(controller.getProgressAnimatorListener());
@@ -96,16 +105,72 @@
         return (type & TYPE_ON_BOARD_POPUP) != 0;
     }
 
-    public static void showIfNeeded(Launcher launcher) {
+    public static void showForHomeIfNeeded(Launcher launcher) {
+        showForHomeIfNeeded(launcher, true);
+    }
+
+    private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
         if (!launcher.isInState(NORMAL)
-                || launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)
+                || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
                 || AbstractFloatingView.getTopOpenView(launcher) != null
                 || UserManagerCompat.getInstance(launcher).isDemoUser()
                 || ActivityManager.isRunningInTestHarness()) {
             return;
         }
 
-        DiscoveryBounce view = new DiscoveryBounce(launcher);
+        if (withDelay) {
+            new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS);
+            return;
+        }
+
+        DiscoveryBounce view = new DiscoveryBounce(launcher,
+                AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce));
+        view.mIsOpen = true;
+        launcher.getDragLayer().addView(view);
+    }
+
+    public static void showForOverviewIfNeeded(Launcher launcher) {
+        showForOverviewIfNeeded(launcher, true);
+    }
+
+    private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
+        if (!launcher.isInState(OVERVIEW)
+                || !launcher.hasBeenResumed()
+                || launcher.isForceInvisible()
+                || launcher.getDeviceProfile().isVerticalBarLayout()
+                || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
+                || UserManagerCompat.getInstance(launcher).isDemoUser()
+                || ActivityManager.isRunningInTestHarness()) {
+            return;
+        }
+
+        if (withDelay) {
+            new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
+            return;
+        } else if (InternalStateHandler.hasPending()
+                || AbstractFloatingView.getTopOpenView(launcher) != null) {
+            // TODO: Move these checks to the top and call this method after invalidate handler.
+            return;
+        }
+
+        float verticalProgress = OVERVIEW.getVerticalProgress(launcher);
+
+        TimeInterpolator pathInterpolator = new PathInterpolator(0.35f, 0, 0.5f, 1);
+        Keyframe keyframe3 = Keyframe.ofFloat(0.423f, verticalProgress - (1 - 0.9438f));
+        keyframe3.setInterpolator(pathInterpolator);
+        Keyframe keyframe4 = Keyframe.ofFloat(0.654f, verticalProgress);
+        keyframe4.setInterpolator(pathInterpolator);
+
+        PropertyValuesHolder propertyValuesHolder = PropertyValuesHolder.ofKeyframe("progress",
+                Keyframe.ofFloat(0, verticalProgress),
+                Keyframe.ofFloat(0.346f, verticalProgress), keyframe3, keyframe4,
+                Keyframe.ofFloat(1f, verticalProgress));
+        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(null,
+                new PropertyValuesHolder[]{propertyValuesHolder});
+        animator.setDuration(2166);
+        animator.setRepeatCount(5);
+
+        DiscoveryBounce view = new DiscoveryBounce(launcher, animator);
         view.mIsOpen = true;
         launcher.getDragLayer().addView(view);
     }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 461f5b5..378450e 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -237,6 +237,11 @@
     public boolean hasVisibleContent() {
         return false;
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
 
 
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index a069d5d..a916697 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -25,6 +25,7 @@
 import android.view.View;
 import android.widget.Button;
 import android.widget.LinearLayout;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -169,8 +170,7 @@
     public void setMarkersCount(int numMarkers) { }
 
     @Override
-    public void setPageDescription(CharSequence description) {
-        // We don't want custom page description as the tab-bar already has two tabs with their
-        // own descriptions.
+    public boolean hasOverlappingRendering() {
+        return false;
     }
 }
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 087752d..1dba7d6 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -17,6 +17,7 @@
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
@@ -52,45 +53,37 @@
     private final long mDuration;
 
     protected final AnimatorSet mAnim;
-    private AnimatorSet mOriginalTarget;
 
     protected float mCurrentFraction;
     private Runnable mEndAction;
 
+    protected boolean mTargetCancelled = false;
+
     protected AnimatorPlaybackController(AnimatorSet anim, long duration) {
         mAnim = anim;
-        mOriginalTarget = mAnim;
         mDuration = duration;
 
         mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
         mAnimationPlayer.setInterpolator(Interpolators.LINEAR);
         mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
         mAnimationPlayer.addUpdateListener(this);
+
+        mAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mTargetCancelled = true;
+            }
+        });
     }
 
     public AnimatorSet getTarget() {
         return mAnim;
     }
 
-    public void setOriginalTarget(AnimatorSet anim) {
-        mOriginalTarget = anim;
-    }
-
-    public AnimatorSet getOriginalTarget() {
-        return mOriginalTarget;
-    }
-
     public long getDuration() {
         return mDuration;
     }
 
-    public AnimatorPlaybackController cloneFor(AnimatorSet anim) {
-        AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(anim, mDuration);
-        controller.setOriginalTarget(mOriginalTarget);
-        controller.setPlayFraction(mCurrentFraction);
-        return controller;
-    }
-
     /**
      * Starts playing the animation forward from current position.
      */
@@ -206,6 +199,11 @@
         @Override
         public void setPlayFraction(float fraction) {
             mCurrentFraction = fraction;
+            // Let the animator report the progress but don't apply the progress to child
+            // animations if it has been cancelled.
+            if (mTargetCancelled) {
+                return;
+            }
             long playPos = clampDuration(fraction);
             for (ValueAnimator anim : mChildAnimations) {
                 anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
index 00258c7..6605ace 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompat.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
@@ -31,7 +31,7 @@
             if (sInstance == null) {
                 context = context.getApplicationContext();
 
-                if (Utilities.ATLEAST_OREO) {
+                if (Utilities.ATLEAST_OREO_MR1) {
                     try {
                         sInstance = new WallpaperManagerCompatVOMR1(context);
                     } catch (Throwable e) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 12d7dc7..99c800d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -39,6 +39,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
@@ -507,16 +508,9 @@
         // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
         mDeleteFolderOnDropCompleted = false;
 
-        final Runnable onCompleteRunnable;
         centerAboutIcon();
 
         AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
-        onCompleteRunnable = new Runnable() {
-            @Override
-            public void run() {
-                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
-            }
-        };
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
@@ -532,7 +526,7 @@
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
 
-                onCompleteRunnable.run();
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
                 mContent.setFocusOnFirstChild();
             }
         });
@@ -614,9 +608,6 @@
             mFolderIcon.clearLeaveBehindIfExists();
         }
 
-        if (!(getParent() instanceof DragLayer)) return;
-        DragLayer parent = (DragLayer) getParent();
-
         if (animate) {
             animateClosed();
         } else {
@@ -625,7 +616,8 @@
 
         // Notify the accessibility manager that this folder "window" has disappeared and no
         // longer occludes the workspace items
-        parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        mLauncher.getDragLayer().sendAccessibilityEvent(
+                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
     private void animateClosed() {
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index ec448e9..9ae3775 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.folder;
 
+import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 
@@ -172,9 +173,8 @@
         AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
 
         // Initialize the Folder items' text.
-        PropertyResetListener colorResetListener = new PropertyResetListener<>(
-                BubbleTextView.TEXT_ALPHA_PROPERTY,
-                Color.alpha(Themes.getAttrColor(mContext, android.R.attr.textColorSecondary)));
+        PropertyResetListener colorResetListener =
+                new PropertyResetListener<>(TEXT_ALPHA_PROPERTY, 1f);
         for (BubbleTextView icon : mFolder.getItemsOnPage(mFolder.mContent.getCurrentPage())) {
             if (mIsOpening) {
                 icon.setTextVisibility(false);
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 680c020..81f3f90 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -80,6 +80,7 @@
     private final float[] mLeftBorder;
     private final float[] mRightBorder;
     private final Rect mBounds;
+    private final Path mShapePath;
     private final Matrix mMatrix;
 
     private final Paint mPaintIcon;
@@ -116,6 +117,7 @@
         mPaintMaskShapeOutline.setColor(Color.BLACK);
         mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
 
+        mShapePath = new Path();
         mMatrix = new Matrix();
         mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
     }
@@ -146,13 +148,13 @@
         mMatrix.reset();
         mMatrix.setScale(mBounds.width(), mBounds.height());
         mMatrix.postTranslate(mBounds.left, mBounds.top);
-        maskPath.transform(mMatrix);
+        maskPath.transform(mMatrix, mShapePath);
 
         // XOR operation
-        mCanvasARGB.drawPath(maskPath, mPaintMaskShape);
+        mCanvasARGB.drawPath(mShapePath, mPaintMaskShape);
 
         // DST_OUT operation around the mask path outline
-        mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline);
+        mCanvasARGB.drawPath(mShapePath, mPaintMaskShapeOutline);
 
         // Check if the result is almost transparent
         return isTransparentBitmap(mBitmapARGB);
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 1fd7078..b527b6a 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -150,22 +150,12 @@
     public void onCreate() {
         super.onCreate();
         sIsCreated = true;
-        mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
-            @Override
-            public void onSettingChanged(boolean isNotificationBadgingEnabled) {
-                if (!isNotificationBadgingEnabled) {
-                    requestUnbind();
-                }
-            }
-        };
-        mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         sIsCreated = false;
-        mNotificationBadgingObserver.unregister();
     }
 
     public static @Nullable NotificationListener getInstanceIfConnected() {
@@ -203,6 +193,17 @@
     public void onListenerConnected() {
         super.onListenerConnected();
         sIsConnected = true;
+
+        mNotificationBadgingObserver = new SettingsObserver.Secure(getContentResolver()) {
+            @Override
+            public void onSettingChanged(boolean isNotificationBadgingEnabled) {
+                if (!isNotificationBadgingEnabled) {
+                    requestUnbind();
+                }
+            }
+        };
+        mNotificationBadgingObserver.register(NOTIFICATION_BADGING);
+
         onNotificationFullRefresh();
     }
 
@@ -214,11 +215,16 @@
     public void onListenerDisconnected() {
         super.onListenerDisconnected();
         sIsConnected = false;
+        mNotificationBadgingObserver.unregister();
     }
 
     @Override
     public void onNotificationPosted(final StatusBarNotification sbn) {
         super.onNotificationPosted(sbn);
+        if (sbn == null) {
+            // There is a bug in platform where we can get a null notification; just ignore it.
+            return;
+        }
         mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, new NotificationPostedMsg(sbn))
             .sendToTarget();
         if (sStatusBarNotificationsChangedListener != null) {
@@ -244,6 +250,10 @@
     @Override
     public void onNotificationRemoved(final StatusBarNotification sbn) {
         super.onNotificationRemoved(sbn);
+        if (sbn == null) {
+            // There is a bug in platform where we can get a null notification; just ignore it.
+            return;
+        }
         Pair<PackageUserKey, NotificationKeyData> packageUserKeyAndNotificationKey
             = new Pair<>(PackageUserKey.fromNotification(sbn),
             NotificationKeyData.fromNotification(sbn));
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 3ce7291..8fafb6f 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -25,6 +25,4 @@
     void setActiveMarker(int activePage);
 
     void setMarkersCount(int numMarkers);
-
-    void setPageDescription(CharSequence description);
 }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 524ec3c..709975f 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -228,11 +228,6 @@
     }
 
     @Override
-    public void setPageDescription(CharSequence description) {
-        setContentDescription(description);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Add extra spacing of mDotRadius on all sides so than entry animation could be run.
         int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 4ad7feb..3c16cde 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -186,11 +186,6 @@
         }
     }
 
-    @Override
-    public void setPageDescription(CharSequence description) {
-        setContentDescription(description);
-    }
-
     public void setShouldAutoHide(boolean shouldAutoHide) {
         mShouldAutoHide = shouldAutoHide;
         if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index 0a2c3e4..cf7c6ba 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -60,6 +60,10 @@
         return sScheduler.clearReference(this);
     }
 
+    public static boolean hasPending() {
+        return sScheduler.hasPending();
+    }
+
     public static boolean handleCreate(Launcher launcher, Intent intent) {
         return handleIntent(launcher, intent, false, false);
     }
@@ -132,5 +136,9 @@
             }
             return false;
         }
+
+        public boolean hasPending() {
+            return mPendingHandler.get() != null;
+        }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 8f83648..0036bb9 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,28 +18,43 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.provider.Settings.System.ACCELEROMETER_ROTATION;
-import static android.provider.Settings.System.getUriFor;
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+
+import static com.android.launcher3.Utilities.ATLEAST_NOUGAT;
 
 import android.app.Activity;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
 
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 
 /**
  * Utility class to manage launcher rotation
  */
-public class RotationHelper extends ContentObserver {
+public class RotationHelper implements OnSharedPreferenceChangeListener {
+
+    public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
+
+    public static boolean getAllowRotationDefaultValue() {
+        if (ATLEAST_NOUGAT) {
+            // If the device was scaled, used the original dimensions to determine if rotation
+            // is allowed of not.
+            Resources res = Resources.getSystem();
+            int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
+                    * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
+            return originalSmallestWidth >= 600;
+        }
+        return false;
+    }
 
     public static final int REQUEST_NONE = 0;
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
 
     private final Activity mActivity;
-    private final ContentResolver mCr;
+    private final SharedPreferences mPrefs;
 
     private final boolean mIgnoreAutoRotateSettings;
     private boolean mAutoRotateEnabled;
@@ -60,23 +75,24 @@
     private int mLastActivityFlags = -1;
 
     public RotationHelper(Activity activity) {
-        super(new Handler());
         mActivity = activity;
 
         // On large devices we do not handle auto-rotate differently.
         mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
         if (!mIgnoreAutoRotateSettings) {
-            mCr = mActivity.getContentResolver();
-            mCr.registerContentObserver(getUriFor(ACCELEROMETER_ROTATION), false, this);
-            mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+            mPrefs = Utilities.getPrefs(mActivity);
+            mPrefs.registerOnSharedPreferenceChangeListener(this);
+            mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+                    getAllowRotationDefaultValue());
         } else {
-            mCr = null;
+            mPrefs = null;
         }
     }
 
     @Override
-    public void onChange(boolean selfChange) {
-        mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+        mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+                getAllowRotationDefaultValue());
         notifyChange();
     }
 
@@ -104,8 +120,8 @@
     public void destroy() {
         if (!mDestroyed) {
             mDestroyed = true;
-            if (mCr != null) {
-                mCr.unregisterContentObserver(this);
+            if (mPrefs != null) {
+                mPrefs.unregisterOnSharedPreferenceChangeListener(this);
             }
         }
     }
@@ -121,19 +137,17 @@
                     SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
         } else if (mCurrentStateRequest == REQUEST_LOCK) {
             activityFlags = SCREEN_ORIENTATION_LOCKED;
-        } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE) {
+        } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE
+                || mAutoRotateEnabled) {
             activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
-        } else if (mAutoRotateEnabled) {
-            // If auto rotation is on, lock to device orientation
-            activityFlags = SCREEN_ORIENTATION_NOSENSOR;
         } else {
             // If auto rotation is off, allow rotation on the activity, in case the user is using
             // forced rotation.
-            activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
+            activityFlags = SCREEN_ORIENTATION_NOSENSOR;
         }
         if (activityFlags != mLastActivityFlags) {
             mLastActivityFlags = activityFlags;
-            mActivity.setRequestedOrientation(mLastActivityFlags);
+            mActivity.setRequestedOrientation(activityFlags);
         }
     }
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index f1195ed..c0ad110 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.touch;
 
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 
 import android.animation.Animator;
@@ -40,7 +41,6 @@
 
     private static final String TAG = "ASCTouchController";
     public static final float RECATCH_REJECTION_FRACTION = .0875f;
-    public static final int SINGLE_FRAME_MS = 16;
 
     // Progress after which the transition is assumed to be a success in case user does not fling
     public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
@@ -279,7 +279,7 @@
 
     @Override
     public void onAnimationCancel(Animator animation) {
-        if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) {
+        if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
             Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
             clearState();
         }
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 23f55aa..f59f14e 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -17,6 +17,7 @@
 
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.ViewConfiguration.getLongPressTimeout;
 
@@ -30,6 +31,7 @@
 import android.view.View.OnTouchListener;
 
 import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Workspace;
@@ -71,8 +73,7 @@
         int action = ev.getActionMasked();
         if (action == ACTION_DOWN) {
             // Check if we can handle long press.
-            boolean handleLongPress = AbstractFloatingView.getTopOpenView(mLauncher) == null
-                    && mLauncher.isInState(NORMAL);
+            boolean handleLongPress = canHandleLongPress();
 
             if (handleLongPress) {
                 // Check if the event is not near the edges
@@ -122,12 +123,28 @@
             // We don't want to handle touch, let workspace handle it as usual.
             result = false;
         }
+
+        if (action == ACTION_UP || action == ACTION_POINTER_UP) {
+            if (!mWorkspace.isTouchActive()) {
+                final CellLayout currentPage =
+                        (CellLayout) mWorkspace.getChildAt(mWorkspace.getCurrentPage());
+                if (currentPage != null) {
+                    mWorkspace.onWallpaperTap(ev);
+                }
+            }
+        }
+
         if (action == ACTION_UP || action == ACTION_CANCEL) {
             cancelLongPress();
         }
         return result;
     }
 
+    private boolean canHandleLongPress() {
+        return AbstractFloatingView.getTopOpenView(mLauncher) == null
+                && mLauncher.isInState(NORMAL);
+    }
+
     private void cancelLongPress() {
         mWorkspace.removeCallbacks(this);
         mLongPressState = STATE_CANCELLED;
@@ -136,15 +153,19 @@
     @Override
     public void run() {
         if (mLongPressState == STATE_REQUESTED) {
-            mLongPressState = STATE_PENDING_PARENT_INFORM;
-            mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
+            if (canHandleLongPress()) {
+                mLongPressState = STATE_PENDING_PARENT_INFORM;
+                mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
 
-            mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-            mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
-                    Action.Direction.NONE, ContainerType.WORKSPACE,
-                    mWorkspace.getCurrentPage());
-            OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
+                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+                mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+                        Action.Direction.NONE, ContainerType.WORKSPACE,
+                        mWorkspace.getCurrentPage());
+                OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
+            } else {
+                cancelLongPress();
+            }
         }
     }
 }
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 489e59e..149b38b 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -34,6 +34,8 @@
 
 import java.util.ArrayList;
 
+import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+
 /**
  * A viewgroup with utility methods for drag-n-drop and touch interception
  */
@@ -119,6 +121,21 @@
     }
 
     @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        if (child instanceof AbstractFloatingView) {
+            // Handles the case where the view is removed without being properly closed.
+            // This can happen if something goes wrong during a state change/transition.
+            postDelayed(() -> {
+                AbstractFloatingView floatingView = (AbstractFloatingView) child;
+                if (floatingView.isOpen()) {
+                    floatingView.close(false);
+                }
+            }, SINGLE_FRAME_MS);
+        }
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
         if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 1cd6699..05bab8b 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -372,4 +372,11 @@
         }
         return sTempRect.contains((int) x, (int) y);
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        // There is actually some overlap between the track and the thumb. But since the track
+        // alpha is so low, it does not matter.
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 090b3e6..a508191 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -24,7 +24,6 @@
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.EdgeEffectFactory;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.SparseBooleanArray;
 import android.view.View;
 import android.widget.EdgeEffect;
@@ -58,6 +57,7 @@
     private final SpringAnimation mSpring;
 
     private float mDampedScrollShift = 0;
+    private SpringEdgeEffect mActiveEdge;
 
     public SpringRelativeLayout(Context context) {
         this(context, null);
@@ -90,6 +90,13 @@
         return super.drawChild(canvas, child, drawingTime);
     }
 
+    private void setActiveEdge(SpringEdgeEffect edge) {
+        if (mActiveEdge != edge && mActiveEdge != null) {
+            mActiveEdge.mDistance = 0;
+        }
+        mActiveEdge = edge;
+    }
+
     private void setDampedScrollShift(float shift) {
         if (shift != mDampedScrollShift) {
             mDampedScrollShift = shift;
@@ -144,6 +151,7 @@
 
         @Override
         public void onPull(float deltaDistance, float displacement) {
+            setActiveEdge(this);
             mDistance += deltaDistance * (mVelocityMultiplier / 3f);
             setDampedScrollShift(mDistance * getHeight());
         }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
index 49a9dc7..6366b2d 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
@@ -16,7 +16,7 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.APPS_VIEW_SHOWN;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 
 import android.view.View;
@@ -49,8 +49,8 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        if (!launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)) {
-            launcher.getSharedPrefs().edit().putBoolean(APPS_VIEW_SHOWN, true).apply();
+        if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+            launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
         }
 
         AbstractFloatingView.closeAllOpenViews(launcher);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index be9d5b7..bd1a96e 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.uioverrides;
 
+import android.content.Context;
+
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.util.TouchController;
@@ -27,6 +29,8 @@
                 launcher.getDragController(), new AllAppsSwipeController(launcher)};
     }
 
+    public static void setOnTouchControllersChangedListener(Context context, Runnable listener) { }
+
     public static StateHandler[] getStateHandler(Launcher launcher) {
         return new StateHandler[] {
                 launcher.getAllAppsController(), launcher.getWorkspace() };
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index a54268a..af8b15c 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -18,8 +18,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3.tests">
 
-    <uses-sdk android:targetSdkVersion="25" android:minSdkVersion="21"/>
-
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />