Merge "Handle null small icon" into ub-launcher3-dorval-polish2
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index bcb522b..23bddf6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -70,7 +70,7 @@
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
-            android:windowSoftInputMode="adjustPan|stateUnchanged"
+            android:windowSoftInputMode="adjustPan"
             android:screenOrientation="nosensor"
             android:configChanges="keyboard|keyboardHidden|navigation"
             android:resizeableActivity="true"
@@ -82,6 +82,7 @@
                 <category android:name="android.intent.category.HOME" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.MONKEY"/>
+                <category android:name="android.intent.category.LAUNCHER_APP" />
             </intent-filter>
         </activity>
 
diff --git a/go/res/values-v26/bools.xml b/go/res/values-v26/bools.xml
new file mode 100644
index 0000000..cc4a7ba
--- /dev/null
+++ b/go/res/values-v26/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <bool name="notification_badging_enabled">false</bool>
+</resources>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/go/res/values/dimens.xml
new file mode 100644
index 0000000..f1b1053
--- /dev/null
+++ b/go/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <!-- Dynamic Grid -->
+    <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/go/res/values/override.xml b/go/res/values/override.xml
new file mode 100644
index 0000000..268cb98
--- /dev/null
+++ b/go/res/values/override.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2017 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources>
+    <!-- String representing the intent to delete a package. -->
+    <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;B.android.intent.extra.RETURN_RESULT=true;end</string>
+</resources>
\ No newline at end of file
diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml
index 094fc74..487c026 100644
--- a/go/res/xml/device_profiles.xml
+++ b/go/res/xml/device_profiles.xml
@@ -25,7 +25,7 @@
         launcher:numColumns="4"
         launcher:numFolderRows="4"
         launcher:numFolderColumns="4"
-        launcher:iconSize="56"
+        launcher:iconSize="60"
         launcher:iconTextSize="14.0"
         launcher:defaultLayoutId="@xml/default_workspace_4x4"
         />
diff --git a/res/anim/discovery_bounce.xml b/res/anim/discovery_bounce.xml
deleted file mode 100644
index 1f7d466..0000000
--- a/res/anim/discovery_bounce.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** 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.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:ordering="sequentially"
-        android:startOffset="200">
-
-    <objectAnimator
-        android:propertyName="progress"
-        android:duration="250"
-        android:interpolator="@interpolator/disco_bounce_section1"
-        android:valueFrom="1f"
-        android:valueTo=".94f"
-        android:valueType="floatType"/>
-    <objectAnimator
-        android:propertyName="progress"
-        android:duration="216"
-        android:interpolator="@interpolator/disco_bounce_section2"
-        android:valueFrom=".94f"
-        android:valueTo="1.012f"
-        android:valueType="floatType"/>
-    <objectAnimator
-        android:propertyName="progress"
-        android:duration="234"
-        android:interpolator="@interpolator/disco_bounce_section3"
-        android:valueFrom="1.012f"
-        android:valueTo="1f"
-        android:valueType="floatType"/>
-</set>
diff --git a/res/animator-v23/discovery_bounce.xml b/res/animator-v23/discovery_bounce.xml
new file mode 100644
index 0000000..8d0e8fd
--- /dev/null
+++ b/res/animator-v23/discovery_bounce.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="2166"
+    android:repeatCount="5">
+    <propertyValuesHolder
+        android:propertyName="progress"
+        android:valueType="floatType">
+        <keyframe
+            android:fraction="0"
+            android:value="1f" />
+        <keyframe
+            android:fraction="0.346"
+            android:value="1f" />
+        <keyframe
+            android:fraction=".423"
+            android:interpolator="@interpolator/disco_bounce"
+            android:value="0.9438f" />
+        <keyframe
+            android:fraction="0.654"
+            android:interpolator="@interpolator/disco_bounce"
+            android:value="1f" />
+        <keyframe
+            android:fraction="1"
+            android:value="1f" />
+    </propertyValuesHolder>
+</objectAnimator>
diff --git a/res/animator/discovery_bounce.xml b/res/animator/discovery_bounce.xml
new file mode 100644
index 0000000..f02ebdb
--- /dev/null
+++ b/res/animator/discovery_bounce.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:ordering="sequentially">
+    <objectAnimator
+        android:duration="166"
+        android:interpolator="@interpolator/disco_bounce"
+        android:propertyName="progress"
+        android:startOffset="750"
+        android:valueFrom="1f"
+        android:valueTo="0.9438f"
+        android:valueType="floatType" />
+    <objectAnimator
+        android:duration="500"
+        android:interpolator="@interpolator/disco_bounce"
+        android:propertyName="progress"
+        android:valueTo="1f"
+        android:valueType="floatType" />
+</set>
diff --git a/res/drawable/ic_instant_app_badge.xml b/res/drawable/ic_instant_app_badge.xml
new file mode 100644
index 0000000..cc53230
--- /dev/null
+++ b/res/drawable/ic_instant_app_badge.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/profile_badge_size"
+    android:height="@dimen/profile_badge_size"
+    android:viewportWidth="18"
+    android:viewportHeight="18">
+
+    <path
+        android:fillColor="@android:color/black"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/black"
+        android:fillAlpha="0.87"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
+</vector>
\ No newline at end of file
diff --git a/res/interpolator/disco_bounce_section1.xml b/res/interpolator/disco_bounce.xml
similarity index 81%
rename from res/interpolator/disco_bounce_section1.xml
rename to res/interpolator/disco_bounce.xml
index 2156216..6ec339e 100644
--- a/res/interpolator/disco_bounce_section1.xml
+++ b/res/interpolator/disco_bounce.xml
@@ -16,7 +16,7 @@
   -->
 
 <pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.9"
-                  android:controlY1="0"
-                  android:controlX2="0.5"
-                  android:controlY2="1"/>
+    android:controlX1="0.35"
+    android:controlY1="0"
+    android:controlX2="0.5"
+    android:controlY2="1" />
diff --git a/res/interpolator/disco_bounce_section2.xml b/res/interpolator/disco_bounce_section2.xml
deleted file mode 100644
index 86cc789..0000000
--- a/res/interpolator/disco_bounce_section2.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2016 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.9"
-                  android:controlY1="0"
-                  android:controlX2="0.6"
-                  android:controlY2="1"/>
diff --git a/res/interpolator/disco_bounce_section3.xml b/res/interpolator/disco_bounce_section3.xml
deleted file mode 100644
index 1acef03..0000000
--- a/res/interpolator/disco_bounce_section3.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2016 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.1"
-                  android:controlY1="0"
-                  android:controlX2="0.7"
-                  android:controlY2="1"/>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 6340619..39df2b1 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -68,11 +68,10 @@
 
     </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
     <View
-        style="@style/AllAppsNavBarProtection"
         android:id="@+id/nav_bar_bg"
+        android:background="?attr/allAppsNavBarScrimColor"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_gravity="bottom"
-        android:focusable="false"
-        android:visibility="invisible" />
+        android:focusable="false"  />
 </com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/notification.xml b/res/layout/notification.xml
index 4a02aa1..1eebb43 100644
--- a/res/layout/notification.xml
+++ b/res/layout/notification.xml
@@ -49,7 +49,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:layout_gravity="start"
-                android:gravity="bottom"
+                android:gravity="center_vertical"
                 android:text="@string/notifications_header"
                 android:textSize="@dimen/notification_header_text_size"
                 android:textColor="?android:attr/textColorPrimary" />
@@ -58,7 +58,7 @@
                 android:layout_width="@dimen/notification_icon_size"
                 android:layout_height="match_parent"
                 android:layout_gravity="end"
-                android:gravity="bottom|center_horizontal"
+                android:gravity="center"
                 android:textSize="@dimen/notification_header_count_text_size"
                 android:fontFamily="sans-serif-medium"
                 android:textColor="?android:attr/textColorPrimary" />
diff --git a/res/layout/notification_main.xml b/res/layout/notification_main.xml
index f681e8b..f94face 100644
--- a/res/layout/notification_main.xml
+++ b/res/layout/notification_main.xml
@@ -31,7 +31,7 @@
         android:background="?attr/popupColorPrimary"
         android:paddingStart="@dimen/notification_padding_start"
         android:paddingEnd="@dimen/notification_main_text_padding_end"
-        android:paddingBottom="16dp">
+        android:paddingBottom="14dp">
         <TextView
             android:id="@+id/title"
             android:layout_width="match_parent"
@@ -59,7 +59,7 @@
         android:layout_width="@dimen/notification_icon_size"
         android:layout_height="@dimen/notification_icon_size"
         android:layout_marginEnd="@dimen/notification_padding_end"
-        android:layout_marginBottom="8dp"
+        android:layout_marginBottom="7dp"
         android:layout_gravity="center_vertical|end" />
 
 </com.android.launcher3.notification.NotificationMainView>
diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml
index c430521..fc07002 100644
--- a/res/layout/search_container_all_apps.xml
+++ b/res/layout/search_container_all_apps.xml
@@ -19,11 +19,11 @@
     android:layout_width="match_parent"
     android:layout_height="@dimen/all_apps_search_bar_height"
     android:layout_gravity="center|top"
+    android:layout_marginBottom="-8dp"
     android:gravity="center|bottom"
-    android:saveEnabled="false"
     android:paddingLeft="@dimen/dynamic_grid_edge_margin"
     android:paddingRight="@dimen/dynamic_grid_edge_margin"
-    android:layout_marginBottom="-8dp" >
+    android:saveEnabled="false" >
 
     <!--
       Note: The following relation should always be true so that the shadows are aligned properly
@@ -45,6 +45,7 @@
         android:imeOptions="actionSearch|flagNoExtractUi"
         android:inputType="text|textNoSuggestions|textCapWords"
         android:maxLines="1"
+        android:saveEnabled="false"
         android:scrollHorizontally="true"
         android:singleLine="true"
         android:textColor="?android:attr/textColorSecondary"
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
index fd6fc4d..b25f46a 100644
--- a/res/values-v26/styles.xml
+++ b/res/values-v26/styles.xml
@@ -26,10 +26,4 @@
         <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
     </style>
 
-    <!-- From O and above, we show a dark nav bar in all-apps -->
-    <style name="AllAppsNavBarProtection">
-        <item name="android:alpha">0.6</item>
-        <item name="android:background">?android:attr/colorPrimary</item>
-    </style>
-
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 7b52dae..5aee715 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -20,6 +20,7 @@
 
     <!-- Attributes used for launcher theme -->
     <attr name="allAppsScrimColor" format="color" />
+    <attr name="allAppsNavBarScrimColor" format="color" />
     <attr name="popupColorPrimary" format="color" />
     <attr name="popupColorSecondary" format="color" />
     <attr name="popupColorTertiary" format="color" />
@@ -122,6 +123,7 @@
         <attr name="iconTextSize" format="float" />
 
         <attr name="defaultLayoutId" format="reference" />
+        <attr name="demoModeLayoutId" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/res/values/config.xml b/res/values/config.xml
index e7ec0a0..10b612b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -1,7 +1,7 @@
 <resources>
 <!-- Dynamic Grid -->
     <!-- Out of 100, the percent of space the overview bar should try and take vertically. -->
-    <integer name="config_dynamic_grid_overview_icon_zone_percentage">20</integer>
+    <integer name="config_dynamic_grid_overview_icon_zone_percentage">22</integer>
 
 <!-- Miscellaneous -->
     <bool name="config_largeHeap">false</bool>
@@ -9,7 +9,7 @@
     <bool name="is_large_tablet">false</bool>
     <bool name="allow_rotation">false</bool>
 
-    <integer name="extracted_color_gradient_alpha">191</integer>
+    <integer name="extracted_color_gradient_alpha">153</integer>
 
     <!-- A string pointer to the original app name string. This allows derived projects to
      easily override the app name without providing all translations -->
@@ -19,6 +19,9 @@
     q=<query> to the data to the intent -->
     <string name="market_search_intent" translatable="false">market://search?c=apps</string>
 
+    <!-- String representing the intent to delete a package.-->
+    <string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end</string>
+
     <!-- Values for icon shape overrides. These should correspond to entries defined
      in icon_shape_override_paths_names -->
     <string-array translatable="false" name="icon_shape_override_paths_values">
@@ -113,6 +116,9 @@
     <!-- Name of a color extraction implementation class. -->
     <string name="color_extraction_impl_class" translatable="false"></string>
 
+    <!-- Name of a subclass of com.android.launcher3.util.InstantAppResolver. Can be empty. -->
+    <string name="instant_app_resolver_class" translatable="false"></string>
+
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3f6efd7..b1f9d63 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -214,9 +214,11 @@
     <dimen name="notification_padding_end">12dp</dimen>
     <!-- notification_padding_end + (icon_size - footer_icon_size) / 2 -->
     <dimen name="notification_footer_icon_row_padding">15dp</dimen>
-    <dimen name="notification_header_height">32dp</dimen>
-    <dimen name="notification_main_height">96dp</dimen>
+    <dimen name="notification_header_height">36dp</dimen>
+    <dimen name="notification_main_height">84dp</dimen>
     <dimen name="notification_footer_height">32dp</dimen>
+    <!-- How much space to keep as padding for the last notification when the footer collapses -->
+    <dimen name="notification_empty_footer_height">6dp</dimen>
     <dimen name="notification_header_text_size">13sp</dimen>
     <dimen name="notification_header_count_text_size">12sp</dimen>
     <dimen name="notification_main_title_size">16sp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5bdf512..8129e81 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -29,7 +29,8 @@
     </style>
 
     <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
-        <item name="allAppsScrimColor">#DDFFFFFF</item>
+        <item name="allAppsScrimColor">#CCFFFFFF</item>
+        <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
         <item name="popupColorPrimary">#FFF</item>
         <item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
         <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
@@ -61,7 +62,8 @@
         <item name="android:textColorHint">#A0FFFFFF</item>
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
         <item name="android:colorPrimary">#FF333333</item>
-        <item name="allAppsScrimColor">#33000000</item>
+        <item name="allAppsScrimColor">#7A212121</item>
+        <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="popupColorPrimary">?android:attr/colorPrimary</item>
         <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
         <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
@@ -103,12 +105,6 @@
         <item name="android:includeFontPadding">false</item>
     </style>
 
-    <!-- Style for nav bar background in all-apps screen -->
-    <style name="AllAppsNavBarProtection">
-        <item name="android:alpha">?android:attr/spotShadowAlpha</item>
-        <item name="android:background">@color/default_shadow_color_no_alpha</item>
-    </style>
-
     <!-- Base theme for BubbleTextView and sub classes -->
     <style name="BaseIcon">
         <item name="android:layout_width">match_parent</item>
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 9ec26e2..7d2f753 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
@@ -31,6 +32,10 @@
  */
 public class AppInfo extends ItemInfoWithIcon {
 
+    public static final int FLAG_SYSTEM_UNKNOWN = 0;
+    public static final int FLAG_SYSTEM_YES = 1 << 0;
+    public static final int FLAG_SYSTEM_NO = 1 << 1;
+
     /**
      * The intent used to start the application.
      */
@@ -43,6 +48,11 @@
      */
     public int isDisabled = ShortcutInfo.DEFAULT;
 
+    /**
+     * Stores if the app is a system app or not.
+     */
+    public int isSystemApp;
+
     public AppInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
     }
@@ -71,6 +81,10 @@
         }
 
         intent = makeLaunchIntent(info);
+
+        isSystemApp = (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
+
     }
 
     public AppInfo(AppInfo info) {
@@ -79,6 +93,7 @@
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
         isDisabled = info.isDisabled;
+        isSystemApp = info.isSystemApp;
     }
 
     @Override
@@ -95,10 +110,14 @@
     }
 
     public static Intent makeLaunchIntent(LauncherActivityInfo info) {
+        return makeLaunchIntent(info.getComponentName());
+    }
+
+    public static Intent makeLaunchIntent(ComponentName cn) {
         return new Intent(Intent.ACTION_MAIN)
-            .addCategory(Intent.CATEGORY_LAUNCHER)
-            .setComponent(info.getComponentName())
-            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setComponent(cn)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
     }
 
     @Override
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 1a405f9..a486a3a 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -55,7 +55,6 @@
 
     private final int[] mDirectionVector = new int[2];
     private final int[] mLastDirectionVector = new int[2];
-    private final int[] mTmpPt = new int[2];
 
     private final IntRange mTempRange1 = new IntRange();
     private final IntRange mTempRange2 = new IntRange();
@@ -344,13 +343,12 @@
         return rect;
     }
 
-    /**
-     * This is the final step of the resize. Here we save the new widget size and position
-     * to LauncherModel and animate the resize frame.
-     */
-    public void commitResize() {
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        // We are done with resizing the widget. Save the widget size & position to LauncherModel
         resizeWidgetIfNeeded(true);
-        requestLayout();
     }
 
     private void onTouchUp() {
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index c55a586..82175b7 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -62,7 +62,7 @@
     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) {
+        if (this instanceof AllAppsContainerView) {
             mBaseDrawable = new ColorDrawable();
         } else {
             TypedArray a = context.obtainStyledAttributes(attrs,
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a63767c..ac842f9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -184,7 +184,7 @@
     public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info.iconBitmap, info);
         setTag(info);
-        if (promiseStateChanged || info.isPromise()) {
+        if (promiseStateChanged || (info.hasPromiseIconUi())) {
             applyPromiseState(promiseStateChanged);
         }
 
@@ -481,7 +481,7 @@
     public void applyPromiseState(boolean promiseStateChanged) {
         if (getTag() instanceof ShortcutInfo) {
             ShortcutInfo info = (ShortcutInfo) getTag();
-            final boolean isPromise = info.isPromise();
+            final boolean isPromise = info.hasPromiseIconUi();
             final int progressLevel = isPromise ?
                     ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
                             info.getInstallProgress() : 0)) : 100;
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index e4a3226..632e490 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -29,6 +29,7 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -69,6 +70,7 @@
     /** The paint applied to the drag view on hover */
     protected int mHoverColor = 0;
 
+    protected CharSequence mText;
     protected ColorStateList mOriginalTextColor;
     protected Drawable mDrawable;
 
@@ -96,6 +98,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mText = getText();
         mOriginalTextColor = getTextColors();
     }
 
@@ -297,4 +300,30 @@
     public int getTextColor() {
         return getTextColors().getDefaultColor();
     }
+
+    /**
+     * Returns True if any update was made.
+     */
+    public boolean updateText(boolean hide) {
+        if ((hide && getText().toString().isEmpty()) || (!hide && mText.equals(getText()))) {
+            return false;
+        }
+
+        setText(hide ? "" : mText);
+        return true;
+    }
+
+    public boolean isTextTruncated() {
+        int availableWidth = getMeasuredWidth();
+        if (mHideParentOnDisable) {
+            ViewGroup parent = (ViewGroup) getParent();
+            availableWidth = parent.getMeasuredWidth() - parent.getPaddingLeft()
+                    - parent.getPaddingRight();
+        }
+        availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
+                + getCompoundDrawablePadding());
+        CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
+                TextUtils.TruncateAt.END);
+        return !mText.equals(displayedText);
+    }
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 3ebccda..a75c6d1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -606,7 +606,7 @@
         // Hotseat icons - remove text
         if (child instanceof BubbleTextView) {
             BubbleTextView bubbleChild = (BubbleTextView) child;
-            bubbleChild.setTextVisibility(bubbleChild.shouldTextBeVisible());
+            bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
         }
 
         child.setScaleX(mChildScale);
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 975675a..4dcb64f 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -65,9 +65,11 @@
      * Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
      */
     public void setTextBasedOnDragSource(DragSource dragSource) {
-        if (!TextUtils.isEmpty(getText())) {
-            setText(dragSource.supportsDeleteDropTarget() ? R.string.remove_drop_target_label
+        if (!TextUtils.isEmpty(mText)) {
+            mText = getResources().getString(dragSource.supportsDeleteDropTarget()
+                    ? R.string.remove_drop_target_label
                     : android.R.string.cancel);
+            requestLayout();
         }
     }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 150bc53..69ee03e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -267,6 +267,10 @@
     }
 
     DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+        // We take the minimum sizes of this profile and it's multi-window variant to ensure that
+        // the system decor is always excluded.
+        mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
+
         // In multi-window mode, we can have widthPx = availableWidthPx
         // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
         // widthPx and heightPx values where it's needed.
@@ -346,9 +350,18 @@
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
 
-        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx
                 + Utilities.calculateTextHeight(iconTextSizePx);
+        int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
+        if (iconDrawablePaddingPx > cellYPadding && !isVerticalBarLayout()
+                && !inMultiWindowMode()) {
+            // Ensures that the label is closer to its corresponding icon. This is not an issue
+            // with vertical bar layout or multi-window mode since the issue is handled separately
+            // with their calls to {@link #adjustToHideWorkspaceLabels}.
+            cellHeightPx -= (iconDrawablePaddingPx - cellYPadding);
+            iconDrawablePaddingPx = cellYPadding;
+        }
+        cellWidthPx = iconSizePx + iconDrawablePaddingPx;
 
         // All apps
         allAppsIconTextSizePx = iconTextSizePx;
@@ -555,9 +568,9 @@
 
     int getOverviewModeButtonBarHeight() {
         int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
-        zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
-                Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
-        return zoneHeight;
+        return Utilities.boundToRange(zoneHeight,
+                overviewModeMinIconZoneHeightPx,
+                overviewModeMaxIconZoneHeightPx);
     }
 
     public static int calculateCellWidth(int width, int countX) {
@@ -693,7 +706,8 @@
 
             lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
             lp.width = Math.min(availableWidthPx, maxWidth);
-            lp.height = getOverviewModeButtonBarHeight() + mInsets.bottom;
+            lp.height = getOverviewModeButtonBarHeight();
+            lp.bottomMargin = mInsets.bottom;
             overviewMode.setLayoutParams(lp);
         }
 
@@ -750,11 +764,14 @@
         return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
     }
 
+    public boolean inMultiWindowMode() {
+        return this != inv.landscapeProfile && this != inv.portraitProfile;
+    }
+
     public boolean shouldIgnoreLongPressToOverview(float touchX) {
-        boolean inMultiWindowMode = this != inv.landscapeProfile && this != inv.portraitProfile;
         boolean touchedLhsEdge = mInsets.left == 0 && touchX < edgeMarginPx;
         boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
-        return !inMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
+        return !inMultiWindowMode() && (touchedLhsEdge || touchedRhsEdge);
     }
 
     private static Context getContext(Context c, int orientation) {
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 0840b70..29a1349 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -78,6 +78,58 @@
         setupButtonDropTarget(this, dragController);
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        boolean hideText = hideTextHelper(false /* shouldUpdateText */, false /* no-op */);
+        if (hideTextHelper(true /* shouldUpdateText */, hideText)) {
+            // Text has changed, so we need to re-measure.
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    /**
+     * Helper method that iterates through the children and returns whether any of the visible
+     * {@link ButtonDropTarget} has truncated text.
+     *
+     * @param shouldUpdateText If True, updates the text of all children.
+     * @param hideText If True and {@param shouldUpdateText} is True, clears the text of all
+     *                 children; otherwise it sets the original text value.
+     *
+     *
+     * @return If shouldUpdateText is True, returns whether any of the children updated their text.
+     *         Else, returns whether any of the children have truncated their text.
+     */
+    private boolean hideTextHelper(boolean shouldUpdateText, boolean hideText) {
+        boolean result = false;
+        View visibleView;
+        ButtonDropTarget dropTarget;
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            if (getChildAt(i) instanceof ButtonDropTarget) {
+                visibleView = dropTarget = (ButtonDropTarget) getChildAt(i);
+            } else if (getChildAt(i) instanceof ViewGroup) {
+                // The Drop Target is wrapped in a FrameLayout.
+                visibleView = getChildAt(i);
+                dropTarget = (ButtonDropTarget) ((ViewGroup) visibleView).getChildAt(0);
+            } else {
+                // Ignore other views.
+                continue;
+            }
+
+            if (visibleView.getVisibility() == View.VISIBLE) {
+                if (shouldUpdateText) {
+                    result |= dropTarget.updateText(hideText);
+                } else if (dropTarget.isTextTruncated()) {
+                    result = true;
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
     private void setupButtonDropTarget(View view, DragController dragController) {
         if (view instanceof ButtonDropTarget) {
             ButtonDropTarget bdt = (ButtonDropTarget) view;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index af3abeb..a6d80e3 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -149,7 +149,6 @@
             allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
             if (mLauncher != null) {
                 mLauncher.setAllAppsButton(allAppsButton);
-                allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
                 allAppsButton.setOnClickListener(mLauncher);
                 allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
             }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 3bcd7af..573e8a2 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.SQLiteCacheHelper;
@@ -94,6 +95,7 @@
     private final LauncherAppsCompat mLauncherApps;
     private final HashMap<ComponentKey, CacheEntry> mCache =
             new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
+    private final InstantAppResolver mInstantAppResolver;
     private final int mIconDpi;
     @Thunk final IconDB mIconDb;
 
@@ -106,6 +108,7 @@
         mPackageManager = context.getPackageManager();
         mUserManager = UserManagerCompat.getInstance(mContext);
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+        mInstantAppResolver = InstantAppResolver.newInstance(mContext);
         mIconDpi = inv.fillResIconDpi;
         mIconDb = new IconDB(context, inv.iconBitmapSize);
 
@@ -120,7 +123,7 @@
     }
 
     private Drawable getFullResDefaultActivityIcon() {
-        return getFullResIcon(Resources.getSystem(), Utilities.isAtLeastO() ?
+        return getFullResIcon(Resources.getSystem(), Utilities.ATLEAST_OREO ?
                 android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon);
     }
 
@@ -575,7 +578,6 @@
         // For icon caching, do not go through DB. Just update the in-memory entry.
         if (entry == null) {
             entry = new CacheEntry();
-            mCache.put(cacheKey, entry);
         }
         if (!TextUtils.isEmpty(title)) {
             entry.title = title;
@@ -583,6 +585,9 @@
         if (icon != null) {
             entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
         }
+        if (!TextUtils.isEmpty(title) && entry.icon != null) {
+            mCache.put(cacheKey, entry);
+        }
     }
 
     private static ComponentKey getPackageKey(String packageName, UserHandle user) {
@@ -619,6 +624,10 @@
                     // only keep the low resolution icon instead of the larger full-sized icon
                     Bitmap icon = LauncherIcons.createBadgedIconBitmap(
                             appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
+                    if (mInstantAppResolver.isInstantApp(appInfo)) {
+                        icon = LauncherIcons.badgeWithDrawable(icon,
+                                mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
+                    }
                     Bitmap lowResIcon =  generateLowResIcon(icon);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index 6872515..4dee2b5 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -2,6 +2,7 @@
 
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 
 import java.util.Locale;
 
@@ -17,7 +18,7 @@
     }
 
     public void updateSystemStateString() {
-        mSystemState = Locale.getDefault().toString();
+        mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT;
     }
 
     public String getIconSystemState(String packageName) {
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f088d11..f919dd0 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -75,7 +75,7 @@
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
         } else if (info instanceof ShortcutInfo) {
-            componentName = ((ShortcutInfo) info).intent.getComponent();
+            componentName = info.getTargetComponent();
         } else if (info instanceof PendingAddItemInfo) {
             componentName = ((PendingAddItemInfo) info).componentName;
         } else if (info instanceof LauncherAppWidgetInfo) {
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d7bebd1..7a43198 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -85,7 +85,9 @@
      * Number of icons inside the hotseat area.
      */
     public int numHotseatIcons;
+
     int defaultLayoutId;
+    int demoModeLayoutId;
 
     public DeviceProfile landscapeProfile;
     public DeviceProfile portraitProfile;
@@ -99,11 +101,11 @@
         this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
                 p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
                 p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
-                p.defaultLayoutId);
+                p.defaultLayoutId, p.demoModeLayoutId);
     }
 
     InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
-            float is, float lis, float its, int hs, int dlId) {
+            float is, float lis, float its, int hs, int dlId, int dmlId) {
         name = n;
         minWidthDps = w;
         minHeightDps = h;
@@ -117,6 +119,7 @@
         iconTextSize = its;
         numHotseatIcons = hs;
         defaultLayoutId = dlId;
+        demoModeLayoutId = dmlId;
     }
 
     @TargetApi(23)
@@ -144,6 +147,7 @@
         numColumns = closestProfile.numColumns;
         numHotseatIcons = closestProfile.numHotseatIcons;
         defaultLayoutId = closestProfile.defaultLayoutId;
+        demoModeLayoutId = closestProfile.demoModeLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
         minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
@@ -208,7 +212,8 @@
                             a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
                             a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
                             a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
-                            a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0)));
+                            a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
+                            a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
                     a.recycle();
                 }
             }
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index c5be096..fa3253c 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -138,17 +138,11 @@
 
     public ComponentName getTargetComponent() {
         Intent intent = getIntent();
-        if (intent == null) {
+        if (intent != null) {
+            return intent.getComponent();
+        } else {
             return null;
         }
-        ComponentName cn = intent.getComponent();
-        if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT && cn == null) {
-            // Legacy shortcuts may not have a componentName but just a packageName. In that case
-            // create a dummy componentName instead of adding additional check everywhere.
-            String pkg = intent.getPackage();
-            return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
-        }
-        return cn;
     }
 
     public void writeToValues(ContentWriter writer) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0841f4f..32af059 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -91,6 +91,7 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.LauncherAppsCompatVO;
+import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
@@ -113,13 +114,14 @@
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
 import com.android.launcher3.util.ActivityResultInfo;
+import com.android.launcher3.util.RunnableWithId;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -136,6 +138,7 @@
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetsContainerView;
 
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -146,6 +149,9 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
+import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
+
 /**
  * Default launcher application.
  */
@@ -246,7 +252,10 @@
 
     // Main container view and the model for the widget tray screen.
     @Thunk WidgetsContainerView mWidgetsView;
-    @Thunk MultiHashMap<PackageItemInfo, WidgetItem> mAllWidgets;
+
+    // We need to store the orientation Launcher was created with, due to a bug (b/64916689)
+    // that results in widgets being inflated in the wrong orientation.
+    private int mOrientation;
 
     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
     // scroll issues (because the workspace may not have been measured yet) and extra work.
@@ -269,7 +278,6 @@
     private IconCache mIconCache;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
     private final Handler mHandler = new Handler();
-    private boolean mIsResumeFromActionScreenOff;
     private boolean mHasFocus = false;
 
     private ObjectAnimator mScrimAnimator;
@@ -277,8 +285,6 @@
 
     private PopupDataProvider mPopupDataProvider;
 
-    private View.OnTouchListener mHapticFeedbackTouchListener;
-
     // Determines how long to wait after a rotation before restoring the screen orientation to
     // match the sensor state.
     private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
@@ -383,6 +389,7 @@
             mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
         }
 
+        mOrientation = getResources().getConfiguration().orientation;
         mSharedPrefs = Utilities.getPrefs(this);
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
@@ -1043,7 +1050,6 @@
         if (shouldShowDiscoveryBounce()) {
             mAllAppsController.showDiscoveryBounce();
         }
-        mIsResumeFromActionScreenOff = false;
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
@@ -1131,7 +1137,7 @@
             // On O and above we there is always some setting present settings (add icon to
             // home screen or icon badging). On earlier APIs we will have the allow rotation
             // setting, on devices with a locked orientation,
-            return Utilities.isAtLeastO() || !getResources().getBoolean(R.bool.allow_rotation);
+            return Utilities.ATLEAST_OREO || !getResources().getBoolean(R.bool.allow_rotation);
         }
     }
 
@@ -1307,9 +1313,7 @@
         mDragController.addDropTarget(mWorkspace);
         mDropTargetBar.setup(mDragController);
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
-        }
+        mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
 
         if (TestingUtils.MEMORY_DUMP_ENABLED) {
             TestingUtils.addWeightWatcher(this);
@@ -1327,7 +1331,6 @@
                 onClickWallpaperPicker(view);
             }
         }.attachTo(wallpaperButton);
-        wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
 
         // Bind widget button actions
         mWidgetsButton = findViewById(R.id.widget_button);
@@ -1337,7 +1340,6 @@
                 onClickAddWidgetButton(view);
             }
         }.attachTo(mWidgetsButton);
-        mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
 
         // Bind settings actions
         View settingsButton = findViewById(R.id.settings_button);
@@ -1349,7 +1351,6 @@
                     onClickSettingsButton(view);
                 }
             }.attachTo(settingsButton);
-            settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
         } else {
             settingsButton.setVisibility(View.GONE);
         }
@@ -1415,7 +1416,7 @@
         CellLayout layout = getCellLayout(container, screenId);
 
         ShortcutInfo info = null;
-        if (Utilities.isAtLeastO()) {
+        if (Utilities.ATLEAST_OREO) {
             info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
                     this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
         }
@@ -1547,7 +1548,6 @@
                         mAppsView.reset();
                     }
                 }
-                mIsResumeFromActionScreenOff = true;
                 mShouldFadeInScrim = true;
             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                 // ACTION_USER_PRESENT is sent after onStart/onResume. This covers the case where
@@ -1676,6 +1676,8 @@
         return mSharedPrefs;
     }
 
+    public int getOrientation() { return mOrientation; }
+
     @Override
     protected void onNewIntent(Intent intent) {
         long startTime = 0;
@@ -2277,7 +2279,7 @@
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
-        } else if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
+        } else if ((v instanceof PageIndicator) ||
             (v == mAllAppsButton && mAllAppsButton != null)) {
             onClickAllAppsButton(v);
         } else if (tag instanceof AppInfo) {
@@ -2423,7 +2425,7 @@
         }
 
         // Check for abandoned promise
-        if ((v instanceof BubbleTextView) && shortcut.isPromise()) {
+        if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
             String packageName = shortcut.intent.getComponent() != null ?
                     shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
             if (!TextUtils.isEmpty(packageName)) {
@@ -2535,22 +2537,6 @@
         startActivity(intent, getActivityLaunchOptions(v));
     }
 
-    public View.OnTouchListener getHapticFeedbackTouchListener() {
-        if (mHapticFeedbackTouchListener == null) {
-            mHapticFeedbackTouchListener = new View.OnTouchListener() {
-                @SuppressLint("ClickableViewAccessibility")
-                @Override
-                public boolean onTouch(View v, MotionEvent event) {
-                    if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
-                        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
-                    }
-                    return false;
-                }
-            };
-        }
-        return mHapticFeedbackTouchListener;
-    }
-
     @Override
     public void onAccessibilityStateChanged(boolean enabled) {
         mDragLayer.onAccessibilityStateChanged(enabled);
@@ -3067,7 +3053,7 @@
      */
     public void tryAndUpdatePredictedApps() {
         if (mLauncherCallbacks != null) {
-            List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
+            List<ComponentKeyMapper<AppInfo>> apps = mLauncherCallbacks.getPredictedApps();
             if (apps != null) {
                 mAppsView.setPredictedApps(apps);
             }
@@ -3113,12 +3099,12 @@
      *
      * @return {@code true} if we are currently paused. The caller might be able to skip some work
      */
-    @Thunk boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
+    @Thunk boolean waitUntilResume(Runnable run) {
         if (mPaused) {
             if (LOGD) Log.d(TAG, "Deferring update until onResume");
-            if (deletePreviousRunnables) {
-                while (mBindOnResumeCallbacks.remove(run)) {
-                }
+            if (run instanceof RunnableWithId) {
+                // Remove any runnables which have the same id
+                while (mBindOnResumeCallbacks.remove(run)) { }
             }
             mBindOnResumeCallbacks.add(run);
             return true;
@@ -3127,10 +3113,6 @@
         }
     }
 
-    private boolean waitUntilResume(Runnable run) {
-        return waitUntilResume(run, false);
-    }
-
     public void addOnResumeCallback(Runnable run) {
         mOnResumeCallbacks.add(run);
     }
@@ -3328,7 +3310,7 @@
                     break;
                 }
                 case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
-                    view = bindAppWidget((LauncherAppWidgetInfo) item);
+                    view = inflateAppWidget((LauncherAppWidgetInfo) item);
                     if (view == null) {
                         continue;
                     }
@@ -3402,13 +3384,19 @@
     /**
      * Add the views for a widget to the workspace.
      */
-    public View bindAppWidget(LauncherAppWidgetInfo item) {
+    public void bindAppWidget(LauncherAppWidgetInfo item) {
+        View view = inflateAppWidget(item);
+        if (view != null) {
+            mWorkspace.addInScreen(view, item);
+            mWorkspace.requestLayout();
+        }
+    }
+
+    private View inflateAppWidget(LauncherAppWidgetInfo item) {
         if (mIsSafeModeEnabled) {
             PendingAppWidgetHostView view =
                     new PendingAppWidgetHostView(this, item, mIconCache, true);
             prepareAppWidget(view, item);
-            mWorkspace.addInScreen(view, item);
-            mWorkspace.requestLayout();
             return view;
         }
 
@@ -3517,8 +3505,6 @@
             view = new PendingAppWidgetHostView(this, item, mIconCache, false);
         }
         prepareAppWidget(view, item);
-        mWorkspace.addInScreen(view, item);
-        mWorkspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
@@ -3660,25 +3646,17 @@
     }
 
     /**
-     * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent
-     * multiple calls to bind the same list.)
-     */
-    @Thunk ArrayList<AppInfo> mTmpAppsList;
-    private final Runnable mBindAllApplicationsRunnable = new Runnable() {
-        public void run() {
-            bindAllApplications(mTmpAppsList);
-            mTmpAppsList = null;
-        }
-    };
-
-    /**
      * Add the icons for all apps.
      *
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindAllApplications(final ArrayList<AppInfo> apps) {
-        if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
-            mTmpAppsList = apps;
+        Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
+            public void run() {
+                bindAllApplications(apps);
+            }
+        };
+        if (waitUntilResume(r)) {
             return;
         }
 
@@ -3686,10 +3664,10 @@
             Executor pendingExecutor = getPendingExecutor();
             if (pendingExecutor != null && mState != State.APPS) {
                 // Wait until the fade in animation has finished before setting all apps list.
-                mTmpAppsList = apps;
-                pendingExecutor.execute(mBindAllApplicationsRunnable);
+                pendingExecutor.execute(r);
                 return;
             }
+
             mAppsView.setApps(apps);
         }
         if (mLauncherCallbacks != null) {
@@ -3768,16 +3746,12 @@
      * Implementation of the method from LauncherModel.Callbacks.
      *
      * @param updated list of shortcuts which have changed.
-     * @param removed list of shortcuts which were deleted in the background. This can happen when
-     *                an app gets removed from the system or some of its components are no longer
-     *                available.
      */
     @Override
-    public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated,
-            final ArrayList<ShortcutInfo> removed, final UserHandle user) {
+    public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, final UserHandle user) {
         Runnable r = new Runnable() {
             public void run() {
-                bindShortcutsChanged(updated, removed, user);
+                bindShortcutsChanged(updated, user);
             }
         };
         if (waitUntilResume(r)) {
@@ -3787,31 +3761,6 @@
         if (!updated.isEmpty()) {
             mWorkspace.updateShortcuts(updated);
         }
-
-        if (!removed.isEmpty()) {
-            HashSet<ComponentName> removedComponents = new HashSet<>();
-            HashSet<ShortcutKey> removedDeepShortcuts = new HashSet<>();
-
-            for (ShortcutInfo si : removed) {
-                if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-                    removedDeepShortcuts.add(ShortcutKey.fromItemInfo(si));
-                } else {
-                    removedComponents.add(si.getTargetComponent());
-                }
-            }
-
-            if (!removedComponents.isEmpty()) {
-                ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(removedComponents, user);
-                mWorkspace.removeItemsByMatcher(matcher);
-                mDragController.onAppsRemoved(matcher);
-            }
-
-            if (!removedDeepShortcuts.isEmpty()) {
-                ItemInfoMatcher matcher = ItemInfoMatcher.ofShortcutKeys(removedDeepShortcuts);
-                mWorkspace.removeItemsByMatcher(matcher);
-                mDragController.onAppsRemoved(matcher);
-            }
-        }
     }
 
     /**
@@ -3841,28 +3790,17 @@
      * package-removal should clear all items by package name.
      */
     @Override
-    public void bindWorkspaceComponentsRemoved(
-            final HashSet<String> packageNames, final HashSet<ComponentName> components,
-            final UserHandle user) {
+    public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
         Runnable r = new Runnable() {
             public void run() {
-                bindWorkspaceComponentsRemoved(packageNames, components, user);
+                bindWorkspaceComponentsRemoved(matcher);
             }
         };
         if (waitUntilResume(r)) {
             return;
         }
-        if (!packageNames.isEmpty()) {
-            ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageNames, user);
-            mWorkspace.removeItemsByMatcher(matcher);
-            mDragController.onAppsRemoved(matcher);
-
-        }
-        if (!components.isEmpty()) {
-            ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(components, user);
-            mWorkspace.removeItemsByMatcher(matcher);
-            mDragController.onAppsRemoved(matcher);
-        }
+        mWorkspace.removeItemsByMatcher(matcher);
+        mDragController.onAppsRemoved(matcher);
     }
 
     @Override
@@ -3883,28 +3821,25 @@
         }
     }
 
-    private final Runnable mBindAllWidgetsRunnable = new Runnable() {
+    @Override
+    public void bindAllWidgets(final MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
+        Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
+            @Override
             public void run() {
-                bindAllWidgets(mAllWidgets);
+                bindAllWidgets(allWidgets);
             }
         };
-
-    @Override
-    public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> allWidgets) {
-        if (waitUntilResume(mBindAllWidgetsRunnable, true)) {
-            mAllWidgets = allWidgets;
+        if (waitUntilResume(r)) {
             return;
         }
 
         if (mWidgetsView != null && allWidgets != null) {
             Executor pendingExecutor = getPendingExecutor();
             if (pendingExecutor != null && mState != State.WIDGETS) {
-                mAllWidgets = allWidgets;
-                pendingExecutor.execute(mBindAllWidgetsRunnable);
+                pendingExecutor.execute(r);
                 return;
             }
             mWidgetsView.setWidgets(allWidgets);
-            mAllWidgets = null;
         }
 
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
@@ -3960,16 +3895,8 @@
     }
 
     private boolean shouldShowDiscoveryBounce() {
-        if (mState != State.WORKSPACE) {
-            return false;
-        }
-        if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
-            return true;
-        }
-        if (!mIsResumeFromActionScreenOff) {
-            return false;
-        }
-        return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
+        UserManagerCompat um = UserManagerCompat.getInstance(this);
+        return mState == State.WORKSPACE && !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false) && !um.isDemoUser();
     }
 
     protected void moveWorkspaceToDefaultScreen() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index c7b7782..7fe1308 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -92,7 +92,7 @@
         setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
 
-        if (Utilities.isAtLeastO()) {
+        if (Utilities.ATLEAST_OREO) {
             setExecutor(Utilities.THREAD_POOL_EXECUTOR);
         }
     }
@@ -142,9 +142,8 @@
         return false;
     }
 
-    public boolean isReinflateRequired() {
+    public boolean isReinflateRequired(int orientation) {
         // Re-inflate is required if the orientation has changed since last inflated.
-        int orientation = mContext.getResources().getConfiguration().orientation;
         if (mPreviousOrientation != orientation) {
            return true;
        }
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index d66b14c..66da046 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -21,7 +21,7 @@
 import android.view.Menu;
 import android.view.View;
 
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -91,10 +91,8 @@
      */
     boolean shouldMoveToDefaultScreenOnHomeIntent();
     boolean hasSettings();
-    List<ComponentKey> getPredictedApps();
+    List<ComponentKeyMapper<AppInfo>> getPredictedApps();
     int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
     /** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
     int getSearchBarHeight();
-
-    boolean shouldShowDiscoveryBounce();
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 22d62ec..a906b00 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -56,6 +56,7 @@
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
@@ -148,13 +149,10 @@
                                   ArrayList<ItemInfo> addNotAnimated,
                                   ArrayList<ItemInfo> addAnimated);
         public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
-        public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
-                ArrayList<ShortcutInfo> removed, UserHandle user);
+        public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, UserHandle user);
         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
-        public void bindWorkspaceComponentsRemoved(
-                HashSet<String> packageNames, HashSet<ComponentName> components,
-                UserHandle user);
+        public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
         public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
         public void bindAllWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets);
         public void onPageBoundSynchronously(int page);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index dc83f36..b31df98 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -552,7 +552,14 @@
     }
 
     private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
-        int defaultLayout = LauncherAppState.getIDP(getContext()).defaultLayoutId;
+        InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
+        int defaultLayout = idp.defaultLayoutId;
+
+        UserManagerCompat um = UserManagerCompat.getInstance(getContext());
+        if (um.isDemoUser() && idp.demoModeLayoutId != 0) {
+            defaultLayout = idp.demoModeLayoutId;
+        }
+
         return new DefaultLayoutParser(getContext(), widgetHost,
                 mOpenHelper, getContext().getResources(), defaultLayout);
     }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 643d48a..a814323 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -11,6 +11,9 @@
 import android.view.View;
 import android.view.ViewDebug;
 
+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 Paint mOpaquePaint;
@@ -44,20 +47,28 @@
     @TargetApi(23)
     @Override
     protected boolean fitSystemWindows(Rect insets) {
-        boolean rawInsetsChanged = !mInsets.equals(insets);
         mDrawSideInsetBar = (insets.right > 0 || insets.left > 0) &&
                 (!Utilities.ATLEAST_MARSHMALLOW ||
-                getContext().getSystemService(ActivityManager.class).isLowRamDevice());
-        mRightInsetBarWidth = insets.right;
-        mLeftInsetBarWidth = insets.left;
-        setInsets(mDrawSideInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
+                        getContext().getSystemService(ActivityManager.class).isLowRamDevice());
+        if (mDrawSideInsetBar) {
+            mLeftInsetBarWidth = insets.left;
+            mRightInsetBarWidth = insets.right;
+            insets = new Rect(0, insets.top, 0, insets.bottom);
+        } else {
+            mLeftInsetBarWidth = mRightInsetBarWidth = 0;
+        }
+        Launcher.getLauncher(getContext()).getSystemUiController().updateUiState(
+                UI_STATE_ROOT_VIEW, mDrawSideInsetBar ? FLAG_DARK_NAV : 0);
 
-        if (mAlignedView != null && mDrawSideInsetBar) {
+        boolean rawInsetsChanged = !mInsets.equals(insets);
+        setInsets(insets);
+
+        if (mAlignedView != null) {
             // Apply margins on aligned view to handle left/right insets.
             MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams();
-            if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) {
-                lp.leftMargin = insets.left;
-                lp.rightMargin = insets.right;
+            if (lp.leftMargin != mLeftInsetBarWidth || lp.rightMargin != mRightInsetBarWidth) {
+                lp.leftMargin = mLeftInsetBarWidth;
+                lp.rightMargin = mRightInsetBarWidth;
                 mAlignedView.setLayoutParams(lp);
             }
         }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 9ff61ec..e247490 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -80,13 +80,11 @@
 public class LauncherStateTransitionAnimation {
 
     /**
-     * animation used for all apps and widget tray when
-     *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code false}
+     * animation used for the widget tray
      */
     public static final int CIRCULAR_REVEAL = 0;
     /**
-     * animation used for all apps and not widget tray when
-     *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code true}
+     * animation used for all apps tray
      */
     public static final int PULLUP = 1;
 
@@ -154,13 +152,9 @@
                 mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
             }
         };
-        int animType = CIRCULAR_REVEAL;
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            animType = PULLUP;
-        }
         // Only animate the search bar if animating from spring loaded mode back to all apps
         startAnimationToOverlay(
-                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb);
+                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, PULLUP, cb);
     }
 
     /**
@@ -193,12 +187,8 @@
 
         if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED
                 || mAllAppsController.isTransitioning()) {
-            int animType = CIRCULAR_REVEAL;
-            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-                animType = PULLUP;
-            }
             startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
-                    animated, animType, onCompleteRunnable);
+                    animated, PULLUP, onCompleteRunnable);
         } else if (fromState == Launcher.State.WIDGETS ||
                 fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
             startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
@@ -235,8 +225,7 @@
         playCommonTransitionAnimations(toWorkspaceState,
                 animated, initialized, animation, layerViews);
         if (!animated || !initialized) {
-            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                    toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
+            if (toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
                 mAllAppsController.finishPullUp();
             }
             toView.setTranslationX(0.0f);
@@ -527,8 +516,7 @@
         playCommonTransitionAnimations(toWorkspaceState,
                 animated, initialized, animation, layerViews);
         if (!animated || !initialized) {
-            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                    fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
+            if (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
                 mAllAppsController.finishPullDown();
             }
             fromView.setVisibility(View.GONE);
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index de424ab..c2d5501 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -111,7 +111,7 @@
     }
 
     @Override
-    public boolean isReinflateRequired() {
+    public boolean isReinflateRequired(int orientation) {
         // Re inflate is required any time the widget restore status changes
         return mStartState != mInfo.restoreStatus;
     }
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 8caba75..edb7ff5 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -59,7 +59,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!isEnabled(context) || !Utilities.isAtLeastO()) {
+        if (!isEnabled(context) || !Utilities.ATLEAST_OREO) {
             // User has decided to not add icons on homescreen.
             return;
         }
@@ -92,7 +92,7 @@
     }
 
     public static void applyDefaultUserPrefs(final Context context) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         SharedPreferences prefs = Utilities.getPrefs(context);
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 9046372..d40ac8f 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -94,10 +94,12 @@
 
             ButtonPreference iconBadgingPref =
                     (ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
-            if (!Utilities.isAtLeastO()) {
+            if (!Utilities.ATLEAST_OREO) {
                 getPreferenceScreen().removePreference(
                         findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
                 getPreferenceScreen().removePreference(iconBadgingPref);
+            } else if (!getResources().getBoolean(R.bool.notification_badging_enabled)) {
+                getPreferenceScreen().removePreference(iconBadgingPref);
             } else {
                 // Listen to system notification badge settings while this UI is active.
                 mIconBadgingObserver = new IconBadgingObserver(
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index f0d9367..adf008b 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.annotation.TargetApi;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Build;
@@ -59,6 +60,11 @@
     public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
 
     /**
+     * Web UI supported.
+     */
+    public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
+
+    /**
      * Indicates if it represents a common type mentioned in {@link CommonAppTypeParser}.
      * Upto 15 different types supported.
      */
@@ -188,6 +194,10 @@
         return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
     }
 
+    public boolean hasPromiseIconUi() {
+        return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
+    }
+
     public int getInstallProgress() {
         return mInstallProgress;
     }
@@ -226,4 +236,18 @@
     public boolean isDisabled() {
         return isDisabled != 0;
     }
+
+    @Override
+    public ComponentName getTargetComponent() {
+        ComponentName cn = super.getTargetComponent();
+        if (cn == null && (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                || hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
+            // Legacy shortcuts and promise icons with web UI may not have a componentName but just
+            // a packageName. In that case create a dummy componentName instead of adding additional
+            // check everywhere.
+            String pkg = intent.getPackage();
+            return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
+        }
+        return cn;
+    }
 }
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index e15cf9f..902fd34 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -11,12 +11,18 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.widget.Toast;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
 
+import java.net.URISyntaxException;
+
 public class UninstallDropTarget extends ButtonDropTarget {
 
+    private static final String TAG = "UninstallDropTarget";
+    private static Boolean sUninstallDisabled;
+
     public UninstallDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -43,13 +49,22 @@
     }
 
     public static boolean supportsDrop(Context context, ItemInfo info) {
-        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-        Bundle restrictions = userManager.getUserRestrictions();
-        if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
-                || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
+        if (sUninstallDisabled == null) {
+            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            Bundle restrictions = userManager.getUserRestrictions();
+            sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+                    || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false);
+        }
+        if (sUninstallDisabled) {
             return false;
         }
 
+        if (info instanceof AppInfo) {
+            AppInfo appInfo = (AppInfo) info;
+            if (appInfo.isSystemApp != AppInfo.FLAG_SYSTEM_UNKNOWN) {
+                return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0;
+            }
+        }
         return getUninstallTarget(context, info) != null;
     }
 
@@ -99,25 +114,28 @@
             final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
         final ComponentName cn = getUninstallTarget(launcher, info);
 
-        final boolean isUninstallable;
+        boolean canUninstall;
         if (cn == null) {
             // System applications cannot be installed. For now, show a toast explaining that.
             // We may give them the option of disabling apps this way.
             Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
-            isUninstallable = false;
+            canUninstall = false;
         } else {
-            Intent intent = new Intent(Intent.ACTION_DELETE,
-                    Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            intent.putExtra(Intent.EXTRA_USER, info.user);
-            launcher.startActivity(intent);
-            isUninstallable = true;
+            try {
+                Intent i = Intent.parseUri(launcher.getString(R.string.delete_package_intent), 0)
+                        .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+                        .putExtra(Intent.EXTRA_USER, info.user);
+                launcher.startActivity(i);
+                canUninstall = true;
+            } catch (URISyntaxException e) {
+                Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
+                canUninstall = false;
+            }
         }
         if (callback != null) {
-            sendUninstallResult(launcher, isUninstallable, cn, info.user, callback);
+            sendUninstallResult(launcher, canUninstall, cn, info.user, callback);
         }
-        return isUninstallable;
+        return canUninstall;
     }
 
     /**
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 3aa2db0..b6876f6 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -83,15 +83,17 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
-    public static boolean isAtLeastO() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
-    }
+    public static final boolean ATLEAST_OREO_MR1 =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
+
+    public static final boolean ATLEAST_OREO =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
 
     public static final boolean ATLEAST_NOUGAT_MR1 =
-        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
 
     public static final boolean ATLEAST_NOUGAT =
-        Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
 
     public static final boolean ATLEAST_MARSHMALLOW =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f8d6498..37ee6d7 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1213,7 +1213,7 @@
                         && v.getTag() instanceof LauncherAppWidgetInfo) {
                     LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
                     LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) v;
-                    if (lahv.isReinflateRequired()) {
+                    if (lahv.isReinflateRequired(mLauncher.getOrientation())) {
                         // Remove and rebind the current widget (which was inflated in the wrong
                         // orientation), but don't delete it from the database
                         mLauncher.removeItem(lahv, info, false  /* deleteFromDb */);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 76772dc..a105a73 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -273,9 +273,8 @@
         float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
                 1.0f : 0f;
         float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded ||
-                (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f;
-        float finalQsbAlpha = (states.stateIsNormal ||
-                (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f;
+                states.stateIsNormalHidden) ? 1f : 0f;
+        float finalQsbAlpha = (states.stateIsNormal || states.stateIsNormalHidden) ? 1f : 0f;
 
         float finalWorkspaceTranslationY = 0;
         if (states.stateIsOverview || states.stateIsOverviewHidden) {
@@ -312,8 +311,7 @@
             if (states.stateIsOverviewHidden) {
                 finalAlpha = 0f;
             } else if(states.stateIsNormalHidden) {
-                finalAlpha = (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                        i == mWorkspace.getNextPage()) ? 1 : 0;
+                finalAlpha = (i == mWorkspace.getNextPage()) ? 1 : 0;
             } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
                 finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
             } else {
@@ -322,7 +320,7 @@
 
             // If we are animating to/from the small state, then hide the side pages and fade the
             // current page in
-            if (!FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !mWorkspace.isSwitchingState()) {
+            if (!FeatureFlags.NO_ALL_APPS_ICON && !mWorkspace.isSwitchingState()) {
                 if (states.workspaceToAllApps || states.allAppsToWorkspace) {
                     boolean isCurrentPage = (i == toPage);
                     if (states.allAppsToWorkspace && isCurrentPage) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 0d512ab..4eba5c6 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -50,6 +50,7 @@
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.PackageUserKey;
 
 import java.util.List;
@@ -102,25 +103,21 @@
     @Override
     protected void updateBackground(
             int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) {
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
-                getRevealView().setBackground(new InsetDrawable(mBaseDrawable,
-                        paddingLeft, paddingTop, paddingRight, paddingBottom));
-                getContentView().setBackground(
-                        new InsetDrawable(new ColorDrawable(Color.TRANSPARENT),
-                                paddingLeft, paddingTop, paddingRight, paddingBottom));
-            } else {
-                getRevealView().setBackground(mBaseDrawable);
-            }
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            getRevealView().setBackground(new InsetDrawable(mBaseDrawable,
+                    paddingLeft, paddingTop, paddingRight, paddingBottom));
+            getContentView().setBackground(
+                    new InsetDrawable(new ColorDrawable(Color.TRANSPARENT),
+                            paddingLeft, paddingTop, paddingRight, paddingBottom));
         } else {
-            super.updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
+            getRevealView().setBackground(mBaseDrawable);
         }
     }
 
     /**
      * Sets the current set of predicted apps.
      */
-    public void setPredictedApps(List<ComponentKey> apps) {
+    public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
         mApps.setPredictedApps(apps);
     }
 
@@ -232,11 +229,9 @@
         mAppsRecyclerView.preMeasureViews(mAdapter);
         mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            getRevealView().setVisibility(View.VISIBLE);
-            getContentView().setVisibility(View.VISIBLE);
-            getContentView().setBackground(null);
-        }
+        getRevealView().setVisibility(View.VISIBLE);
+        getContentView().setVisibility(View.VISIBLE);
+        getContentView().setBackground(null);
     }
 
     public SearchUiManager getSearchUiManager() {
@@ -254,32 +249,15 @@
         // Update the number of items in the grid before we measure the view
         grid.updateAppsViewNumCols();
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
-            if (mNumAppsPerRow != grid.inv.numColumns ||
-                    mNumPredictedAppsPerRow != grid.inv.numColumns) {
-                mNumAppsPerRow = grid.inv.numColumns;
-                mNumPredictedAppsPerRow = grid.inv.numColumns;
-
-                mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
-                mAdapter.setNumAppsPerRow(mNumAppsPerRow);
-                mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
-            }
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            return;
-        }
-
-        // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
-        if (mNumAppsPerRow != grid.allAppsNumCols ||
-                mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
-            mNumAppsPerRow = grid.allAppsNumCols;
-            mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
+        if (mNumAppsPerRow != grid.inv.numColumns ||
+                mNumPredictedAppsPerRow != grid.inv.numColumns) {
+            mNumAppsPerRow = grid.inv.numColumns;
+            mNumPredictedAppsPerRow = grid.inv.numColumns;
 
             mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
             mAdapter.setNumAppsPerRow(mNumAppsPerRow);
             mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
         }
-
-        // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
@@ -372,8 +350,6 @@
             ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
             navBarBgLp.height = insets.bottom;
             navBarBg.setLayoutParams(navBarBgLp);
-            navBarBg.setVisibility(FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
-                    ? View.INVISIBLE : View.VISIBLE);
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index ba4fbe0..1f60fcc 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -448,9 +448,20 @@
                 row = Math.abs(numTotalRows - row);
             }
 
-            // We manipulate the stiffness, min, and max values based on the items distance to the
-            // first row and the items distance to the center column to create the ^-shaped motion
-            // effect.
+            calculateSpringValues(spring, row, col);
+        }
+
+        @Override
+        public void setDefaultValues(SpringAnimation spring) {
+            calculateSpringValues(spring, 0, mAppsPerRow / 2);
+        }
+
+        /**
+         * We manipulate the stiffness, min, and max values based on the items distance to the
+         * first row and the items distance to the center column to create the ^-shaped motion
+         * effect.
+         */
+        private void calculateSpringValues(SpringAnimation spring, int row, int col) {
             float rowFactor = (1 + row) * 0.5f;
             float colFactor = getColumnFactor(col, mAppsPerRow);
 
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index edfe0c1..743b16e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -7,6 +7,7 @@
 import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.graphics.Color;
+import android.support.animation.SpringAnimation;
 import android.support.v4.graphics.ColorUtils;
 import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.view.MotionEvent;
@@ -98,9 +99,10 @@
 
     // Used in discovery bounce animation to provide the transition without workspace changing.
     private boolean mIsTranslateWithoutWorkspace = false;
-    private AnimatorSet mDiscoBounceAnimation;
+    private Animator mDiscoBounceAnimation;
     private GradientView mGradientView;
 
+    private SpringAnimation mSearchSpring;
     private SpringAnimationHandler mSpringAnimationHandler;
 
     public AllAppsTransitionController(Launcher l) {
@@ -226,6 +228,7 @@
                 }
                 mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */);
                 if (hasSpringAnimationHandler()) {
+                    mSpringAnimationHandler.add(mSearchSpring, true /* setDefaultValues */);
                     // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
                     mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
                 }
@@ -418,8 +421,8 @@
         cancelDiscoveryAnimation();
 
         // assumption is that this variable is always null
-        mDiscoBounceAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(mLauncher,
-                R.anim.discovery_bounce);
+        mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher,
+                R.animator.discovery_bounce);
         mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animator) {
@@ -499,6 +502,7 @@
     public void finishPullUp() {
         mHotseat.setVisibility(View.INVISIBLE);
         if (hasSpringAnimationHandler()) {
+            mSpringAnimationHandler.remove(mSearchSpring);
             mSpringAnimationHandler.reset();
         }
         setProgress(0f);
@@ -544,6 +548,7 @@
                 mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
         mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
         mSpringAnimationHandler = mAppsView.getSpringAnimationHandler();
+        mSearchSpring = mAppsView.getSearchUiManager().getSpringForFling();
     }
 
     private boolean hasSpringAnimationHandler() {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 5e7a5ca..6bbe3ea 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -29,6 +29,7 @@
 import com.android.launcher3.discovery.AppDiscoveryItem;
 import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 import com.android.launcher3.util.LabelComparator;
 
 import java.util.ArrayList;
@@ -173,7 +174,7 @@
     // The set of sections that we allow fast-scrolling to (includes non-merged sections)
     private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
     // The set of predicted app component names
-    private final List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
+    private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
     // The set of predicted apps resolved from the component names and the current set of apps
     private final List<AppInfo> mPredictedApps = new ArrayList<>();
     private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
@@ -298,20 +299,20 @@
         updateAdapterItems();
     }
 
-    private List<AppInfo> processPredictedAppComponents(List<ComponentKey> components) {
+    private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
         if (mComponentToAppMap.isEmpty()) {
             // Apps have not been bound yet.
             return Collections.emptyList();
         }
 
         List<AppInfo> predictedApps = new ArrayList<>();
-        for (ComponentKey ck : components) {
-            AppInfo info = mComponentToAppMap.get(ck);
+        for (ComponentKeyMapper<AppInfo> mapper : components) {
+            AppInfo info = mapper.getItem(mComponentToAppMap);
             if (info != null) {
                 predictedApps.add(info);
             } else {
                 if (FeatureFlags.IS_DOGFOOD_BUILD) {
-                    Log.e(TAG, "Predicted app not found: " + ck);
+                    Log.e(TAG, "Predicted app not found: " + mapper);
                 }
             }
             // Stop at the number of predicted apps
@@ -331,7 +332,7 @@
      * If the number of predicted apps is the same as the previous list of predicted apps,
      * we can optimize by swapping them in place.
      */
-    public void setPredictedApps(List<ComponentKey> apps) {
+    public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
         mPredictedAppComponents.clear();
         mPredictedAppComponents.addAll(apps);
 
@@ -472,14 +473,14 @@
 
         if (DEBUG_PREDICTIONS) {
             if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
-                mPredictedAppComponents.add(new ComponentKey(mApps.get(0).componentName,
-                        Process.myUserHandle()));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
+                mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
+                        Process.myUserHandle())));
             }
         }
 
@@ -644,8 +645,8 @@
         return result;
     }
 
-    public AppInfo findApp(ComponentKey key) {
-        return mComponentToAppMap.get(key);
+    public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
+        return mapper.getItem(mComponentToAppMap);
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index c0d7850..34230e0 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps;
 
+import android.support.animation.SpringAnimation;
+import android.support.annotation.NonNull;
 import android.view.KeyEvent;
 
 /**
@@ -28,6 +30,11 @@
     void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView);
 
     /**
+     * A {@link SpringAnimation} that will be used when the user flings.
+     */
+    @NonNull SpringAnimation getSpringForFling();
+
+    /**
      * Notifies the search manager that the apps-list has changed and the search UI should be
      * updated accordingly.
      */
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 39e2088..ddf6e58 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -17,6 +17,9 @@
 
 import android.content.Context;
 import android.graphics.Rect;
+import android.support.animation.FloatValueHolder;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.text.Selection;
@@ -62,6 +65,8 @@
     private View mDivider;
     private HeaderElevationController mElevationController;
 
+    private SpringAnimation mSpring;
+
     public AppsSearchContainerLayout(Context context) {
         this(context, null);
     }
@@ -81,6 +86,9 @@
 
         mSearchQueryBuilder = new SpannableStringBuilder();
         Selection.setSelection(mSearchQueryBuilder, 0);
+
+        // Note: This spring does nothing.
+        mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
     }
 
     @Override
@@ -108,8 +116,7 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
-                !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
             getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
         }
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -128,6 +135,11 @@
     }
 
     @Override
+    public @NonNull SpringAnimation getSpringForFling() {
+        return mSpring;
+    }
+
+    @Override
     public void refreshSearchResult() {
         mSearchBarController.refreshSearchResult();
     }
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
index 3e58adc..eec3a48 100644
--- a/src/com/android/launcher3/anim/SpringAnimationHandler.java
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java
@@ -70,6 +70,20 @@
     }
 
     /**
+     * Adds a spring to the list of springs handled by this class.
+     * @param spring The new spring to be added.
+     * @param setDefaultValues If True, sets the spring to the default
+     *                         {@link AnimationFactory} values.
+     */
+    public void add(SpringAnimation spring, boolean setDefaultValues) {
+        if (setDefaultValues) {
+            mAnimationFactory.setDefaultValues(spring);
+        }
+        spring.setStartVelocity(mCurrentVelocity);
+        mAnimations.add(spring);
+    }
+
+    /**
      * Adds a new or recycled animation to the list of springs handled by this class.
      *
      * @param view The view the spring is attached to.
@@ -82,15 +96,17 @@
             view.setTag(R.id.spring_animation_tag, spring);
         }
         mAnimationFactory.update(spring, object);
-        spring.setStartVelocity(mCurrentVelocity);
-        mAnimations.add(spring);
+        add(spring, false /* setDefaultValues */);
     }
 
     /**
      * Stops and removes the spring attached to {@param view}.
      */
     public void remove(View view) {
-        SpringAnimation animation = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+        remove((SpringAnimation) view.getTag(R.id.spring_animation_tag));
+    }
+
+    public void remove(SpringAnimation animation) {
         if (animation.canSkipToEnd()) {
             animation.skipToEnd();
         }
@@ -226,6 +242,11 @@
          * Updates the value of {@param spring} based on {@param object}.
          */
         void update(SpringAnimation spring, T object);
+
+        /**
+         * Sets the factory default values for the given {@param spring}.
+         */
+        void setDefaultValues(SpringAnimation spring);
     }
 
     /**
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 4e00eae..a77a87f 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -40,7 +40,7 @@
     public static AppWidgetManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isAtLeastO()) {
+                if (Utilities.ATLEAST_OREO) {
                     sInstance = new AppWidgetManagerCompatVO(context.getApplicationContext());
                 } else {
                     sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 75a2a5d..2cac536 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -53,7 +53,7 @@
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isAtLeastO()) {
+                if (Utilities.ATLEAST_OREO) {
                     sInstance = new LauncherAppsCompatVO(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
index cbcabdf..00258c7 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompat.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
@@ -31,10 +31,10 @@
             if (sInstance == null) {
                 context = context.getApplicationContext();
 
-                if (Utilities.isAtLeastO()) {
+                if (Utilities.ATLEAST_OREO) {
                     try {
                         sInstance = new WallpaperManagerCompatVOMR1(context);
-                    } catch (Exception e) {
+                    } catch (Throwable e) {
                         // The wallpaper APIs do not yet exist
                     }
                 }
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
index 6233fab..524f266 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
@@ -16,96 +16,70 @@
 package com.android.launcher3.compat;
 
 import android.annotation.TargetApi;
+import android.app.WallpaperColors;
 import android.app.WallpaperManager;
+import android.app.WallpaperManager.OnColorsChangedListener;
 import android.content.Context;
 import android.graphics.Color;
-import android.os.Build;
 import android.support.annotation.Nullable;
 import android.util.Log;
 
-import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
 
-@TargetApi(Build.VERSION_CODES.O)
+
+@TargetApi(27)
 public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
 
     private static final String TAG = "WMCompatVOMR1";
 
     private final WallpaperManager mWm;
+    private Method mWCColorHintsMethod;
 
-    private final Class mOCLClass;
-    private final Method mAddOCLMethod;
-
-    private final Method mWCGetMethod;
-    private final Method mWCGetPrimaryColorMethod;
-    private final Method mWCGetSecondaryColorMethod;
-    private final Method mWCGetTertiaryColorMethod;
-    private final Method mWCColorHintsMethod;
-
-    WallpaperManagerCompatVOMR1(Context context) throws Exception {
+    WallpaperManagerCompatVOMR1(Context context) throws Throwable {
         mWm = context.getSystemService(WallpaperManager.class);
-
-        mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener");
-        mAddOCLMethod = WallpaperManager.class.getDeclaredMethod(
-                "addOnColorsChangedListener", mOCLClass);
-
-        mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
-        Class wallpaperColorsClass = mWCGetMethod.getReturnType();
-        mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor");
-        mWCGetSecondaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getSecondaryColor");
-        mWCGetTertiaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getTertiaryColor");
-        mWCColorHintsMethod = wallpaperColorsClass.getDeclaredMethod("getColorHints");
+        String className = WallpaperColors.class.getName();
+        try {
+            mWCColorHintsMethod = WallpaperColors.class.getDeclaredMethod("getColorHints");
+        } catch (Exception exc) {
+            Log.e(TAG, "getColorHints not available", exc);
+        }
     }
 
     @Nullable
     @Override
     public WallpaperColorsCompat getWallpaperColors(int which) {
-        try {
-            return convertColorsObject(mWCGetMethod.invoke(mWm, which));
-        } catch (Exception e) {
-            Log.e(TAG, "Error calling wallpaper API", e);
-            return null;
-        }
+        return convertColorsObject(mWm.getWallpaperColors(which));
     }
 
     @Override
     public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
-        Object onChangeListener = Proxy.newProxyInstance(
-                WallpaperManager.class.getClassLoader(),
-                new Class[]{mOCLClass},
-                new InvocationHandler() {
-                    @Override
-                    public Object invoke(Object o, Method method, Object[] objects)
-                            throws Throwable {
-                        String methodName = method.getName();
-                        if ("onColorsChanged".equals(methodName)) {
-                            listener.onColorsChanged(
-                                    convertColorsObject(objects[0]), (Integer) objects[1]);
-                        } else if ("toString".equals(methodName)) {
-                            return listener.toString();
-                        }
-                        return null;
-                    }
-                });
-        try {
-            mAddOCLMethod.invoke(mWm, onChangeListener);
-        } catch (Exception e) {
-            Log.e(TAG, "Error calling wallpaper API", e);
-        }
+        OnColorsChangedListener onChangeListener = new OnColorsChangedListener() {
+            @Override
+            public void onColorsChanged(WallpaperColors colors, int which) {
+                listener.onColorsChanged(convertColorsObject(colors), which);
+            }
+        };
+        mWm.addOnColorsChangedListener(onChangeListener, null);
     }
 
-    private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception {
+    private WallpaperColorsCompat convertColorsObject(WallpaperColors colors) {
         if (colors == null) {
             return null;
         }
-        Color primary = (Color) mWCGetPrimaryColorMethod.invoke(colors);
-        Color secondary = (Color) mWCGetSecondaryColorMethod.invoke(colors);
-        Color tertiary = (Color) mWCGetTertiaryColorMethod.invoke(colors);
+        Color primary = colors.getPrimaryColor();
+        Color secondary = colors.getSecondaryColor();
+        Color tertiary = colors.getTertiaryColor();
         int primaryVal = primary != null ? primary.toArgb() : 0;
         int secondaryVal = secondary != null ? secondary.toArgb() : 0;
         int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
-        int colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
+        int colorHints = 0;
+        try {
+            if (mWCColorHintsMethod != null) {
+                colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
+            }
+        } catch (Exception exc) {
+            Log.e(TAG, "error calling color hints", exc);
+        }
         return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
     }
 }
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 7964dd1..6a4cbcb 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -34,7 +34,6 @@
     public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
     public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
     public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
-    public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
     public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
     // When enabled allows to use any point on the fast scrollbar to start dragging.
     public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
@@ -57,8 +56,6 @@
     public static final boolean PULLDOWN_SEARCH = false;
     // When enabled the status bar may show dark icons based on the top of the wallpaper.
     public static final boolean LIGHT_STATUS_BAR = false;
-    // When enabled icons are badged with the number of notifications associated with that app.
-    public static final boolean BADGE_ICONS = true;
     // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
     public static final boolean LEGACY_ICON_TREATMENT = true;
     // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index be5f01a..fde7995 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -243,7 +243,7 @@
             return true;
         }
 
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && mAllAppsController.onControllerInterceptTouchEvent(ev)) {
+        if (mAllAppsController.onControllerInterceptTouchEvent(ev)) {
             mActiveController = mAllAppsController;
             return true;
         }
@@ -544,7 +544,6 @@
 
     public void clearResizeFrame() {
         if (mCurrentResizeFrame != null) {
-            mCurrentResizeFrame.commitResize();
             removeView(mCurrentResizeFrame);
             mCurrentResizeFrame = null;
         }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index e81e2a3..33d4fa6 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -193,7 +193,7 @@
      */
     @TargetApi(Build.VERSION_CODES.O)
     public void setItemInfo(final ItemInfo info) {
-        if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) {
+        if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.ATLEAST_OREO)) {
             return;
         }
         if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index c8d3890..b9d97ac 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -128,7 +128,7 @@
     }
 
     public static boolean handleDragRequest(Launcher launcher, Intent intent) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return false;
         }
         if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) {
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 52abbc7..a70a9bb 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -25,7 +25,9 @@
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Process;
 
+import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
@@ -69,8 +71,12 @@
 
     @Override
     public Drawable getFullResIcon(IconCache cache) {
-        return mContext.getSystemService(LauncherApps.class)
+        Drawable d = mContext.getSystemService(LauncherApps.class)
                 .getShortcutIconDrawable(mInfo, LauncherAppState.getIDP(mContext).fillResIconDpi);
+        if (d == null) {
+            d = new FastBitmapDrawable(cache.getDefaultIcon(Process.myUserHandle()));
+        }
+        return d;
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6533b04..8339bc5 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -61,6 +61,7 @@
 import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.badge.FolderBadgeInfo;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.BaseItemDragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.graphics.IconPalette;
@@ -375,6 +376,9 @@
         if (d.dragInfo instanceof AppInfo) {
             // Came from all apps -- make a copy
             item = ((AppInfo) d.dragInfo).makeShortcut();
+        } else if (d.dragSource instanceof BaseItemDragListener){
+            // Came from a different window -- make a copy
+            item = new ShortcutInfo((ShortcutInfo) d.dragInfo);
         } else {
             item = (ShortcutInfo) d.dragInfo;
         }
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 45344c0..371479b 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -80,7 +80,7 @@
 
 
     protected Path getPreloadProgressPath(Context context) {
-        if (Utilities.isAtLeastO()) {
+        if (Utilities.ATLEAST_OREO) {
             try {
                 // Try to load the path from Mask Icon
                 Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 8ed62bc..28fc423 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -231,7 +231,7 @@
      */
     public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
             @Nullable Path path, @Nullable boolean[] outMaskShape) {
-        if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable &&
+        if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
                 mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
             if (outBounds != null) {
                 outBounds.set(mAdaptiveIconBounds);
@@ -347,7 +347,7 @@
         float areaScale = area / (width * height);
         // Use sqrt of the final ratio as the images is scaled across both width and height.
         float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
-        if (Utilities.isAtLeastO() && d instanceof AdaptiveIconDrawable &&
+        if (Utilities.ATLEAST_OREO && d instanceof AdaptiveIconDrawable &&
                 mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
             mAdaptiveIconScale = scale;
             mAdaptiveIconBounds.set(mBounds);
diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
index e2d1d50..223243b 100644
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ b/src/com/android/launcher3/graphics/IconShapeOverride.java
@@ -59,7 +59,7 @@
     private static final int RESTART_REQUEST_CODE = 42; // the answer to everything
 
     public static boolean isSupported(Context context) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return false;
         }
         // Only supported when developer settings is enabled
@@ -82,7 +82,7 @@
     }
 
     public static void apply(Context context) {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         String path = getAppliedValue(context);
@@ -130,7 +130,7 @@
         private final int mOverrideId;
         private final String mOverrideValue;
 
-        @SuppressWarnings("deprecated")
+        @SuppressWarnings("deprecation")
         public ResourcesOverride(Resources parent, int overrideId, String overrideValue) {
             super(parent.getAssets(), parent.getDisplayMetrics(), parent.getConfiguration());
             mOverrideId = overrideId;
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index d955674..d55baf0 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -38,6 +38,7 @@
 import android.support.annotation.Nullable;
 
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
@@ -103,7 +104,7 @@
         float scale = 1f;
         if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
             normalizer = IconNormalizer.getInstance(context);
-            if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
+            if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                 boolean[] outShape = new boolean[1];
                 AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
@@ -122,7 +123,7 @@
             }
         }
         Bitmap bitmap = createIconBitmap(icon, context, scale);
-        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
+        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                 icon instanceof AdaptiveIconDrawable) {
             bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
         }
@@ -157,13 +158,13 @@
         float scale = 1f;
         if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
             normalizer = IconNormalizer.getInstance(context);
-            if (Utilities.isAtLeastO() && iconAppTargetSdk >= Build.VERSION_CODES.O) {
+            if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                 boolean[] outShape = new boolean[1];
                 AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                         context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
                 dr.setBounds(0, 0, 1, 1);
                 scale = normalizer.getScale(icon, iconBounds, dr.getIconMask(), outShape);
-                if (Utilities.isAtLeastO() && FeatureFlags.LEGACY_ICON_TREATMENT &&
+                if (Utilities.ATLEAST_OREO && FeatureFlags.LEGACY_ICON_TREATMENT &&
                         !outShape[0]) {
                     Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale);
                     if (wrappedIcon != icon) {
@@ -192,13 +193,16 @@
      * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions.
      */
     public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) {
+        return badgeWithDrawable(srcTgt, new FastBitmapDrawable(badge), context);
+    }
+
+    public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) {
         int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
         synchronized (sCanvas) {
             sCanvas.setBitmap(srcTgt);
-            sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()),
-                    new Rect(srcTgt.getWidth() - badgeSize,
-                            srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()),
-                    new Paint(Paint.FILTER_BITMAP_FLAG));
+            int iconSize = srcTgt.getWidth();
+            badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
+            badge.draw(sCanvas);
             sCanvas.setBitmap(null);
         }
         return srcTgt;
@@ -209,12 +213,12 @@
      */
     public static Bitmap createIconBitmap(Drawable icon, Context context) {
         float scale = 1f;
-        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
+        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                 icon instanceof AdaptiveIconDrawable) {
             scale = ShadowGenerator.getScaleForBounds(new RectF(0, 0, 0, 0));
         }
         Bitmap bitmap =  createIconBitmap(icon, context, scale);
-        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.isAtLeastO() &&
+        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                 icon instanceof AdaptiveIconDrawable) {
             bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
         }
@@ -267,7 +271,7 @@
             final int top = (textureHeight-height) / 2;
 
             sOldBounds.set(icon.getBounds());
-            if (Utilities.isAtLeastO() && icon instanceof AdaptiveIconDrawable) {
+            if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
                 int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
                         Math.min(left, top));
                 int size = Math.max(width, height);
@@ -292,7 +296,7 @@
      * create AdaptiveIconDrawable.
      */
     static Drawable wrapToAdaptiveIconDrawable(Context context, Drawable drawable, float scale) {
-        if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.isAtLeastO())) {
+        if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) {
             return drawable;
         }
 
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
index ffcedb2..b40bf78 100644
--- a/src/com/android/launcher3/graphics/ShadowDrawable.java
+++ b/src/com/android/launcher3/graphics/ShadowDrawable.java
@@ -146,7 +146,7 @@
             d.draw(canvas);
         }
 
-        if (Utilities.isAtLeastO()) {
+        if (Utilities.ATLEAST_OREO) {
             bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
         }
         mState.mLastDrawnBitmap = bitmap;
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index ebb69c4..81333b1 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -105,13 +105,13 @@
     private static String getItemStr(Target t) {
         String typeStr = getFieldName(t.itemType, ItemType.class);
         if (t.packageNameHash != 0) {
-            typeStr += ", packageHash=" + t.packageNameHash;
+            typeStr += ", packageHash=" + t.packageNameHash + ", predictiveRank=" + t.predictedRank;
         }
         if (t.componentHash != 0) {
-            typeStr += ", componentHash=" + t.componentHash;
+            typeStr += ", componentHash=" + t.componentHash + ", predictiveRank=" + t.predictedRank;
         }
         if (t.intentHash != 0) {
-            typeStr += ", intentHash=" + t.intentHash;
+            typeStr += ", intentHash=" + t.intentHash + ", predictiveRank=" + t.predictedRank;
         }
         return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
                 + "), pageIdx=" + t.pageIndex;
@@ -125,9 +125,11 @@
 
     public static Target newItemTarget(ItemInfo info) {
         Target t = newTarget(Target.Type.ITEM);
+
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 t.itemType = ItemType.APP_ICON;
+                t.predictedRank = -100; // Never assigned
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 t.itemType = ItemType.SHORTCUT;
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9b4510f..d5b5aa7 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.LauncherModel.Callbacks;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 
 import java.util.ArrayList;
@@ -94,19 +95,12 @@
 
 
     public void bindUpdatedShortcuts(
-            ArrayList<ShortcutInfo> updatedShortcuts, UserHandle user) {
-        bindUpdatedShortcuts(updatedShortcuts, new ArrayList<ShortcutInfo>(), user);
-    }
-
-    public void bindUpdatedShortcuts(
-            final ArrayList<ShortcutInfo> updatedShortcuts,
-            final ArrayList<ShortcutInfo> removedShortcuts,
-            final UserHandle user) {
-        if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
+            final ArrayList<ShortcutInfo> updatedShortcuts, final UserHandle user) {
+        if (!updatedShortcuts.isEmpty()) {
             scheduleCallbackTask(new CallbackTask() {
                 @Override
                 public void execute(Callbacks callbacks) {
-                    callbacks.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user);
+                    callbacks.bindShortcutsChanged(updatedShortcuts, user);
                 }
             });
         }
@@ -132,4 +126,16 @@
             }
         });
     }
+
+    public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) {
+        getModelWriter().deleteItemsFromDatabase(matcher);
+
+        // Call the components-removed callback
+        scheduleCallbackTask(new CallbackTask() {
+            @Override
+            public void execute(Callbacks callbacks) {
+                callbacks.bindWorkspaceComponentsRemoved(matcher);
+            }
+        });
+    }
 }
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 7a27741..0139bd9 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -88,7 +88,7 @@
             case OP_CACHE_UPDATE:
                 return true;
             case OP_SESSION_UPDATE:
-                return si.isPromise();
+                return si.hasPromiseIconUi();
             default:
                 return false;
         }
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index c56325a..4756edc 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -431,6 +431,10 @@
                                 }
                             }
 
+                            if ((c.restoreFlag & ShortcutInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
+                                validTarget = false;
+                            }
+
                             if (validTarget) {
                                 // The shortcut points to a valid target (either no target
                                 // or something which is ready to be used)
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 1e0af68..32dfe25 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -16,6 +16,9 @@
 package com.android.launcher3.model;
 
 import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
@@ -28,6 +31,7 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.util.InstantAppResolver;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -46,6 +50,17 @@
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
+            try {
+                // For instant apps we do not get package-add. Use setting events to update
+                // any pinned icons.
+                ApplicationInfo ai = app.getContext()
+                        .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
+                if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
+                    app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Ignore
+            }
             // Ignore install success events as they are handled by Package add events.
             return;
         }
@@ -94,7 +109,7 @@
                 if (info instanceof ShortcutInfo) {
                     ShortcutInfo si = (ShortcutInfo) info;
                     ComponentName cn = si.getTargetComponent();
-                    if (si.isPromise() && (cn != null)
+                    if (si.hasPromiseIconUi() && (cn != null)
                             && mInstallInfo.packageName.equals(cn.getPackageName())) {
                         si.setInstallProgress(mInstallInfo.progress);
                         if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index c6e878c..78ecbc6 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
+
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.IconCache;
@@ -32,7 +33,6 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.ShortcutInfo;
@@ -43,8 +43,10 @@
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -99,10 +101,11 @@
                     appsList.addPackage(context, packages[i], mUser);
 
                     // Automatically add homescreen icon for work profile apps for below O device.
-                    if (!Utilities.isAtLeastO() && !Process.myUserHandle().equals(mUser)) {
+                    if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
                         SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
                     }
                 }
+                flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
@@ -169,12 +172,15 @@
             }
         }
 
+        final LongArrayMap<Boolean> removedShortcuts = new LongArrayMap<>();
+
         // Update shortcut infos
         if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
             final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
-            final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<>();
             final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
 
+            // For system apps, package manager send OP_UPDATE when an app is enabled.
+            final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
             synchronized (dataModel) {
                 for (ItemInfo info : dataModel.itemsIdMap) {
                     if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
@@ -196,9 +202,14 @@
                         if (cn != null && matcher.matches(si, cn)) {
                             AppInfo appInfo = addedOrUpdatedApps.get(cn);
 
-                            // For system apps, package manager send OP_UPDATE when an
-                            // app is enabled.
-                            if (si.isPromise() && (mOp == OP_ADD || mOp == OP_UPDATE)) {
+                            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
+                                removedShortcuts.put(si.id, false);
+                                if (mOp == OP_REMOVE) {
+                                    continue;
+                                }
+                            }
+
+                            if (si.isPromise() && isNewApkAvailable) {
                                 if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
                                     // Auto install icon
                                     LauncherAppsCompat launcherApps
@@ -212,23 +223,23 @@
                                             appInfo = addedOrUpdatedApps.get(cn);
                                         }
 
-                                        if ((intent == null) || (appInfo == null)) {
-                                            removedShortcuts.add(si);
+                                        if (intent != null && appInfo != null) {
+                                            si.intent = intent;
+                                            si.status = ShortcutInfo.DEFAULT;
+                                            infoUpdated = true;
+                                        } else if (si.hasPromiseIconUi()) {
+                                            removedShortcuts.put(si.id, true);
                                             continue;
                                         }
-                                        si.intent = intent;
                                     }
-                                }
-
-                                si.status = ShortcutInfo.DEFAULT;
-                                infoUpdated = true;
-                                if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                                    iconCache.getTitleAndIcon(si, si.usingLowResIcon);
+                                } else {
+                                    si.status = ShortcutInfo.DEFAULT;
+                                    infoUpdated = true;
                                 }
                             }
 
-                            if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
-                                    && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                            if (isNewApkAvailable &&
+                                    si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
                                 iconCache.getTitleAndIcon(si, si.usingLowResIcon);
                                 infoUpdated = true;
                             }
@@ -246,7 +257,7 @@
                         if (infoUpdated) {
                             getModelWriter().updateItemInDatabase(si);
                         }
-                    } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
+                    } else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
                         LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
                         if (mUser.equals(widgetInfo.user)
                                 && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
@@ -267,9 +278,9 @@
                 }
             }
 
-            bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
+            bindUpdatedShortcuts(updatedShortcuts, mUser);
             if (!removedShortcuts.isEmpty()) {
-                getModelWriter().deleteItemsFromDatabase(removedShortcuts);
+                deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
             }
 
             if (!widgets.isEmpty()) {
@@ -306,22 +317,13 @@
         }
 
         if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
-            getModelWriter().deleteItemsFromDatabase(
-                    ItemInfoMatcher.ofPackages(removedPackages, mUser));
-            getModelWriter().deleteItemsFromDatabase(
-                    ItemInfoMatcher.ofComponents(removedComponents, mUser));
+            ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
+                    .or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
+                    .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
+            deleteAndBindComponentsRemoved(removeMatch);
 
             // Remove any queued items from the install queue
             InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
-
-            // Call the components-removed callback
-            scheduleCallbackTask(new CallbackTask() {
-                @Override
-                public void execute(Callbacks callbacks) {
-                    callbacks.bindWorkspaceComponentsRemoved(
-                            removedPackages, removedComponents, mUser);
-                }
-            });
         }
 
         if (!removedApps.isEmpty()) {
@@ -344,8 +346,9 @@
                     callbacks.notifyWidgetProvidersChanged();
                 }
             });
-        } else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
-            // Load widgets for the new package.
+        } else if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
+            // Load widgets for the new package. Changes due to app updates are handled through
+            // AppWidgetHost events, this is just to initialize the long-press options.
             for (int i = 0; i < N; i++) {
                 dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
             }
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 17cc238..c1f33a6 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -26,9 +26,12 @@
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.MultiHashMap;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -56,33 +59,35 @@
         deepShortcutManager.onShortcutsChanged(mShortcuts);
 
         // Find ShortcutInfo's that have changed on the workspace.
-        final ArrayList<ShortcutInfo> removedShortcutInfos = new ArrayList<>();
-        MultiHashMap<String, ShortcutInfo> idsToWorkspaceShortcutInfos = new MultiHashMap<>();
+        HashSet<ShortcutKey> removedKeys = new HashSet<>();
+        MultiHashMap<ShortcutKey, ShortcutInfo> keyToShortcutInfo = new MultiHashMap<>();
+        HashSet<String> allIds = new HashSet<>();
+
         for (ItemInfo itemInfo : dataModel.itemsIdMap) {
             if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                 ShortcutInfo si = (ShortcutInfo) itemInfo;
-                if (si.getIntent().getPackage().equals(mPackageName)
-                        && si.user.equals(mUser)) {
-                    idsToWorkspaceShortcutInfos.addToList(si.getDeepShortcutId(), si);
+                if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) {
+                    keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
+                    allIds.add(si.getDeepShortcutId());
                 }
             }
         }
 
         final ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
-        if (!idsToWorkspaceShortcutInfos.isEmpty()) {
+        if (!keyToShortcutInfo.isEmpty()) {
             // Update the workspace to reflect the changes to updated shortcuts residing on it.
             List<ShortcutInfoCompat> shortcuts = deepShortcutManager.queryForFullDetails(
-                    mPackageName, new ArrayList<>(idsToWorkspaceShortcutInfos.keySet()), mUser);
+                    mPackageName, new ArrayList<>(allIds), mUser);
             for (ShortcutInfoCompat fullDetails : shortcuts) {
-                List<ShortcutInfo> shortcutInfos = idsToWorkspaceShortcutInfos
-                        .remove(fullDetails.getId());
+                ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
+                List<ShortcutInfo> shortcutInfos = keyToShortcutInfo.remove(key);
                 if (!fullDetails.isPinned()) {
                     // The shortcut was previously pinned but is no longer, so remove it from
                     // the workspace and our pinned shortcut counts.
                     // Note that we put this check here, after querying for full details,
                     // because there's a possible race condition between pinning and
                     // receiving this callback.
-                    removedShortcutInfos.addAll(shortcutInfos);
+                    removedKeys.add(key);
                     continue;
                 }
                 for (final ShortcutInfo shortcutInfo : shortcutInfos) {
@@ -94,16 +99,14 @@
             }
         }
 
-        // If there are still entries in idsToWorkspaceShortcutInfos, that means that
+        // If there are still entries in keyToShortcutInfo, that means that
         // the corresponding shortcuts weren't passed in onShortcutsChanged(). This
         // means they were cleared, so we remove and unpin them now.
-        for (String id : idsToWorkspaceShortcutInfos.keySet()) {
-            removedShortcutInfos.addAll(idsToWorkspaceShortcutInfos.get(id));
-        }
+        removedKeys.addAll(keyToShortcutInfo.keySet());
 
-        bindUpdatedShortcuts(updatedShortcutInfos, removedShortcutInfos, mUser);
-        if (!removedShortcutInfos.isEmpty()) {
-            getModelWriter().deleteItemsFromDatabase(removedShortcutInfos);
+        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        if (!keyToShortcutInfo.isEmpty()) {
+            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
 
         if (mUpdateIdMap) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 802771f..8170f9a 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -29,9 +29,11 @@
 import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ItemInfoMatcher;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 
@@ -70,17 +72,19 @@
 
         // Update the workspace to reflect the changes to updated shortcuts residing on it.
         ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
-        ArrayList<ShortcutInfo> deletedShortcutInfos = new ArrayList<>();
+        HashSet<ShortcutKey> removedKeys = new HashSet<>();
+
         for (ItemInfo itemInfo : dataModel.itemsIdMap) {
             if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                     && mUser.equals(itemInfo.user)) {
                 ShortcutInfo si = (ShortcutInfo) itemInfo;
                 if (isUserUnlocked) {
-                    ShortcutInfoCompat shortcut = pinnedShortcuts.get(ShortcutKey.fromItemInfo(si));
+                    ShortcutKey key = ShortcutKey.fromItemInfo(si);
+                    ShortcutInfoCompat shortcut = pinnedShortcuts.get(key);
                     // We couldn't verify the shortcut during loader. If its no longer available
                     // (probably due to clear data), delete the workspace item as well
                     if (shortcut == null) {
-                        deletedShortcutInfos.add(si);
+                        removedKeys.add(key);
                         continue;
                     }
                     si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
@@ -93,9 +97,9 @@
                 updatedShortcutInfos.add(si);
             }
         }
-        bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser);
-        if (!deletedShortcutInfos.isEmpty()) {
-            getModelWriter().deleteItemsFromDatabase(deletedShortcutInfos);
+        bindUpdatedShortcuts(updatedShortcutInfos, mUser);
+        if (!removedKeys.isEmpty()) {
+            deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
         }
 
         // Remove shortcut id map for that user
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 2455eab..ad07d37 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -200,7 +200,9 @@
             PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
                     Launcher.getLauncher(getContext()));
             if (popup != null) {
-                Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight(),
+                final int newHeight = getResources().getDimensionPixelSize(
+                        R.dimen.notification_empty_footer_height);
+                Animator collapseFooter = popup.reduceNotificationViewHeight(getHeight() - newHeight,
                         getResources().getInteger(R.integer.config_removeNotificationViewDuration));
                 collapseFooter.addListener(new AnimatorListenerAdapter() {
                     @Override
@@ -208,7 +210,7 @@
                         ((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE);
                         // Keep view around because gutter is aligned to it, but remove height to
                         // both hide the view and keep calculations correct for last dismissal.
-                        getLayoutParams().height = 0;
+                        getLayoutParams().height = newHeight;
                         requestLayout();
                     }
                 });
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index f54a50d..26f9346 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -94,6 +94,9 @@
 
     @Override
     public void onClick(View view) {
+        if (intent == null) {
+            return;
+        }
         final Launcher launcher = Launcher.getLauncher(view.getContext());
         Bundle activityOptions = ActivityOptions.makeClipRevealAnimation(
                 view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 78c64d7..ab94c32 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -90,9 +90,19 @@
         return mMainView;
     }
 
+    /**
+     * This method is used to calculate the height to remove when dismissing the last notification.
+     * We subtract the height of the footer in this case since the footer should be gone or in the
+     * process of being removed.
+     * @return The height of the entire notification item, minus the footer if it still exists.
+     */
     public int getHeightMinusFooter() {
-        int footerHeight = mFooter.getParent() == null ? 0 : mFooter.getHeight();
-        return getHeight() - footerHeight;
+        if (mFooter.getParent() == null) {
+            return getHeight();
+        }
+        int excessFooterHeight = mFooter.getHeight() - getResources().getDimensionPixelSize(
+                R.dimen.notification_empty_footer_height);
+        return getHeight() - excessFooterHeight;
     }
 
     public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) {
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 6a70989..9126626 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -32,7 +32,6 @@
 import android.util.Pair;
 
 import com.android.launcher3.LauncherModel;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.SettingsObserver;
 
@@ -164,9 +163,6 @@
     }
 
     public static void setNotificationsChangedListener(NotificationsChangedListener listener) {
-        if (!FeatureFlags.BADGE_ICONS) {
-            return;
-        }
         sNotificationsChangedListener = listener;
 
         NotificationListener notificationListener = getInstanceIfConnected();
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
index 682d5a9..911be93 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
@@ -48,7 +48,6 @@
         setCaretDrawable(caretDrawable);
 
         Launcher l = Launcher.getLauncher(context);
-        setOnTouchListener(l.getHapticFeedbackTouchListener());
         setOnClickListener(l);
         setOnFocusChangeListener(l.mFocusHandler);
     }
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 29834d7..6281fec 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -141,7 +141,6 @@
         super.onFinishInflate();
         mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
         mAllAppsHandle.setImageDrawable(getCaretDrawable());
-        mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
         mAllAppsHandle.setOnClickListener(mLauncher);
         mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
         mLauncher.setAllAppsButton(mAllAppsHandle);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index c3e2d8b..8441598 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -275,8 +275,9 @@
             if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
                 mNotificationItemView = (NotificationItemView) item;
                 boolean notificationFooterHasIcons = numNotifications > 1;
-                int footerHeight = notificationFooterHasIcons ?
-                        res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
+                int footerHeight = res.getDimensionPixelSize(
+                        notificationFooterHasIcons ? R.dimen.notification_footer_height
+                                : R.dimen.notification_empty_footer_height);
                 item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
                 if (notificationFooterHasIcons) {
                     mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index d26f9f6..65acaa9 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -36,6 +36,7 @@
 
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -79,10 +80,15 @@
         private AppWidgetProviderInfo mWidgetInfo;
         private QsbWidgetHostView mQsb;
 
+        // We need to store the orientation here, due to a bug (b/64916689) that results in widgets
+        // being inflated in the wrong orientation.
+        private int mOrientation;
+
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             mQsbWidgetHost = new QsbWidgetHost(getActivity());
+            mOrientation = getContext().getResources().getConfiguration().orientation;
         }
 
         private FrameLayout mWrapper;
@@ -193,7 +199,7 @@
         @Override
         public void onResume() {
             super.onResume();
-            if (mQsb != null && mQsb.isReinflateRequired()) {
+            if (mQsb != null && mQsb.isReinflateRequired(mOrientation)) {
                 rebindFragment();
             }
         }
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index 8b6fa16..a8a41f6 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -47,9 +47,9 @@
     }
 
 
-    public boolean isReinflateRequired() {
+    public boolean isReinflateRequired(int orientation) {
         // Re-inflate is required if the orientation has changed since last inflation.
-        return mPreviousOrientation != getResources().getConfiguration().orientation;
+        return mPreviousOrientation != orientation;
     }
 
 
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 5ce78dc..f44f5c8 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -66,7 +66,7 @@
 
     public static boolean supportsShortcuts(ItemInfo info) {
         boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo
-                && ((com.android.launcher3.ShortcutInfo) info).isPromise();
+                && ((com.android.launcher3.ShortcutInfo) info).hasPromiseIconUi();
         return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                 && !info.isDisabled() && !isItemPromise;
     }
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 8d43518..5cb8c4c 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -11,6 +11,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherCallbacks;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.ComponentKeyMapper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -197,7 +198,7 @@
         }
 
         @Override
-        public List<ComponentKey> getPredictedApps() {
+        public List<ComponentKeyMapper<AppInfo>> getPredictedApps() {
             // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
             return new ArrayList<>();
         }
@@ -214,10 +215,5 @@
         @Override
         public void onDetachedFromWindow() {
         }
-
-        @Override
-        public boolean shouldShowDiscoveryBounce() {
-            return false;
-        }
     }
 }
diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java
new file mode 100644
index 0000000..916176a
--- /dev/null
+++ b/src/com/android/launcher3/util/ComponentKeyMapper.java
@@ -0,0 +1,50 @@
+package com.android.launcher3.util;
+
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ComponentKeyMapper<T> {
+
+    protected final ComponentKey mComponentKey;
+
+    public ComponentKeyMapper(ComponentKey key) {
+        this.mComponentKey = key;
+    }
+
+    public @Nullable T getItem(Map<ComponentKey, T> map) {
+        return map.get(mComponentKey);
+    }
+
+    public String getPackage() {
+        return mComponentKey.componentName.getPackageName();
+    }
+
+    public String getComponentClass() {
+        return mComponentKey.componentName.getClassName();
+    }
+
+    @Override
+    public String toString() {
+        return mComponentKey.toString();
+    }
+
+}
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
new file mode 100644
index 0000000..e60d768
--- /dev/null
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A wrapper class to access instant app related APIs.
+ */
+public class InstantAppResolver {
+
+    public static InstantAppResolver newInstance(Context context) {
+        return Utilities.getOverrideObject(
+                InstantAppResolver.class, context, R.string.instant_app_resolver_class);
+    }
+
+    public boolean isInstantApp(ApplicationInfo info) {
+        return false;
+    }
+
+    public List<ApplicationInfo> getInstantApps() {
+        return Collections.emptyList();
+    }
+}
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 42de284..18787b6 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.os.UserHandle;
+import android.util.SparseLongArray;
 
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
@@ -66,6 +67,32 @@
         return filtered;
     }
 
+    /**
+     * Returns a new matcher with returns true if either this or {@param matcher} returns true.
+     */
+    public ItemInfoMatcher or(final ItemInfoMatcher matcher) {
+       final ItemInfoMatcher that = this;
+        return new ItemInfoMatcher() {
+            @Override
+            public boolean matches(ItemInfo info, ComponentName cn) {
+                return that.matches(info, cn) || matcher.matches(info, cn);
+            }
+        };
+    }
+
+    /**
+     * Returns a new matcher with returns true if both this and {@param matcher} returns true.
+     */
+    public ItemInfoMatcher and(final ItemInfoMatcher matcher) {
+        final ItemInfoMatcher that = this;
+        return new ItemInfoMatcher() {
+            @Override
+            public boolean matches(ItemInfo info, ComponentName cn) {
+                return that.matches(info, cn) && matcher.matches(info, cn);
+            }
+        };
+    }
+
     public static ItemInfoMatcher ofUser(final UserHandle user) {
         return new ItemInfoMatcher() {
             @Override
@@ -104,4 +131,14 @@
             }
         };
     }
+
+    public static ItemInfoMatcher ofItemIds(
+            final LongArrayMap<Boolean> ids, final Boolean matchDefault) {
+        return new ItemInfoMatcher() {
+            @Override
+            public boolean matches(ItemInfo info, ComponentName cn) {
+                return ids.get(info.id, matchDefault);
+            }
+        };
+    }
 }
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 091dd84..009aee7 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -67,7 +67,7 @@
             return;
         }
 
-        if (Utilities.isAtLeastO() && !SessionCommitReceiver.isEnabled(context)) {
+        if (Utilities.ATLEAST_OREO && !SessionCommitReceiver.isEnabled(context)) {
             // Just mark the folder id preference to avoid new folder creation later.
             ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply();
             return;
diff --git a/src/com/android/launcher3/util/RunnableWithId.java b/src/com/android/launcher3/util/RunnableWithId.java
new file mode 100644
index 0000000..030eb09
--- /dev/null
+++ b/src/com/android/launcher3/util/RunnableWithId.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+/**
+ * A runnable with an id associated which is used for equality check.
+ */
+public abstract class RunnableWithId implements Runnable {
+
+    public static final int RUNNABLE_ID_BIND_APPS = 1;
+    public static final int RUNNABLE_ID_BIND_WIDGETS = 2;
+
+    public final int id;
+
+    public RunnableWithId(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof RunnableWithId && ((RunnableWithId) obj).id == id;
+    }
+}
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 6b5b095..edbf05a 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -30,6 +30,7 @@
     public static final int UI_STATE_BASE_WINDOW = 0;
     public static final int UI_STATE_ALL_APPS = 1;
     public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
+    public static final int UI_STATE_ROOT_VIEW = 3;
 
     public static final int FLAG_LIGHT_NAV = 1 << 0;
     public static final int FLAG_DARK_NAV = 1 << 1;
@@ -37,7 +38,7 @@
     public static final int FLAG_DARK_STATUS = 1 << 3;
 
     private final Window mWindow;
-    private final int[] mStates = new int[3];
+    private final int[] mStates = new int[4];
 
     public SystemUiController(Window window) {
         mWindow = window;
@@ -58,7 +59,7 @@
         // Apply the state flags in priority order
         int newFlags = oldFlags;
         for (int stateFlag : mStates) {
-            if (Utilities.isAtLeastO()) {
+            if (Utilities.ATLEAST_OREO) {
                 if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
                     newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
                 } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
diff --git a/src/com/android/launcher3/widget/WidgetListRowEntry.java b/src/com/android/launcher3/widget/WidgetListRowEntry.java
index 3e89eeb..335b8c7 100644
--- a/src/com/android/launcher3/widget/WidgetListRowEntry.java
+++ b/src/com/android/launcher3/widget/WidgetListRowEntry.java
@@ -41,4 +41,8 @@
         this.widgets = items;
     }
 
+    @Override
+    public String toString() {
+        return pkgItem.packageName + ":" + widgets.size();
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 14a9d17..acec3dd 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -17,10 +17,12 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.graphics.Point;
 import android.support.v7.widget.LinearLayoutManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Toast;
@@ -31,8 +33,10 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.model.PackageItemInfo;
@@ -74,7 +78,11 @@
     public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mLauncher = Launcher.getLauncher(context);
-        mAdapter = new WidgetsListAdapter(this, this, context);
+        LauncherAppState apps = LauncherAppState.getInstance(context);
+        mAdapter = new WidgetsListAdapter(context, LayoutInflater.from(context),
+                apps.getWidgetCache(), new AlphabeticIndexCompat(context), this, this,
+                new WidgetsDiffReporter(apps.getIconCache()));
+        mAdapter.setNotifyListener();
         if (LOGD) {
             Log.d(TAG, "WidgetsContainerView constructor");
         }
@@ -232,7 +240,6 @@
      */
     public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> model) {
         mAdapter.setWidgets(model);
-        mAdapter.notifyDataSetChanged();
 
         View loader = getContentView().findViewById(R.id.loader);
         if (loader != null) {
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
new file mode 100644
index 0000000..52deec3
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import android.util.Log;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Do diff on widget's tray list items and call the {@link NotifyListener} methods accordingly.
+ */
+public class WidgetsDiffReporter {
+    private final boolean DEBUG = false;
+    private final String TAG = "WidgetsDiffReporter";
+    private final IconCache mIconCache;
+    private NotifyListener mListener;
+
+    public interface NotifyListener {
+        void notifyDataSetChanged();
+        void notifyItemChanged(int index);
+        void notifyItemInserted(int index);
+        void notifyItemRemoved(int index);
+    }
+
+    public WidgetsDiffReporter(IconCache iconCache) {
+        mIconCache = iconCache;
+    }
+
+    public void setListener(NotifyListener listener) {
+        mListener = listener;
+    }
+
+    public void process(ArrayList<WidgetListRowEntry> currentEntries,
+            ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
+        if (DEBUG) {
+            Log.d(TAG, "process oldEntries#=" + currentEntries.size()
+                    + " newEntries#=" + newEntries.size());
+        }
+        if (currentEntries.size() == 0 && newEntries.size() > 0) {
+            currentEntries.addAll(newEntries);
+            mListener.notifyDataSetChanged();
+            return;
+        }
+        ArrayList<WidgetListRowEntry> orgEntries =
+                (ArrayList<WidgetListRowEntry>) currentEntries.clone();
+        Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
+        Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
+
+        WidgetListRowEntry orgRowEntry = orgIter.next();
+        WidgetListRowEntry newRowEntry = newIter.next();
+
+        do {
+            int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
+            if (DEBUG) {
+                Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
+                        diff, orgRowEntry != null? orgRowEntry.toString() : null,
+                        newRowEntry != null? newRowEntry.toString() : null));
+            }
+            int index = -1;
+            if (diff < 0) {
+                index = currentEntries.indexOf(orgRowEntry);
+                mListener.notifyItemRemoved(index);
+                if (DEBUG) {
+                    Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
+                            orgRowEntry.titleSectionName));
+                }
+                currentEntries.remove(index);
+                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+            } else if (diff > 0) {
+                index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
+                        currentEntries.size();
+                currentEntries.add(index, newRowEntry);
+                if (DEBUG) {
+                    Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
+                            newRowEntry.titleSectionName));
+                }
+                newRowEntry = newIter.hasNext() ? newIter.next() : null;
+                mListener.notifyItemInserted(index);
+
+            } else {
+                // same package name but,
+                // did the icon, title, etc, change?
+                // or did the widget size and desc, span, etc change?
+                if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
+                        !orgRowEntry.widgets.equals(newRowEntry.widgets)) {
+                    index = currentEntries.indexOf(orgRowEntry);
+                    currentEntries.set(index, newRowEntry);
+                    mListener.notifyItemChanged(index);
+                    if (DEBUG) {
+                        Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
+                                newRowEntry.titleSectionName));
+                    }
+                }
+                orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+                newRowEntry = newIter.hasNext() ? newIter.next() : null;
+            }
+        } while(orgRowEntry != null || newRowEntry != null);
+    }
+
+    /**
+     * Compare package name using the same comparator as in {@link WidgetsListAdapter}.
+     * Also handle null row pointers.
+     */
+    private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
+            WidgetListRowEntryComparator comparator) {
+        if (curRow == null && newRow == null) {
+            throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
+        }
+
+        if (curRow == null && newRow != null) {
+            return 1; // new row needs to be inserted
+        } else if (curRow != null && newRow == null) {
+            return -1; // old row needs to be deleted
+        }
+        return comparator.compare(curRow, newRow);
+    }
+
+    private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
+        return curInfo.iconBitmap.equals(newInfo.iconBitmap) &&
+                !mIconCache.isDefaultIcon(curInfo.iconBitmap, curInfo.user);
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index a1eb0ab..6b1800c 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -21,9 +21,10 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
@@ -55,40 +56,67 @@
 
     private final WidgetPreviewLoader mWidgetPreviewLoader;
     private final LayoutInflater mLayoutInflater;
-
-    private final View.OnClickListener mIconClickListener;
-    private final View.OnLongClickListener mIconLongClickListener;
-
-    private final ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
     private final AlphabeticIndexCompat mIndexer;
 
+    private final OnClickListener mIconClickListener;
+    private final OnLongClickListener mIconLongClickListener;
     private final int mIndent;
+    private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
+    private final WidgetsDiffReporter mDiffReporter;
 
-    public WidgetsListAdapter(View.OnClickListener iconClickListener,
-            View.OnLongClickListener iconLongClickListener,
-            Context context) {
-        mLayoutInflater = LayoutInflater.from(context);
-        mWidgetPreviewLoader = LauncherAppState.getInstance(context).getWidgetCache();
-
-        mIndexer = new AlphabeticIndexCompat(context);
-
+    public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
+            WidgetPreviewLoader widgetPreviewLoader, AlphabeticIndexCompat indexCompat,
+            OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
+            WidgetsDiffReporter diffReporter) {
+        mLayoutInflater = layoutInflater;
+        mWidgetPreviewLoader = widgetPreviewLoader;
+        mIndexer = indexCompat;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
+        mDiffReporter = diffReporter;
     }
 
-    public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
-        mEntries.clear();
-        WidgetItemComparator widgetComparator = new WidgetItemComparator();
+    public void setNotifyListener() {
+        mDiffReporter.setListener(new WidgetsDiffReporter.NotifyListener() {
+            @Override
+            public void notifyDataSetChanged() {
+                WidgetsListAdapter.this.notifyDataSetChanged();
+            }
 
+            @Override
+            public void notifyItemChanged(int index) {
+                WidgetsListAdapter.this.notifyItemChanged(index);
+            }
+
+            @Override
+            public void notifyItemInserted(int index) {
+                WidgetsListAdapter.this.notifyItemInserted(index);
+            }
+
+            @Override
+            public void notifyItemRemoved(int index) {
+                WidgetsListAdapter.this.notifyItemRemoved(index);
+            }
+        });
+    }
+
+    /**
+     * Update the widget list.
+     */
+    public void setWidgets(MultiHashMap<PackageItemInfo, WidgetItem> widgets) {
+        ArrayList<WidgetListRowEntry> tempEntries = new ArrayList<>();
+
+        WidgetItemComparator widgetComparator = new WidgetItemComparator();
         for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : widgets.entrySet()) {
             WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
             row.titleSectionName = mIndexer.computeSectionName(row.pkgItem.title);
             Collections.sort(row.widgets, widgetComparator);
-            mEntries.add(row);
+            tempEntries.add(row);
         }
-
-        Collections.sort(mEntries, new WidgetListRowEntryComparator());
+        WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
+        Collections.sort(tempEntries, rowComparator);
+        mDiffReporter.process(mEntries, tempEntries, rowComparator);
     }
 
     @Override
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 7fb5d85..1be33d2 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -103,7 +103,7 @@
      */
     protected UiObject2 openAllApps() {
         mDevice.waitForIdle();
-        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+        if (FeatureFlags.NO_ALL_APPS_ICON) {
             // clicking on the page indicator brings up all apps tray on non tablets.
             findViewById(R.id.page_indicator).click();
         } else {
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 4b9d83f..bd21315 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -141,7 +141,7 @@
 
     private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
             Intent... commandIntents) throws Throwable {
-        if (!Utilities.isAtLeastO()) {
+        if (!Utilities.ATLEAST_OREO) {
             return;
         }
         lockRotation(true);
diff --git a/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
new file mode 100644
index 0000000..40b65e4
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.LayoutInflater;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.util.MultiHashMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WidgetsListAdapterTest {
+
+    private final String TAG = "WidgetsListAdapterTest";
+
+    @Mock private LayoutInflater mMockLayoutInflater;
+    @Mock private WidgetPreviewLoader mMockWidgetCache;
+    @Mock private WidgetsDiffReporter.NotifyListener mListener;
+    @Mock private IconCache mIconCache;
+
+    private WidgetsListAdapter mAdapter;
+    private AlphabeticIndexCompat mIndexCompat;
+    private InvariantDeviceProfile mTestProfile;
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mTestProfile = new InvariantDeviceProfile();
+        mTestProfile.numRows = 5;
+        mTestProfile.numColumns = 5;
+        mIndexCompat = new AlphabeticIndexCompat(mContext);
+        WidgetsDiffReporter reporter = new WidgetsDiffReporter(mIconCache);
+        reporter.setListener(mListener);
+        mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
+                mIndexCompat, null, null, reporter);
+    }
+
+    @Test
+    public void test_notifyDataSetChanged() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(1));
+        verify(mListener, times(1)).notifyDataSetChanged();
+    }
+
+    @Test
+    public void test_notifyItemInserted() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(1));
+        mAdapter.setWidgets(generateSampleMap(2));
+        verify(mListener, times(1)).notifyDataSetChanged();
+        verify(mListener, times(1)).notifyItemInserted(1);
+    }
+
+    @Test
+    public void test_notifyItemRemoved() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(2));
+        mAdapter.setWidgets(generateSampleMap(1));
+        verify(mListener, times(1)).notifyDataSetChanged();
+        verify(mListener, times(1)).notifyItemRemoved(1);
+    }
+
+    @Test
+    public void testNotifyItemChanged_PackageIconDiff() throws Exception {
+        mAdapter.setWidgets(generateSampleMap(1));
+        mAdapter.setWidgets(generateSampleMap(1));
+        verify(mListener, times(1)).notifyDataSetChanged();
+        verify(mListener, times(1)).notifyItemChanged(0);
+    }
+
+    @Test
+    public void testNotifyItemChanged_widgetItemInfoDiff() throws Exception {
+        // TODO: same package name but item number changed
+    }
+
+    @Test
+    public void testNotifyItemInsertedRemoved_hodgepodge() throws Exception {
+        // TODO: insert and remove combined.          curMap
+        // newMap [A, C, D]                           [A, B, E]
+        // B - C < 0, removed B from index 1          [A, E]
+        // E - C > 0, C inserted to index 1           [A, C, E]
+        // E - D > 0, D inserted to index 2           [A, C, D, E]
+        // E - null = -1, E deleted from index 3      [A, C, D]
+    }
+
+    /**
+     * Helper method to generate the sample widget model map that can be used for the tests
+     * @param num the number of WidgetItem the map should contain
+     * @return
+     */
+    private MultiHashMap<PackageItemInfo, WidgetItem> generateSampleMap(int num) {
+        MultiHashMap<PackageItemInfo, WidgetItem> newMap = new MultiHashMap();
+        if (num <= 0) return newMap;
+
+        PackageManager pm = mContext.getPackageManager();
+        AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
+        for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(null)) {
+            WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
+                    .fromProviderInfo(mContext, widgetInfo), pm, mTestProfile);
+
+            PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
+            pInfo.title = pInfo.packageName;
+            pInfo.user = wi.user;
+            pInfo.iconBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8);
+            newMap.addToList(pInfo, wi);
+            if (newMap.size() == num) {
+                break;
+            }
+        }
+        return newMap;
+    }
+}