Merge "Quickly fade in QSB when swiping up when SWIPE_HOME = false" into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index 5688407..c583244 100644
--- a/Android.bp
+++ b/Android.bp
@@ -13,54 +13,19 @@
 // limitations under the License.
 
 java_library_static {
-    name: "launcher-log-proto-nano",
-    proto: {
-        type: "nano",
-	output_params: [
-	    "store_unknown_fields=true",
-	    "enum_style=java",
-	],
-	local_include_dirs: [
-	    "protos",
-	    "proto_overrides",
-	],
-    },
-    srcs: [
-        "protos/**/*.proto",
-        "proto_overrides/**/*.proto",
-    ],
-}
-
-android_library{
     name: "launcher-aosp-tapl",
     static_libs: [
         "androidx.annotation_annotation",
-        "androidx.dynamicanimation_dynamicanimation",
-        "androidx.recyclerview_recyclerview",
-        "androidx.preference_preference",
         "androidx.test.runner",
         "androidx.test.rules",
         "androidx.test.uiautomator_uiautomator",
-        "iconloader_base",
-        "launcher-log-proto-nano",
-        "launcherprotosnano",
         "SystemUISharedLib",
     ],
     srcs: [
         "tests/tapl/**/*.java",
-        "quickstep/src/**/*.java",
-        "quickstep/recents_ui_overrides/src/**/*.java",
-        "src/**/*.java",
-        "src_build_config/**/*.java",
-        "src_flags/**/*.java",
-        "src_plugins/**/*.java",
-        "src_shortcuts_overrides/**/*.java",
-    ],
-    dxflags: ["--multi-dex"],
-    resource_dirs: [
-        "res",
-        "quickstep/res",
-        "quickstep/recents_ui_overrides/res",
+        "quickstep/src/com/android/quickstep/SwipeUpSetting.java",
+        "src/com/android/launcher3/util/SecureSettingsObserver.java",
+        "src/com/android/launcher3/TestProtocol.java",
     ],
     platform_apis: true,
 }
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
new file mode 100644
index 0000000..90940c4
--- /dev/null
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.quickstep.views.TaskItemView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/task_icon_and_thumbnail"
+        android:layout_width="@dimen/task_thumbnail_icon_size"
+        android:layout_height="@dimen/task_thumbnail_icon_size"
+        android:layout_gravity="center_vertical"
+        android:layout_marginHorizontal="8dp"/>
+    <TextView
+        android:id="@+id/task_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginHorizontal="8dp"
+        android:singleLine="true"
+        android:textColor="@android:color/white"
+        android:textSize="24sp"/>
+</com.android.quickstep.views.TaskItemView>
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 57cd60a..d3fd240 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,16 +15,17 @@
  */
 package com.android.quickstep;
 
+import android.view.LayoutInflater;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
 
+import com.android.launcher3.R;
+import com.android.quickstep.views.TaskItemView;
 import com.android.systemui.shared.recents.model.Task;
 
 import java.util.ArrayList;
-
 /**
  * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
  * appropriate {@link Task} from the recents task list.
@@ -42,8 +43,9 @@
     @Override
     public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         // TODO: Swap in an actual task view here (view w/ icon, label, etc.)
-        TextView stubView = new TextView(parent.getContext());
-        return new TaskHolder(stubView);
+        TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.task_item_view, parent, false);
+        return new TaskHolder(itemView);
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 1ea6d76..47398c8 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,10 +15,9 @@
  */
 package com.android.quickstep;
 
-import android.widget.TextView;
-
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
+import com.android.quickstep.views.TaskItemView;
 import com.android.systemui.shared.recents.model.Task;
 
 /**
@@ -27,22 +26,21 @@
  */
 final class TaskHolder extends ViewHolder {
 
-    // TODO: Implement the actual task view to be held.
-    // For now, we just use a simple text view.
-    private final TextView mStubView;
+    private final TaskItemView mTaskItemView;
 
-    public TaskHolder(TextView stubView) {
-        super(stubView);
-        mStubView = stubView;
+    public TaskHolder(TaskItemView itemView) {
+        super(itemView);
+        mTaskItemView = itemView;
     }
 
     /**
      * Bind task content to the view. This includes the task icon and title as well as binding
      * input handlers such as which task to launch/remove.
      *
-     * @param task the task to bind to the view this
+     * @param task the task to bind to the view
      */
     public void bindTask(Task task) {
-        mStubView.setText("Stub task view: " + task.titleDescription);
+        mTaskItemView.setLabel(task.titleDescription);
+        mTaskItemView.setIcon(task.icon);
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index 9f359b4..c798cef 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -85,34 +85,40 @@
     }
 
     /**
-     * Loads task content for a list of tasks, including the label and the icon. Uses the list of
-     * tasks since the last load as a cache for loaded content.
+     * Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
+     * that isn't cached, load the content asynchronously in the background.
      *
      * @param tasksToLoad list of tasks that need to load their content
-     * @param onLoadedCallback runnable to run after all tasks have loaded their content
+     * @param onFullyLoadedCallback runnable to run after all tasks have loaded their content
      */
     private void loadTaskContents(ArrayList<Task> tasksToLoad,
-            @Nullable Runnable onLoadedCallback) {
-        AtomicInteger loadRequestsCount = new AtomicInteger(0);
+            @Nullable Runnable onFullyLoadedCallback) {
+        // Make two load requests per task, one for the icon/title and one for the thumbnail.
+        AtomicInteger loadRequestsCount = new AtomicInteger(tasksToLoad.size() * 2);
+        Runnable itemLoadedRunnable = () -> {
+            if (loadRequestsCount.decrementAndGet() == 0 && onFullyLoadedCallback != null) {
+                onFullyLoadedCallback.run();
+            }
+        };
         for (Task task : tasksToLoad) {
+            // Load icon and title.
             int index = mTaskList.indexOf(task);
             if (index >= 0) {
                 // If we've already loaded the task and have its content then just copy it over.
                 Task loadedTask = mTaskList.get(index);
                 task.titleDescription = loadedTask.titleDescription;
                 task.icon = loadedTask.icon;
+                itemLoadedRunnable.run();
             } else {
                 // Otherwise, load the content in the background.
-                loadRequestsCount.getAndIncrement();
-                mRecentsModel.getIconCache().updateIconInBackground(task, loadedTask -> {
-                    if (loadRequestsCount.decrementAndGet() == 0 && onLoadedCallback != null) {
-                        onLoadedCallback.run();
-                    }
-                });
+                mRecentsModel.getIconCache().updateIconInBackground(task,
+                        loadedTask -> itemLoadedRunnable.run());
             }
-        }
-        if (loadRequestsCount.get() == 0 && onLoadedCallback != null) {
-            onLoadedCallback.run();
+
+            // Load the thumbnail. May return immediately and synchronously if the thumbnail is
+            // cached.
+            mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
+                    thumbnail -> itemLoadedRunnable.run());
         }
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 2858deb..89a8454 100644
--- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -52,6 +52,7 @@
             ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
                     .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
             mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+            mRecentsModel.onInitializeSystemUI(bundle);
         }
 
         @Override
@@ -77,6 +78,11 @@
             mOverviewCommandHelper.onTip(actionType, viewType);
         }
 
+        @Override
+        public void onAssistantAvailable(boolean available) {
+            // TODO handle assistant
+        }
+
         /** Deprecated methods **/
         public void onQuickStep(MotionEvent motionEvent) { }
 
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
new file mode 100644
index 0000000..ce3947d
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+/**
+ * View representing an individual task item with the icon + thumbnail adjacent to the task label.
+ */
+public final class TaskItemView extends LinearLayout {
+
+    private TextView mLabelView;
+    private ImageView mIconView;
+
+    public TaskItemView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mLabelView = findViewById(R.id.task_label);
+        mIconView = findViewById(R.id.task_icon_and_thumbnail);
+    }
+
+    /**
+     * Set the label for the task item.
+     *
+     * @param label task label
+     */
+    public void setLabel(String label) {
+        mLabelView.setText(label);
+    }
+
+    /**
+     * Set the icon for the task item.
+     *
+     * @param icon task icon
+     */
+    public void setIcon(Drawable icon) {
+        mIconView.setImageDrawable(icon);
+        // TODO: Add in combination drawable for icon + thumbnail
+    }
+}
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 2b76924..4b64ad4 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 50cc4da..81ff0c7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -62,7 +62,6 @@
 
         if (swipeUpToHome) {
             list.add(new FlingAndHoldTouchController(launcher));
-            list.add(new OverviewToAllAppsTouchController(launcher));
         } else {
             if (launcher.getDeviceProfile().isVerticalBarLayout()) {
                 list.add(new OverviewToAllAppsTouchController(launcher));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index e02c696..109a4c5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -78,6 +78,11 @@
     }
 
     @Override
+    public int getType() {
+        return TYPE_ASSISTANT;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         // TODO add logging
         switch (ev.getActionMasked()) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
index 047f34c..d919a3e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java
@@ -31,6 +31,11 @@
     }
 
     @Override
+    public int getType() {
+        return TYPE_DEVICE_LOCKED;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         // For now, just start the home intent so user is prompted to unlock the device.
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
index 8dfb9ab..ad9fe78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -23,7 +23,16 @@
 
 @TargetApi(Build.VERSION_CODES.O)
 public interface InputConsumer {
-    InputConsumer NO_OP = new InputConsumer() { };
+
+    int TYPE_NO_OP = 0;
+    int TYPE_OVERVIEW = 1;
+    int TYPE_OTHER_ACTIVITY = 2;
+    int TYPE_ASSISTANT = 3;
+    int TYPE_DEVICE_LOCKED = 4;
+
+    InputConsumer NO_OP = () -> TYPE_NO_OP;
+
+    int getType();
 
     default boolean isActive() {
         return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 96f79a8..eee0344 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -30,11 +30,11 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.os.UserHandle;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -106,9 +106,10 @@
         final RecentsView recentsView = activity.getOverviewPanel();
         final TaskView runningTaskView = recentsView.getRunningTaskView();
         final View workspaceView;
-        if (runningTaskView != null) {
-            ComponentName component = runningTaskView.getTask().key.sourceComponent;
-            workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
+        if (runningTaskView != null && runningTaskView.getTask().key.getComponent() != null) {
+            workspaceView = activity.getWorkspace().getFirstMatchForAppClose(
+                    runningTaskView.getTask().key.getComponent().getPackageName(),
+                    UserHandle.of(runningTaskView.getTask().key.userId));
         } else {
             workspaceView = null;
         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 67bfeaa..e3afb92 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -148,6 +148,11 @@
     }
 
     @Override
+    public int getType() {
+        return TYPE_OTHER_ACTIVITY;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         if (mVelocityTracker == null) {
             return;
@@ -225,7 +230,7 @@
                             mTouchSlop) {
                         mPassedTouchSlop = true;
 
-                        TOUCH_INTERACTION_LOG.startQuickStep();
+                        TOUCH_INTERACTION_LOG.addLog("startQuickstep");
                         if (mIsDeferredDownTarget) {
                             // Deferred gesture, start the animation and gesture tracking once
                             // we pass the actual touch slop
@@ -288,7 +293,7 @@
     }
 
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
-        TOUCH_INTERACTION_LOG.startRecentsAnimation();
+        TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation");
 
         RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
         final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
index 28c4db4..cce5cb3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -60,6 +60,11 @@
     }
 
     @Override
+    public int getType() {
+        return TYPE_OVERVIEW;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         if (mInvalidated) {
             return;
@@ -120,7 +125,7 @@
             OverviewCallbacks.get(mActivity).closeAllWindows();
             ActivityManagerWrapper.getInstance()
                     .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-            TOUCH_INTERACTION_LOG.startQuickStep();
+            TOUCH_INTERACTION_LOG.addLog("startQuickstep");
         }
 
         mTrackingStarted = true;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
deleted file mode 100644
index 4b660d4..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.quickstep;
-
-import android.view.MotionEvent;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.LinkedList;
-
-/**
- * Keeps track of debugging logs for a particular quickstep gesture.
- */
-public class TouchInteractionLog {
-
-    // The number of gestures to log
-    private static final int MAX_NUM_LOG_GESTURES = 5;
-
-    private final Calendar mCalendar = Calendar.getInstance();
-    private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MMM dd - kk:mm:ss:SSS");
-    private final LinkedList<ArrayList<String>> mGestureLogs = new LinkedList<>();
-
-    public void prepareForNewGesture() {
-        mGestureLogs.add(new ArrayList<>());
-        while (mGestureLogs.size() > MAX_NUM_LOG_GESTURES) {
-            mGestureLogs.pop();
-        }
-        getCurrentLog().add("[" + mDateFormat.format(mCalendar.getTime()) + "]");
-    }
-
-    public void setInputConsumer(InputConsumer consumer) {
-        getCurrentLog().add("tc=" + consumer.getClass().getSimpleName());
-    }
-
-    public void addMotionEvent(MotionEvent event) {
-        getCurrentLog().add("ev=" + event.getActionMasked());
-    }
-
-    public void startQuickStep() {
-        getCurrentLog().add("qstStart");
-    }
-
-    public void startRecentsAnimation() {
-        getCurrentLog().add("raStart");
-    }
-
-    public void startRecentsAnimationCallback(int numTargets) {
-        getCurrentLog().add("raStartCb=" + numTargets);
-    }
-
-    public void cancelRecentsAnimation() {
-        getCurrentLog().add("raCancel");
-    }
-
-    public void finishRecentsAnimation(boolean toHome) {
-        getCurrentLog().add("raFinish=" + toHome);
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println("TouchInteractionLog {");
-        for (ArrayList<String> gesture : mGestureLogs) {
-            pw.print("    ");
-            for (String log : gesture) {
-                pw.print(log + " ");
-            }
-            pw.println();
-        }
-        pw.println("}");
-    }
-
-    private ArrayList<String> getCurrentLog() {
-        return mGestureLogs.getLast();
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index d1d0e86..6d608ee 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.EventLogArray;
 import com.android.launcher3.util.LooperExecutor;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.systemui.shared.recents.IOverviewProxy;
@@ -70,7 +71,8 @@
     public static final LooperExecutor BACKGROUND_EXECUTOR =
             new LooperExecutor(UiThreadHelper.getBackgroundLooper());
 
-    public static final TouchInteractionLog TOUCH_INTERACTION_LOG = new TouchInteractionLog();
+    public static final EventLogArray TOUCH_INTERACTION_LOG =
+            new EventLogArray("touch_interaction_log", 40);
 
     public static final int EDGE_NAV_BAR = 1 << 8;
 
@@ -87,6 +89,7 @@
                     .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
             runWhenUserUnlocked(() -> {
                 mRecentsModel.setSystemUiProxy(mISystemUiProxy);
+                mRecentsModel.onInitializeSystemUI(bundle);
                 mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
             });
 
@@ -119,6 +122,11 @@
             mOverviewCommandHelper.onTip(actionType, viewType);
         }
 
+        @Override
+        public void onAssistantAvailable(boolean available) {
+            mAssistantAvailable = available;
+        }
+
         /** Deprecated methods **/
         public void onQuickStep(MotionEvent motionEvent) { }
 
@@ -172,6 +180,7 @@
     private TaskOverlayFactory mTaskOverlayFactory;
     private InputConsumerController mInputConsumer;
     private SwipeSharedState mSwipeSharedState;
+    private boolean mAssistantAvailable;
 
     private boolean mIsUserUnlocked;
     private List<Runnable> mOnUserUnlockedCallbacks;
@@ -281,15 +290,13 @@
             return;
         }
         MotionEvent event = (MotionEvent) ev;
+        TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
         if (event.getAction() == ACTION_DOWN) {
-            TOUCH_INTERACTION_LOG.prepareForNewGesture();
             boolean useSharedState = mConsumer.isActive();
             mConsumer.onConsumerAboutToBeSwitched();
             mConsumer = newConsumer(useSharedState, event);
-            TOUCH_INTERACTION_LOG.setInputConsumer(mConsumer);
+            TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
         }
-        TOUCH_INTERACTION_LOG.addMotionEvent(event);
-
         mConsumer.onMotionEvent(event);
     }
 
@@ -308,7 +315,7 @@
 
         if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
             return InputConsumer.NO_OP;
-        } else if (mOverviewInteractionState.isSwipeUpGestureEnabled()
+        } else if (mAssistantAvailable && mOverviewInteractionState.isSwipeUpGestureEnabled()
                 && FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
                 && AssistantTouchConsumer.withinTouchRegion(this, event.getX())) {
             return new AssistantTouchConsumer(this, mRecentsModel.getSystemUiProxy());
@@ -342,6 +349,6 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        TOUCH_INTERACTION_LOG.dump(pw);
+        TOUCH_INTERACTION_LOG.dump("", pw);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index fa827e7..7dc58a5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -671,7 +671,7 @@
         initTransitionEndpoints(dp);
 
         mRecentsAnimationWrapper.setController(targetSet);
-        TOUCH_INTERACTION_LOG.startRecentsAnimationCallback(targetSet.apps.length);
+        TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
 
         mPassedOverviewThreshold = false;
@@ -682,7 +682,7 @@
         mRecentsAnimationWrapper.setController(null);
         mActivityInitListener.unregister();
         setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
-        TOUCH_INTERACTION_LOG.cancelRecentsAnimation();
+        TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
     }
 
     @UiThread
@@ -1025,7 +1025,7 @@
     @UiThread
     private void resumeLastTask() {
         mRecentsAnimationWrapper.finish(false /* toRecents */, null);
-        TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
     }
 
     @UiThread
@@ -1043,7 +1043,7 @@
                         mMainThreadHandler);
             });
         }
-        TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
         doLogGesture(NEW_TASK);
     }
 
@@ -1152,7 +1152,7 @@
                         () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
             }
         }
-        TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
     }
 
     private void finishCurrentTransitionToHome() {
@@ -1160,7 +1160,7 @@
             mRecentsAnimationWrapper.finish(true /* toRecents */,
                     () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
-        TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
+        TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
         doLogGesture(HOME);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index fb58c24..419a666 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -19,6 +19,7 @@
 import static android.widget.Toast.LENGTH_SHORT;
 
 import static com.android.launcher3.BaseActivity.fromContext;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -91,7 +92,6 @@
 
     public static final long SCALE_ICON_DURATION = 120;
     private static final long DIM_ANIM_DURATION = 700;
-    private static final long TASK_LAUNCH_ANIM_DURATION = 200;
 
     public static final Property<TaskView, Float> ZOOM_SCALE =
             new FloatProperty<TaskView>("zoomScale") {
@@ -237,10 +237,10 @@
 
     public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
         final PendingAnimation pendingAnimation =
-                getRecentsView().createTaskLauncherAnimation(this, TASK_LAUNCH_ANIM_DURATION);
-        pendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+                getRecentsView().createTaskLauncherAnimation(this, RECENTS_LAUNCH_DURATION);
+        pendingAnimation.anim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
         AnimatorPlaybackController currentAnimation = AnimatorPlaybackController
-                .wrap(pendingAnimation.anim, TASK_LAUNCH_ANIM_DURATION, null);
+                .wrap(pendingAnimation.anim, RECENTS_LAUNCH_DURATION, null);
         currentAnimation.setEndAction(() -> {
             pendingAnimation.finish(true, Touch.SWIPE);
             launchTask(false);
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index f7126d0..f5e5dd3 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -39,7 +39,6 @@
     <dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
     <dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
     <dimen name="motion_pause_detector_min_displacement">48dp</dimen>
-    <dimen name="motion_pause_detector_max_orthogonal_displacement">48dp</dimen>
 
     <!-- Launcher app transition -->
     <dimen name="content_trans_y">50dp</dimen>
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 411e593..a0ab301 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -175,7 +175,7 @@
         }
     }
 
-    public void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
+    private void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
         mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
         mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, swipeUpEnabled ? 1 : 0, 0).
                 sendToTarget();
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 81a22a1..56bc8570 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,12 +16,15 @@
 package com.android.quickstep;
 
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.RemoteException;
@@ -59,8 +62,8 @@
     private final TaskIconCache mIconCache;
     private final TaskThumbnailCache mThumbnailCache;
 
-    private float mWindowCornerRadius = -1;
-    private Boolean mSupportsRoundedCornersOnWindows;
+    private float mWindowCornerRadius = 0;
+    private boolean mSupportsRoundedCornersOnWindows;
 
     private RecentsModel(Context context) {
         mContext = context;
@@ -73,6 +76,12 @@
         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
     }
 
+    public void onInitializeSystemUI(Bundle params) {
+        mWindowCornerRadius = params.getFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, 0);
+        mSupportsRoundedCornersOnWindows =
+                params.getBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, false);
+    }
+
     public TaskIconCache getIconCache() {
         return mIconCache;
     }
@@ -182,42 +191,10 @@
     }
 
     public float getWindowCornerRadius() {
-        // The window corner radius is expressed in pixels and won't change if the
-        // display density changes. It's safe to cache the value.
-        if (mWindowCornerRadius == -1) {
-            if (mSystemUiProxy != null) {
-                try {
-                    mWindowCornerRadius = mSystemUiProxy.getWindowCornerRadius();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Connection to ISystemUIProxy was lost, ignoring window corner "
-                            + "radius");
-                    return 0;
-                }
-            } else {
-                Log.w(TAG, "ISystemUIProxy is null, ignoring window corner radius");
-                return 0;
-            }
-        }
         return mWindowCornerRadius;
     }
 
     public boolean supportsRoundedCornersOnWindows() {
-        if (mSupportsRoundedCornersOnWindows == null) {
-            if (mSystemUiProxy != null) {
-                try {
-                    mSupportsRoundedCornersOnWindows =
-                            mSystemUiProxy.supportsRoundedCornersOnWindows();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Connection to ISystemUIProxy was lost, ignoring window corner "
-                            + "radius");
-                    return false;
-                }
-            } else {
-                Log.w(TAG, "ISystemUIProxy is null, ignoring window corner radius");
-                return false;
-            }
-        }
-
         return mSupportsRoundedCornersOnWindows;
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 21d8144..8a117c8 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -40,7 +40,6 @@
     private final float mSpeedSomewhatFast;
     private final float mSpeedFast;
     private final float mMinDisplacementForPause;
-    private final float mMaxOrthogonalDisplacementForPause;
     private final Alarm mForcePauseTimeout;
 
     private Long mPreviousTime = null;
@@ -62,8 +61,6 @@
         mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
         mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
         mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
-        mMaxOrthogonalDisplacementForPause = res.getDimension(
-                R.dimen.motion_pause_detector_max_orthogonal_displacement);
         mForcePauseTimeout = new Alarm();
         mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
     }
@@ -77,7 +74,6 @@
         if (mOnMotionPauseListener != null) {
             mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
         }
-        mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
     }
 
     /**
@@ -94,6 +90,7 @@
         if (mFirstOrthogonalPosition == null) {
             mFirstOrthogonalPosition = orthogonalPosition;
         }
+        mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
         long time = SystemClock.uptimeMillis();
         if (mPreviousTime != null && mPreviousPosition != null) {
             long changeInTime = Math.max(1, time - mPreviousTime);
@@ -108,7 +105,6 @@
         }
         mPreviousTime = time;
         mPreviousPosition = position;
-        mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
     }
 
     private void checkMotionPaused(float velocity, float prevVelocity,
@@ -135,9 +131,11 @@
             }
         }
         boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause;
-        boolean passedMaxOrthogonalDisplacement =
-                totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause;
-        isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement;
+        boolean isDisplacementOrthogonal = totalDisplacement.orthogonal > totalDisplacement.primary;
+        if (!passedMinDisplacement || isDisplacementOrthogonal) {
+            mForcePauseTimeout.cancelAlarm();
+            isPaused = false;
+        }
         updatePaused(isPaused);
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
index 7274090..f89842a 100644
--- a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
+++ b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
@@ -19,6 +19,12 @@
 import static com.android.quickstep.QuickStepOnOffRule.Mode.BOTH;
 import static com.android.quickstep.QuickStepOnOffRule.Mode.OFF;
 import static com.android.quickstep.QuickStepOnOffRule.Mode.ON;
+import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
+
+import static org.junit.Assert.assertTrue;
+
+import android.provider.Settings;
+import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -41,6 +47,8 @@
  */
 public class QuickStepOnOffRule implements TestRule {
 
+    static final String TAG = "QuickStepOnOffRule";
+
     public enum Mode {
         ON, OFF, BOTH
     }
@@ -68,39 +76,53 @@
             return new Statement() {
                 @Override
                 public void evaluate() throws Throwable {
-                    try {
-                        if (mode == ON || mode == BOTH) {
-                            evaluateWithQuickstepOn();
+                    if (SwipeUpSetting.isSwipeUpSettingAvailable()) {
+                        try {
+                            if (mode == ON || mode == BOTH) {
+                                evaluateWithQuickstepOn();
+                            }
+                            if (mode == OFF || mode == BOTH) {
+                                evaluateWithQuickstepOff();
+                            }
+                        } finally {
+                            setSwipeUpSetting(null);
                         }
-                        if (mode == OFF || mode == BOTH) {
-                            evaluateWithQuickstepOff();
+                    } else {
+                        // Execute without changing the setting, if the requested mode is
+                        // compatible.
+                        final boolean swipeUpEnabledDefaultValue =
+                                SwipeUpSetting.isSwipeUpEnabledDefaultValue();
+                        if (mode == BOTH ||
+                                mode == ON && swipeUpEnabledDefaultValue ||
+                                mode == OFF && !swipeUpEnabledDefaultValue) {
+                            evaluateWithoutChangingSetting(base);
                         }
-                    } finally {
-                        overrideSwipeUpEnabled(null);
                     }
                 }
 
-                private void evaluateWithQuickstepOff() throws Throwable {
-                    overrideSwipeUpEnabled(false);
+                public void setSwipeUpSetting(String value) {
+                    Log.d(TAG, "setSwipeUpSetting: " + value);
+                    assertTrue("Couldn't change Quickstep mode",
+                            Settings.Secure.putString(
+                                    InstrumentationRegistry.getInstrumentation().getTargetContext().
+                                            getContentResolver(),
+                                    SWIPE_UP_SETTING_NAME,
+                                    value));
+                }
+
+                public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
                     base.evaluate();
                 }
 
+                private void evaluateWithQuickstepOff() throws Throwable {
+                    setSwipeUpSetting("0");
+                    evaluateWithoutChangingSetting(base);
+                }
+
                 private void evaluateWithQuickstepOn() throws Throwable {
-                    overrideSwipeUpEnabled(true);
+                    setSwipeUpSetting("1");
                     base.evaluate();
                 }
-
-                private void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride)
-                        throws Throwable {
-                    mLauncher.overrideSwipeUpEnabled(swipeUpEnabledOverride);
-                    mMainThreadExecutor.execute(() -> OverviewInteractionState.INSTANCE.get(
-                            InstrumentationRegistry.getInstrumentation().getTargetContext()).
-                            notifySwipeUpSettingChanged(mLauncher.isSwipeUpEnabled()));
-                    // TODO(b/124236673): avoid using sleep().
-                    mLauncher.getDevice().waitForIdle();
-                    Thread.sleep(2000);
-                    mLauncher.getDevice().waitForIdle();
-                }
             };
         } else {
             return base;
diff --git a/res/drawable/horizontal_ellipsis.xml b/res/drawable/horizontal_ellipsis.xml
index ad08529..65e958d 100644
--- a/res/drawable/horizontal_ellipsis.xml
+++ b/res/drawable/horizontal_ellipsis.xml
@@ -22,6 +22,6 @@
         android:tint="?android:attr/textColorSecondary" >
 
     <path
-        android:pathData="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
+        android:pathData="M6 10c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm12 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm-6 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"
         android:fillColor="@android:color/white" />
 </vector>
\ No newline at end of file
diff --git a/res/layout/snackbar.xml b/res/layout/snackbar.xml
index bca3308..b818943 100644
--- a/res/layout/snackbar.xml
+++ b/res/layout/snackbar.xml
@@ -29,7 +29,7 @@
         android:ellipsize="end"
         android:textSize="@dimen/snackbar_max_text_size"
         android:textColor="?android:attr/textColorPrimary"
-        android:theme="@style/TextTitle"/>
+        style="@style/TextTitle"/>
     <TextView
         android:id="@+id/action"
         android:layout_height="@dimen/snackbar_content_height"
@@ -42,6 +42,6 @@
         android:textStyle="bold"
         android:textSize="@dimen/snackbar_max_text_size"
         android:textColor="?android:attr/colorAccent"
-        android:theme="@style/TextTitle"
+        style="@style/TextTitle"
         android:capitalize="sentences"/>
 </merge>
\ No newline at end of file
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index e5b1448..62bc53a 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -24,7 +24,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -33,9 +32,11 @@
 import android.os.Process;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Patterns;
+import android.util.Xml;
 
 import com.android.launcher3.LauncherProvider.SqlArguments;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -149,9 +150,6 @@
     private static final String HOTSEAT_CONTAINER_NAME =
             Favorites.containerToString(Favorites.CONTAINER_HOTSEAT);
 
-    private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
-            "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
-
     @Thunk final Context mContext;
     @Thunk final AppWidgetHost mAppWidgetHost;
     protected final LayoutParserCallback mCallback;
@@ -207,7 +205,7 @@
      */
     protected int parseLayout(int layoutId, IntArray screenIds)
             throws XmlPullParserException, IOException {
-        XmlResourceParser parser = mSourceRes.getXml(layoutId);
+        XmlPullParser parser = mSourceRes.getXml(layoutId);
         beginDocument(parser, mRootTag);
         final int depth = parser.getDepth();
         int type;
@@ -228,7 +226,7 @@
      * Parses container and screenId attribute from the current tag, and puts it in the out.
      * @param out array of size 2.
      */
-    protected void parseContainerAndScreen(XmlResourceParser parser, int[] out) {
+    protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
         if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
             out[0] = Favorites.CONTAINER_HOTSEAT;
             // Hack: hotseat items are stored using screen ids
@@ -243,7 +241,7 @@
      * Parses the current node and returns the number of elements added.
      */
     protected int parseAndAddNode(
-            XmlResourceParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
+            XmlPullParser parser, ArrayMap<String, TagParser> tagParserMap, IntArray screenIds)
         throws XmlPullParserException, IOException {
 
         if (TAG_INCLUDE.equals(parser.getName())) {
@@ -324,7 +322,7 @@
          * Parses the tag and adds to the db
          * @return the id of the row added or -1;
          */
-        int parseAndAdd(XmlResourceParser parser)
+        int parseAndAdd(XmlPullParser parser)
                 throws XmlPullParserException, IOException;
     }
 
@@ -334,7 +332,7 @@
     protected class AppShortcutParser implements TagParser {
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser) {
+        public int parseAndAdd(XmlPullParser parser) {
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
 
@@ -371,7 +369,7 @@
         /**
          * Helper method to allow extending the parser capabilities
          */
-        protected int invalidPackageOrClass(XmlResourceParser parser) {
+        protected int invalidPackageOrClass(XmlPullParser parser) {
             Log.w(TAG, "Skipping invalid <favorite> with no component");
             return -1;
         }
@@ -383,7 +381,7 @@
     protected class AutoInstallParser implements TagParser {
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser) {
+        public int parseAndAdd(XmlPullParser parser) {
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
             if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
@@ -414,7 +412,7 @@
         }
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser) {
+        public int parseAndAdd(XmlPullParser parser) {
             final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
             final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0);
 
@@ -449,7 +447,7 @@
                     intent, Favorites.ITEM_TYPE_SHORTCUT);
         }
 
-        protected Intent parseIntent(XmlResourceParser parser) {
+        protected Intent parseIntent(XmlPullParser parser) {
             final String url = getAttributeValue(parser, ATTR_URL);
             if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
                 if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
@@ -470,7 +468,7 @@
     protected class PendingWidgetParser implements TagParser {
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser)
+        public int parseAndAdd(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
@@ -541,7 +539,7 @@
         }
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser)
+        public int parseAndAdd(XmlPullParser parser)
                 throws XmlPullParserException, IOException {
             final String title;
             final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
@@ -649,7 +647,7 @@
      * Return attribute value, attempting launcher-specific namespace first
      * before falling back to anonymous attribute.
      */
-    protected static String getAttributeValue(XmlResourceParser parser, String attribute) {
+    protected static String getAttributeValue(XmlPullParser parser, String attribute) {
         String value = parser.getAttributeValue(
                 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
         if (value == null) {
@@ -662,13 +660,14 @@
      * Return attribute resource value, attempting launcher-specific namespace
      * first before falling back to anonymous attribute.
      */
-    protected static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+    protected static int getAttributeResourceValue(XmlPullParser parser, String attribute,
             int defaultValue) {
-        int value = parser.getAttributeResourceValue(
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+        int value = attrs.getAttributeResourceValue(
                 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
                 defaultValue);
         if (value == defaultValue) {
-            value = parser.getAttributeResourceValue(null, attribute, defaultValue);
+            value = attrs.getAttributeResourceValue(null, attribute, defaultValue);
         }
         return value;
     }
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 44830e8..75297f6 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -10,7 +10,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -76,7 +75,7 @@
     }
 
     @Override
-    protected void parseContainerAndScreen(XmlResourceParser parser, int[] out) {
+    protected void parseContainerAndScreen(XmlPullParser parser, int[] out) {
         out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
         String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
         if (strContainer != null) {
@@ -91,7 +90,7 @@
     public class AppShortcutWithUriParser extends AppShortcutParser {
 
         @Override
-        protected int invalidPackageOrClass(XmlResourceParser parser) {
+        protected int invalidPackageOrClass(XmlPullParser parser) {
             final String uri = getAttributeValue(parser, ATTR_URI);
             if (TextUtils.isEmpty(uri)) {
                 Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
@@ -185,7 +184,7 @@
         }
 
         @Override
-        protected Intent parseIntent(XmlResourceParser parser) {
+        protected Intent parseIntent(XmlPullParser parser) {
             String uri = null;
             try {
                 uri = getAttributeValue(parser, ATTR_URI);
@@ -205,7 +204,7 @@
         private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+        public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             final int groupDepth = parser.getDepth();
             int type;
@@ -233,7 +232,7 @@
     @Thunk class PartnerFolderParser implements TagParser {
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+        public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             // Folder contents come from an external XML resource
             final Partner partner = Partner.get(mPackageManager);
@@ -242,7 +241,7 @@
                 final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
                         "xml", partner.getPackageName());
                 if (resId != 0) {
-                    final XmlResourceParser partnerParser = partnerRes.getXml(resId);
+                    final XmlPullParser partnerParser = partnerRes.getXml(resId);
                     beginDocument(partnerParser, TAG_FOLDER);
 
                     FolderParser folderParser = new FolderParser(getFolderElementsMap(partnerRes));
@@ -259,7 +258,7 @@
     @Thunk class MyFolderParser extends FolderParser {
 
         @Override
-        public int parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
+        public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException,
                 IOException {
             final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
             if (resId != 0) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d65fe76..61cd2f1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -257,7 +257,7 @@
 
     private RotationHelper mRotationHelper;
 
-    private final Handler mHandler = new Handler();
+    final Handler mHandler = new Handler();
     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
 
     @Override
@@ -488,11 +488,15 @@
 
     @Override
     public void invalidateParent(ItemInfo info) {
-        FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(getDeviceProfile().inv);
-        if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
+        if (info.container >= 0) {
             View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
-            if (folderIcon != null) {
-                folderIcon.invalidate();
+            if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) {
+                FolderIconPreviewVerifier verifier =
+                        new FolderIconPreviewVerifier(getDeviceProfile().inv);
+                verifier.setFolderInfo((FolderInfo) folderIcon.getTag());
+                if (verifier.isItemInPreview(info.rank)) {
+                    folderIcon.invalidate();
+                }
             }
         }
     }
@@ -1748,6 +1752,8 @@
         setWorkspaceLoading(true);
 
         // Clear the workspace because it's going to be rebound
+        mDragController.cancelDrag();
+
         mWorkspace.clearDropTargets();
         mWorkspace.removeAllWorkspaceScreens();
         mAppWidgetHost.clearViews();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5ab6eea..b1664f1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.AddWorkspaceItemsTask;
@@ -210,20 +211,20 @@
             final int itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
         ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
         if (modelItem != null && item != modelItem) {
-            // check all the data is consistent
-            if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
-                ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
-                ShortcutInfo shortcut = (ShortcutInfo) item;
-                if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
-                        modelShortcut.intent.filterEquals(shortcut.intent) &&
-                        modelShortcut.id == shortcut.id &&
-                        modelShortcut.itemType == shortcut.itemType &&
-                        modelShortcut.container == shortcut.container &&
-                        modelShortcut.screenId == shortcut.screenId &&
-                        modelShortcut.cellX == shortcut.cellX &&
-                        modelShortcut.cellY == shortcut.cellY &&
-                        modelShortcut.spanX == shortcut.spanX &&
-                        modelShortcut.spanY == shortcut.spanY) {
+            // If it is a release build on a release device, check all the data is consistent as
+            // we don't want to crash non-dev users.
+            if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
+                    modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
+                if (modelItem.title.toString().equals(item.title.toString()) &&
+                        modelItem.getIntent().filterEquals(item.getIntent()) &&
+                        modelItem.id == item.id &&
+                        modelItem.itemType == item.itemType &&
+                        modelItem.container == item.container &&
+                        modelItem.screenId == item.screenId &&
+                        modelItem.cellX == item.cellX &&
+                        modelItem.cellY == item.cellY &&
+                        modelItem.spanX == item.spanX &&
+                        modelItem.spanY == item.spanY) {
                     // For all intents and purposes, this is the same object
                     return;
                 }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index fb33694..ce73229 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
 
 import android.annotation.TargetApi;
+import android.app.backup.BackupManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
@@ -150,7 +151,7 @@
             mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
 
             if (RestoreDbTask.isPending(getContext())) {
-                if (!RestoreDbTask.performRestore(mOpenHelper)) {
+                if (!RestoreDbTask.performRestore(mOpenHelper, new BackupManager(getContext()))) {
                     mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
                 }
                 // Set is pending to false irrespective of the result, so that it doesn't get
@@ -542,6 +543,7 @@
      * The class is subclassed in tests to create an in-memory db.
      */
     public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
+        private final BackupManager mBackupManager;
         private final Handler mWidgetHostResetHandler;
         private final Context mContext;
         private int mMaxItemId = -1;
@@ -571,6 +573,7 @@
             super(context, tableName, SCHEMA_VERSION);
             mContext = context;
             mWidgetHostResetHandler = widgetHostResetHandler;
+            mBackupManager = new BackupManager(mContext);
         }
 
         protected void initIds() {
@@ -620,9 +623,12 @@
             Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
         }
 
+        public long getSerialNumberForUser(UserHandle user) {
+            return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(user);
+        }
+
         public long getDefaultUserSerial() {
-            return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
-                    Process.myUserHandle());
+            return getSerialNumberForUser(Process.myUserHandle());
         }
 
         private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index b49578b..cee1c26 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -187,11 +187,9 @@
         return new float[] {1, 0, 0};
     }
 
-    /**
-     * @return Whether we should scale the hotseat as if it were part of the workspace.
-     */
-    public boolean scaleHotseatWithWorkspace() {
-        return true;
+    public float[] getHotseatScaleAndTranslation(Launcher launcher) {
+        // For most states, treat the hotseat as if it were part of the workspace.
+        return getWorkspaceScaleAndTranslation(launcher);
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 119058d..f6b54f2 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -248,6 +248,22 @@
             return;
         }
 
+        if (delay > 0) {
+            // Create the animation after the delay as some properties can change between preparing
+            // the animation and running the animation.
+            int startChangeId = mConfig.mChangeId;
+            mUiHandler.postDelayed(() -> {
+                if (mConfig.mChangeId == startChangeId) {
+                    goToStateAnimated(state, fromState, onCompleteRunnable);
+                }
+            }, delay);
+        } else {
+            goToStateAnimated(state, fromState, onCompleteRunnable);
+        }
+    }
+
+    private void goToStateAnimated(LauncherState state, LauncherState fromState,
+            Runnable onCompleteRunnable) {
         // Since state NORMAL can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
         mConfig.duration = state == NORMAL ? fromState.transitionDuration : state.transitionDuration;
@@ -256,12 +272,7 @@
         prepareForAtomicAnimation(fromState, state, builder);
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(
                 state, builder, onCompleteRunnable);
-        Runnable runnable = new StartAnimRunnable(animation);
-        if (delay > 0) {
-            mUiHandler.postDelayed(runnable, delay);
-        } else {
-            mUiHandler.post(runnable);
-        }
+        mUiHandler.post(new StartAnimRunnable(animation));
     }
 
     /**
@@ -533,6 +544,8 @@
 
         private AnimatorSet mCurrentAnimation;
         private LauncherState mTargetState;
+        // Id to keep track of config changes, to tie an animation with the corresponding request
+        private int mChangeId = 0;
 
         /**
          * Cancels the current animation and resets config variables.
@@ -554,6 +567,7 @@
 
             mCurrentAnimation = null;
             playbackController = null;
+            mChangeId ++;
         }
 
         public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 00da6fa..7fa1aa0 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1027,7 +1027,8 @@
         if (overScrollAmount < 0) {
             super.scrollTo(overScrollAmount, getScrollY());
         } else {
-            super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
+            int x = Math.max(0, Math.min(getScrollX(), mMaxScrollX));
+            super.scrollTo(x + overScrollAmount, getScrollY());
         }
         invalidate();
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c3edfe5..e699500 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -593,8 +593,9 @@
         int viewLocationTop = rect.top;
 
         if (isBubbleTextView && !fromDeepShortcutView) {
-            BubbleTextView btv = (BubbleTextView) v;
-            btv.getIconBounds(rect);
+            ((BubbleTextView) v).getIconBounds(rect);
+        } else if (isFolderIcon) {
+            ((FolderIcon) v).getPreviewBounds(rect);
         } else {
             rect.set(0, 0, rect.width(), rect.height());
         }
@@ -616,14 +617,14 @@
      *               eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
      */
     public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
-            Object[] outObj) {
+            boolean flattenDrawable, Object[] outObj) {
         LauncherAppState appState = LauncherAppState.getInstance(launcher);
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
             LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(launcher)
                     .resolveActivity(info.getIntent(), info.user);
             outObj[0] = activityInfo;
             return (activityInfo != null) ? appState.getIconCache()
-                    .getFullResIcon(activityInfo, false) : null;
+                    .getFullResIcon(activityInfo, flattenDrawable) : null;
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             if (info instanceof PendingAddShortcutInfo) {
                 ShortcutConfigActivityInfo activityInfo =
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7f5ca42..09eb6d9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -19,9 +19,13 @@
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
+import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
 
 import android.animation.Animator;
@@ -34,7 +38,6 @@
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -44,8 +47,10 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -98,8 +103,6 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
 import java.util.function.Predicate;
 
 /**
@@ -521,6 +524,9 @@
         mScreenOrder.clear();
         mWorkspaceScreens.clear();
 
+        // Remove any deferred refresh callbacks
+        mLauncher.mHandler.removeCallbacksAndMessages(DeferredWidgetRefresh.class);
+
         // Ensure that the first page is always present
         bindAndInitFirstWorkspaceScreen(qsb);
 
@@ -1608,7 +1614,7 @@
 
         boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
         boolean willBecomeShortcut =
-                (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+                (info.itemType == ITEM_TYPE_APPLICATION ||
                         info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
                         info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
 
@@ -2516,7 +2522,7 @@
             View view;
 
             switch (info.itemType) {
-            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+            case ITEM_TYPE_APPLICATION:
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                 if (info.container == NO_ID && info instanceof AppInfo) {
@@ -2892,51 +2898,29 @@
     }
 
     /**
-     * Returns a list of all the CellLayouts on the Homescreen, starting with
-     * {@param startPage}, then going outward alternating between pages prior to the startPage,
-     * and then the pages after the startPage.
-     * ie. if there are 5 pages [0, 1, 2, 3, 4] and startPage is 1, we return [1, 0, 2, 3, 4].
-     */
-    private CellLayout[] getWorkspaceCellLayouts(int startPage) {
-        int screenCount = getChildCount();
-        final CellLayout[] layouts = new CellLayout[screenCount];
-        int screen = 0;
-
-        layouts[screen] = (CellLayout) getChildAt(startPage);
-        screen++;
-
-        for (int i = 1; screen < screenCount; ++i) {
-            CellLayout prevPage = (CellLayout) getChildAt(startPage - i);
-            CellLayout nextPage = (CellLayout) getChildAt(startPage + i);
-
-            if (prevPage != null) {
-                layouts[screen] = prevPage;
-                screen++;
-            }
-            if (nextPage != null) {
-                layouts[screen] = nextPage;
-                screen++;
-            }
-        }
-        return layouts;
-    }
-
-    /**
      * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
      * animation.
      *
-     * @param component The component of the task being dismissed.
+     * @param packageName The package name of the app to match.
+     * @param user The user of the app to match.
      */
-    public View getFirstMatchForAppClose(ComponentName component) {
+    public View getFirstMatchForAppClose(String packageName, UserHandle user) {
         final int curPage = getCurrentPage();
         final CellLayout currentPage = (CellLayout) getPageAt(curPage);
-        final Workspace.ItemOperator isItemComponent = (info, view) ->
-                info != null && Objects.equals(info.getTargetComponent(), component);
-        final Workspace.ItemOperator isItemInFolder = (info, view) -> {
+        final Workspace.ItemOperator packageAndUser = (ItemInfo info, View view) -> info != null
+                && info.getTargetComponent() != null
+                && TextUtils.equals(info.getTargetComponent().getPackageName(), packageName)
+                && info.user.equals(user);
+        final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
+                packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
+        final Workspace.ItemOperator packageAndUserAndShortcut = (ItemInfo info, View view) ->
+                packageAndUser.evaluate(info, view) && (info.itemType == ITEM_TYPE_SHORTCUT
+                        || info.itemType == ITEM_TYPE_DEEP_SHORTCUT);
+        final Workspace.ItemOperator packageAndUserInFolder = (info, view) -> {
             if (info instanceof FolderInfo) {
                 FolderInfo folderInfo = (FolderInfo) info;
                 for (ShortcutInfo shortcutInfo : folderInfo.contents) {
-                    if (Objects.equals(shortcutInfo.getTargetComponent(), component)) {
+                    if (packageAndUser.evaluate(shortcutInfo, view)) {
                         return true;
                     }
                 }
@@ -2944,33 +2928,16 @@
             return false;
         };
 
-        CellLayout[] hotseatAndCurrentPage = new CellLayout[] { getHotseat(), currentPage };
-        // First we look if the app itself is in the hotseat or on the current workspace page.
-        View icon = getFirstMatch(hotseatAndCurrentPage, isItemComponent);
-        if (icon != null) {
-            return icon;
+        // Order: App icons, shortcuts, app/shortcut in folder. Items in hotseat get returned first.
+        if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
+            return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
+                    packageAndUserAndApp, packageAndUserAndShortcut, packageAndUserInFolder);
+        } else {
+            // Do not use Folder as a criteria, since it'll cause a crash when trying to draw
+            // FolderAdaptiveIcon as the background.
+            return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
+                    packageAndUserAndApp, packageAndUserAndShortcut);
         }
-        // Then we look if the app is in a folder on the hotseat or current workspace page.
-        icon = getFirstMatch(hotseatAndCurrentPage, isItemInFolder);
-        if (icon != null) {
-            return icon;
-        }
-        // Continue searching for the app or for a folder with the app on other pages of the
-        // workspace. We skip the current page, since we already searched above.
-        CellLayout[] allPages = getWorkspaceCellLayouts(curPage);
-        CellLayout[] page = new CellLayout[1];
-        for (int i = 1; i < allPages.length; ++i) {
-            page[0] = allPages[i];
-            icon = getFirstMatch(page, isItemComponent);
-            if (icon != null) {
-                return icon;
-            }
-            icon = getFirstMatch(page, isItemInFolder);
-            if (icon != null) {
-                return icon;
-            }
-        }
-        return null;
     }
 
     public View getHomescreenIconByItemId(final int id) {
@@ -2998,21 +2965,38 @@
         return value[0];
     }
 
-    private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator operator) {
-        final View[] value = new View[1];
+    /**
+     * @param cellLayouts List of CellLayouts to scan, in order of preference.
+     * @param operators List of operators, in order starting from best matching operator.
+     * @return
+     */
+    private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
+        // This array is filled with the first match for each operator.
+        final View[] matches = new View[operators.length];
+        // For efficiency, the outer loop should be CellLayout.
         for (CellLayout cellLayout : cellLayouts) {
             mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> {
-                if (operator.evaluate(info, v)) {
-                    value[0] = v;
-                    return true;
+                for (int i = 0; i < operators.length; ++i) {
+                    if (matches[i] == null && operators[i].evaluate(info, v)) {
+                        matches[i] = v;
+                        if (i == 0) {
+                            // We can return since this is the best match possible.
+                            return true;
+                        }
+                    }
                 }
                 return false;
             });
-            if (value[0] != null) {
+            if (matches[0] != null) {
                 break;
             }
         }
-        return value[0];
+        for (View match : matches) {
+            if (match != null) {
+                return match;
+            }
+        }
+        return null;
     }
 
     void clearDropTargets() {
@@ -3348,13 +3332,15 @@
             LauncherAppWidgetHost host) {
             mInfos = infos;
             mHost = host;
-            mHandler = new Handler();
+            mHandler = mLauncher.mHandler;
             mRefreshPending = true;
 
             mHost.addProviderChangeListener(this);
             // Force refresh after 10 seconds, if we don't get the provider changed event.
             // This could happen when the provider is no longer available in the app.
-            mHandler.postDelayed(this, 10000);
+            Message msg = Message.obtain(mHandler, this);
+            msg.obj = DeferredWidgetRefresh.class;
+            mHandler.sendMessageDelayed(msg, 10000);
         }
 
         @Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 1c595ab..0507470 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -72,6 +72,7 @@
     private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
             AnimatorSetBuilder builder, AnimationConfig config) {
         float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
+        float[] hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(mLauncher);
         mNewScale = scaleAndTranslation[0];
         PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
         final int childCount = mWorkspace.getChildCount();
@@ -89,16 +90,16 @@
             Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
             propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
 
-            if (state.scaleHotseatWithWorkspace()) {
-                DragLayer dragLayer = mLauncher.getDragLayer();
-                int[] workspacePivot = new int[]{(int) mWorkspace.getPivotX(),
-                        (int) mWorkspace.getPivotY()};
-                dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
-                dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
-                hotseat.setPivotX(workspacePivot[0]);
-                hotseat.setPivotY(workspacePivot[1]);
-                propertySetter.setFloat(hotseat, SCALE_PROPERTY, mNewScale, scaleInterpolator);
-            }
+            // Set the hotseat's pivot point to match the workspace's, so that it scales together.
+            DragLayer dragLayer = mLauncher.getDragLayer();
+            int[] workspacePivot = new int[]{(int) mWorkspace.getPivotX(),
+                    (int) mWorkspace.getPivotY()};
+            dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
+            dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
+            hotseat.setPivotX(workspacePivot[0]);
+            hotseat.setPivotY(workspacePivot[1]);
+            float hotseatScale = hotseatScaleAndTranslation[0];
+            propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator);
 
             float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
             propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
@@ -116,12 +117,11 @@
                 scaleAndTranslation[1], translationInterpolator);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
                 scaleAndTranslation[2], translationInterpolator);
-        if (state.scaleHotseatWithWorkspace()) {
-            propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
-                    scaleAndTranslation[2], translationInterpolator);
-            propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
-                    scaleAndTranslation[2], translationInterpolator);
-        }
+
+        propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
+                hotseatScaleAndTranslation[2], translationInterpolator);
+        propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
+                hotseatScaleAndTranslation[2], translationInterpolator);
 
         // Set scrim
         WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 8c57f46..cb239b3 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -21,6 +21,9 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
 import android.provider.Settings;
 
 import androidx.annotation.GuardedBy;
@@ -102,7 +105,7 @@
             false, "Enable springs for quickstep animations");
 
     public static final TogglableFlag ADAPTIVE_ICON_WINDOW_ANIM = new TogglableFlag(
-            "ADAPTIVE_ICON_WINDOW_ANIM", false,
+            "ADAPTIVE_ICON_WINDOW_ANIM", true,
             "Use adaptive icons for window animations.");
 
     public static final TogglableFlag ENABLE_QUICKSTEP_LIVE_TILE = new TogglableFlag(
@@ -116,7 +119,7 @@
             "ENABLE_HINTS_IN_OVERVIEW", false,
             "Show chip hints and gleams on the overview screen");
 
-    public static final TogglableFlag ENABLE_ASSISTANT_GESTURE = new TogglableFlag(
+    public static final TogglableFlag ENABLE_ASSISTANT_GESTURE = new ToggleableGlobalSettingsFlag(
             "ENABLE_ASSISTANT_GESTURE", false,
             "Enable swipe up from the bottom right corner to start assistant");
 
@@ -256,6 +259,16 @@
         @Override
         public void initialize(Context context) {
             contentResolver = context.getContentResolver();
+            contentResolver.registerContentObserver(Settings.Global.getUriFor(getKey()), true,
+                    new ContentObserver(new Handler(Looper.getMainLooper())) {
+                        @Override
+                        public void onChange(boolean selfChange) {
+                            superInitialize(context);
+                    }});
+            superInitialize(context);
+        }
+
+        private void superInitialize(Context context) {
             super.initialize(context);
         }
 
@@ -274,10 +287,5 @@
             }
             return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
         }
-
-        @Override
-        public boolean get() {
-            return getFromStorage(null, getDefaultValue());
-        }
     }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 8a27f9d..bdbea29 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -199,7 +199,8 @@
                 Object[] outObj = new Object[1];
                 int w = mBitmap.getWidth();
                 int h = mBitmap.getHeight();
-                Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h, outObj);
+                Drawable dr = Utilities.getFullDrawable(mLauncher, info, w, h,
+                        false /* flattenDrawable */, outObj);
 
                 if (dr instanceof AdaptiveIconDrawable) {
                     int blurMargin = (int) mLauncher.getResources()
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 794ab4e..5e41bb7 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.graphics.ShiftedBitmapDrawable;
 import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.util.Preconditions;
 
@@ -133,39 +134,4 @@
 
         return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
     }
-
-    /**
-     * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
-     */
-    private static class ShiftedBitmapDrawable extends Drawable {
-
-        private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
-        private final Bitmap mBitmap;
-        private final float mShiftX;
-        private final float mShiftY;
-
-        ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
-            mBitmap = bitmap;
-            mShiftX = shiftX;
-            mShiftY = shiftY;
-        }
-
-        @Override
-        public void draw(Canvas canvas) {
-            canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
-        }
-
-        @Override
-        public void setAlpha(int i) { }
-
-        @Override
-        public void setColorFilter(ColorFilter colorFilter) {
-            mPaint.setColorFilter(colorFilter);
-        }
-
-        @Override
-        public int getOpacity() {
-            return PixelFormat.TRANSLUCENT;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 30dbe69..71c9148 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -189,6 +189,10 @@
         return icon;
     }
 
+    public void getPreviewBounds(Rect outBounds) {
+        mBackground.getBounds(outBounds);
+    }
+
     public Folder getFolder() {
         return mFolder;
     }
@@ -196,6 +200,7 @@
     private void setFolder(Folder folder) {
         mFolder = folder;
         mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+        mPreviewVerifier.setFolderInfo(mFolder.getInfo());
         updatePreviewItems(false);
     }
 
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
index 5a27cd4..4c84e35 100644
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.folder;
 
+import android.util.Log;
+
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InvariantDeviceProfile;
 
@@ -26,14 +28,20 @@
  */
 public class FolderIconPreviewVerifier {
 
+    private static final String TAG = "FolderPreviewVerifier";
+
     private final int mMaxGridCountX;
     private final int mMaxGridCountY;
     private final int mMaxItemsPerPage;
-    private final int[] mGridSize = new int[2];
+    private final int[] mGridSize = new int[] { 1, 1 };
 
+    private int mNumItemsInFolder;
     private int mGridCountX;
     private boolean mDisplayingUpperLeftQuadrant = false;
 
+    /**
+     * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
+     */
     public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
         mMaxGridCountX = profile.numFolderColumns;
         mMaxGridCountY = profile.numFolderRows;
@@ -42,11 +50,14 @@
 
     public void setFolderInfo(FolderInfo info) {
         int numItemsInFolder = info.contents.size();
-        FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
-                mMaxGridCountY, mMaxItemsPerPage, mGridSize);
-        mGridCountX = mGridSize[0];
+        if (numItemsInFolder != mNumItemsInFolder) {
+            FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
+                    mMaxGridCountY, mMaxItemsPerPage, mGridSize);
+            mGridCountX = mGridSize[0];
 
-        mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
+            mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
+            mNumItemsInFolder = numItemsInFolder;
+        }
     }
 
     /**
@@ -62,6 +73,10 @@
      * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
      */
     public boolean isItemInPreview(int page, int rank) {
+        if (mGridSize[0] == 1) {
+            Log.w(TAG, "setFolderInfo not called before checking if item is in preview.");
+        }
+
         // First page items are laid out such that the first 4 items are always in the upper
         // left quadrant. For all other pages, we need to check the row and col.
         if (page > 0 || mDisplayingUpperLeftQuadrant) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 06eaf38..d4ce6dd 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -321,6 +321,7 @@
 
         FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
                 Launcher.getLauncher(getContext()).getDeviceProfile().inv);
+        verifier.setFolderInfo(mFolder.getInfo());
         rank = 0;
         for (int i = 0; i < itemCount; i++) {
             View v = list.size() > i ? list.get(i) : null;
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index fd4774f..ac908f4 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -33,6 +33,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.RadialGradient;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Shader;
 import android.util.Property;
@@ -154,6 +155,14 @@
         invalidate();
     }
 
+    void getBounds(Rect outBounds) {
+        int top = basePreviewOffsetY;
+        int left = basePreviewOffsetX;
+        int right = left + previewSize;
+        int bottom = top + previewSize;
+        outBounds.set(left, top, right, bottom);
+    }
+
     int getRadius() {
         return previewSize / 2;
     }
diff --git a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
new file mode 100644
index 0000000..52d45bb
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A simple drawable which draws a bitmap at a fixed position irrespective of the bounds
+ */
+public class ShiftedBitmapDrawable extends Drawable {
+
+    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+    private final Bitmap mBitmap;
+    private float mShiftX;
+    private float mShiftY;
+
+    public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
+        mBitmap = bitmap;
+        mShiftX = shiftX;
+        mShiftY = shiftY;
+    }
+
+    public float getShiftX() {
+        return mShiftX;
+    }
+
+    public float getShiftY() {
+        return mShiftY;
+    }
+
+    public void setShiftX(float shiftX) {
+        mShiftX = shiftX;
+    }
+
+    public void setShiftY(float shiftY) {
+        mShiftY = shiftY;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        canvas.drawBitmap(mBitmap, mShiftX, mShiftY, mPaint);
+    }
+
+    @Override
+    public void setAlpha(int i) { }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mPaint.setColorFilter(colorFilter);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/logging/EventLogArray.java b/src/com/android/launcher3/logging/EventLogArray.java
new file mode 100644
index 0000000..bfb3792
--- /dev/null
+++ b/src/com/android/launcher3/logging/EventLogArray.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.logging;
+
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * A utility class to record and log events. Events are stored in a fixed size array and old logs
+ * are purged as new events come.
+ */
+public class EventLogArray {
+
+    private static final int TYPE_ONE_OFF = 0;
+    private static final int TYPE_FLOAT = 1;
+    private static final int TYPE_INTEGER = 2;
+    private static final int TYPE_BOOL_TRUE = 3;
+    private static final int TYPE_BOOL_FALSE = 4;
+
+    private final String name;
+    private final EventEntry[] logs;
+    private int nextIndex;
+
+    public EventLogArray(String name, int size) {
+        this.name = name;
+        logs = new EventEntry[size];
+        nextIndex = 0;
+    }
+
+    public void addLog(String event) {
+        addLog(TYPE_ONE_OFF, event, 0);
+    }
+
+    public void addLog(String event, int extras) {
+        addLog(TYPE_INTEGER, event, extras);
+    }
+
+    public void addLog(String event, float extras) {
+        addLog(TYPE_FLOAT, event, extras);
+    }
+
+    public void addLog(String event, boolean extras) {
+        addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0);
+    }
+
+    private void addLog(int type, String event, float extras) {
+        // Merge the logs if its a duplicate
+        int last = (nextIndex + logs.length - 1) % logs.length;
+        int secondLast = (nextIndex + logs.length - 2) % logs.length;
+        if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
+            logs[last].update(type, event, extras);
+            logs[secondLast].duplicateCount++;
+            return;
+        }
+
+        if (logs[nextIndex] == null) {
+            logs[nextIndex] = new EventEntry();
+        }
+        logs[nextIndex].update(type, event, extras);
+        nextIndex = (nextIndex + 1) % logs.length;
+    }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + name + " event history:");
+        SimpleDateFormat sdf = new SimpleDateFormat("  HH:mm:ss.SSSZ  ", Locale.US);
+        Date date = new Date();
+
+        for (int i = 0; i < logs.length; i++) {
+            EventEntry log = logs[(nextIndex + logs.length - i - 1) % logs.length];
+            if (log == null) {
+                continue;
+            }
+            date.setTime(log.time);
+
+            StringBuilder msg = new StringBuilder(prefix).append(sdf.format(date))
+                    .append(log.event);
+            switch (log.type) {
+                case TYPE_BOOL_FALSE:
+                    msg.append(": false");
+                    break;
+                case TYPE_BOOL_TRUE:
+                    msg.append(": true");
+                    break;
+                case TYPE_FLOAT:
+                    msg.append(": ").append(log.extras);
+                    break;
+                case TYPE_INTEGER:
+                    msg.append(": ").append((int) log.extras);
+                    break;
+                default: // fall out
+            }
+            if (log.duplicateCount > 0) {
+                msg.append(" & ").append(log.duplicateCount).append(" similar events");
+            }
+            writer.println(msg);
+        }
+    }
+
+    private boolean isEntrySame(EventEntry entry, int type, String event) {
+        return entry != null && entry.type == type && entry.event.equals(event);
+    }
+
+    /** A single event entry. */
+    private static class EventEntry {
+
+        private int type;
+        private String event;
+        private float extras;
+        private long time;
+        private int duplicateCount;
+
+        public void update(int type, String event, float extras) {
+            this.type = type;
+            this.event = event;
+            this.extras = extras;
+            time = System.currentTimeMillis();
+            duplicateCount = 0;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 7275576..2b20b08 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -340,8 +340,6 @@
                 Intent intent;
                 String targetPkg;
 
-                FolderIconPreviewVerifier verifier =
-                        new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
                 while (!mStopped && c.moveToNext()) {
                     try {
                         if (c.user == null) {
@@ -366,13 +364,13 @@
                             ComponentName cn = intent.getComponent();
                             targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
 
-                            if (!Process.myUserHandle().equals(c.user)) {
+                            if (allUsers.indexOfValue(c.user) < 0) {
                                 if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                                    c.markDeleted("Legacy shortcuts are only allowed for default user");
+                                    c.markDeleted("Legacy shortcuts are only allowed for current users");
                                     continue;
                                 } else if (c.restoreFlag != 0) {
                                     // Don't restore items for other profiles.
-                                    c.markDeleted("Restore from managed profile not supported");
+                                    c.markDeleted("Restore from other profiles not supported");
                                     continue;
                                 }
                             }
@@ -461,8 +459,7 @@
                                 c.markRestored();
                             }
 
-                            boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
-                                    !verifier.isItemInPreview(c.getInt(rankIndex));
+                            boolean useLowResIcon = !c.isOnWorkspaceOrHotseat();
 
                             if (c.restoreFlag != 0) {
                                 // Already verified above that user is same as default user
@@ -745,24 +742,25 @@
                 }
             }
 
+            // Sort the folder items, update ranks, and make sure all preview items are high res.
             FolderIconPreviewVerifier verifier =
                     new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
-            // Sort the folder items and make sure all items in the preview are high resolution.
             for (FolderInfo folder : mBgDataModel.folders) {
                 Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
                 verifier.setFolderInfo(folder);
+                int size = folder.contents.size();
 
-                int numItemsInPreview = 0;
-                for (ShortcutInfo info : folder.contents) {
+                // Update ranks here to ensure there are no gaps caused by removed folder items.
+                // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
+                // for now. Database will be updated once user manually modifies folder.
+                for (int rank = 0; rank < size; ++rank) {
+                    ShortcutInfo info = folder.contents.get(rank);
+                    info.rank = rank;
+
                     if (info.usingLowResIcon()
                             && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                             && verifier.isItemInPreview(info.rank)) {
                         mIconCache.getTitleAndIcon(info, false);
-                        numItemsInPreview++;
-                    }
-
-                    if (numItemsInPreview >= MAX_NUM_ITEMS_IN_PREVIEW) {
-                        break;
                     }
                 }
             }
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index bcca4d8..040b5e5 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -18,10 +18,15 @@
 
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
+import android.app.backup.BackupManager;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.LongSparseArray;
+import android.util.SparseLongArray;
 
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherProvider.DatabaseHelper;
@@ -48,10 +53,10 @@
     private static final String INFO_COLUMN_NAME = "name";
     private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
 
-    public static boolean performRestore(DatabaseHelper helper) {
+    public static boolean performRestore(DatabaseHelper helper, BackupManager backupManager) {
         SQLiteDatabase db = helper.getWritableDatabase();
         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
-            new RestoreDbTask().sanitizeDB(helper, db);
+            new RestoreDbTask().sanitizeDB(helper, db, backupManager);
             t.commit();
             return true;
         } catch (Exception e) {
@@ -62,20 +67,44 @@
 
     /**
      * Makes the following changes in the provider DB.
-     *   1. Removes all entries belonging to a managed profile as managed profiles
-     *      cannot be restored.
+     *   1. Removes all entries belonging to any profiles that were not restored.
      *   2. Marks all entries as restored. The flags are updated during first load or as
      *      the restored apps get installed.
-     *   3. If the user serial for primary profile is different than that of the previous device,
-     *      update the entries to the new profile id.
+     *   3. If the user serial for any restored profile is different than that of the previous
+     *      device, update the entries to the new profile id.
      */
-    private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db) throws Exception {
+    private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
+            throws Exception {
+        // Primary user ids
+        long myProfileId = helper.getDefaultUserSerial();
         long oldProfileId = getDefaultProfileId(db);
-        // Delete all entries which do not belong to the main user
-        int itemsDeleted = db.delete(
-                Favorites.TABLE_NAME, "profileId != ?", new String[]{Long.toString(oldProfileId)});
+        LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId);
+        LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size()
+                + 1);
+
+        // Build mapping of restored profile ids to their new profile ids.
+        profileMapping.put(oldProfileId, myProfileId);
+        for (int i = oldManagedProfileIds.size() - 1; i >= 0; --i) {
+            long oldManagedProfileId = oldManagedProfileIds.keyAt(i);
+            UserHandle user = getUserForAncestralSerialNumber(backupManager, oldManagedProfileId);
+            if (user != null) {
+                long newManagedProfileId = helper.getSerialNumberForUser(user);
+                profileMapping.put(oldManagedProfileId, newManagedProfileId);
+            }
+        }
+
+        // Delete all entries which do not belong to any restored profile(s).
+        int numProfiles = profileMapping.size();
+        String[] profileIds = new String[numProfiles];
+        profileIds[0] = Long.toString(oldProfileId);
+        StringBuilder whereClause = new StringBuilder("profileId != ?");
+        for (int i = profileMapping.size() - 1; i >= 1; --i) {
+            whereClause.append(" AND profileId != ?");
+            profileIds[i] = Long.toString(profileMapping.keyAt(i));
+        }
+        int itemsDeleted = db.delete(Favorites.TABLE_NAME, whereClause.toString(), profileIds);
         if (itemsDeleted > 0) {
-            FileLog.d(TAG, itemsDeleted + " items belonging to a managed profile, were deleted");
+            FileLog.d(TAG, itemsDeleted + " items from unrestored user(s) were deleted");
         }
 
         // Mark all items as restored.
@@ -85,7 +114,7 @@
                 | (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
         db.update(Favorites.TABLE_NAME, values, null, null);
 
-        // Mark widgets with appropriate restore flag
+        // Mark widgets with appropriate restore flag.
         values.put(Favorites.RESTORED,  LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
                 LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
                 LauncherAppWidgetInfo.FLAG_UI_NOT_READY |
@@ -93,21 +122,46 @@
         db.update(Favorites.TABLE_NAME, values, "itemType = ?",
                 new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
 
-        long myProfileId = helper.getDefaultUserSerial();
+        // Migrate ids. To avoid any overlap, we initially move conflicting ids to a temp location.
+        // Using Long.MIN_VALUE since profile ids can not be negative, so there will be no overlap.
+        final long tempLocationOffset = Long.MIN_VALUE;
+        SparseLongArray tempMigratedIds = new SparseLongArray(profileMapping.size());
+        int numTempMigrations = 0;
+        for (int i = profileMapping.size() - 1; i >= 0; --i) {
+            long oldId = profileMapping.keyAt(i);
+            long newId = profileMapping.valueAt(i);
+
+            if (oldId != newId) {
+                if (profileMapping.indexOfKey(newId) >= 0) {
+                    tempMigratedIds.put(numTempMigrations, newId);
+                    numTempMigrations++;
+                    newId = tempLocationOffset + newId;
+                }
+                migrateProfileId(db, oldId, newId);
+            }
+        }
+
+        // Migrate ids from their temporary id to their actual final id.
+        for (int i = tempMigratedIds.size() - 1; i >= 0; --i) {
+            long newId = tempMigratedIds.valueAt(i);
+            migrateProfileId(db, tempLocationOffset + newId, newId);
+        }
+
         if (myProfileId != oldProfileId) {
-            FileLog.d(TAG, "Changing primary user id from " + oldProfileId + " to " + myProfileId);
-            migrateProfileId(db, myProfileId);
+            changeDefaultColumn(db, myProfileId);
         }
     }
 
     /**
-     * Updates profile id of all entries and changes the default value for the column.
+     * Updates profile id of all entries from {@param oldProfileId} to {@param newProfileId}.
      */
-    protected void migrateProfileId(SQLiteDatabase db, long newProfileId) {
+    protected void migrateProfileId(SQLiteDatabase db, long oldProfileId, long newProfileId) {
+        FileLog.d(TAG, "Changing profile user id from " + oldProfileId + " to " + newProfileId);
         // Update existing entries.
         ContentValues values = new ContentValues();
         values.put(Favorites.PROFILE_ID, newProfileId);
-        db.update(Favorites.TABLE_NAME, values, null, null);
+        db.update(Favorites.TABLE_NAME, values, "profileId = ?",
+                new String[]{Long.toString(oldProfileId)});
 
         // Change default value of the column.
         db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
@@ -116,6 +170,43 @@
         dropTable(db, "favorites_old");
     }
 
+
+    /**
+     * Changes the default value for the column.
+     */
+    protected void changeDefaultColumn(SQLiteDatabase db, long newProfileId) {
+        db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
+        Favorites.addTableToDb(db, newProfileId, false);
+        db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
+        dropTable(db, "favorites_old");
+    }
+
+    /**
+     * Returns a list of the managed profile id(s) used in the favorites table of the provided db.
+     */
+    private LongSparseArray<Long> getManagedProfileIds(SQLiteDatabase db, long defaultProfileId) {
+        LongSparseArray<Long> ids = new LongSparseArray<>();
+        try (Cursor c = db.rawQuery("SELECT profileId from favorites WHERE profileId != ? "
+                + "GROUP BY profileId", new String[] {Long.toString(defaultProfileId)})){
+                while (c.moveToNext()) {
+                    ids.put(c.getLong(c.getColumnIndex(Favorites.PROFILE_ID)), null);
+                }
+        }
+        return ids;
+    }
+
+    /**
+     * Returns a UserHandle of a restored managed profile with the given serial number, or null
+     * if none found.
+     */
+    private UserHandle getUserForAncestralSerialNumber(BackupManager backupManager,
+            long ancestralSerialNumber) {
+        if (Build.VERSION.SDK_INT < 29) {
+            return null;
+        }
+        return backupManager.getUserForAncestralSerialNumber(ancestralSerialNumber);
+    }
+
     /**
      * Returns the profile id used in the favorites table of the provided db.
      */
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index d49dfbb..fcace98 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -73,8 +73,8 @@
     }
 
     @Override
-    public boolean scaleHotseatWithWorkspace() {
-        return false;
+    public float[] getHotseatScaleAndTranslation(Launcher launcher) {
+        return new float[] {1, 0, 0};
     }
 
     @Override
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 2b9e7b6..ec0462b 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -35,6 +35,7 @@
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.InsettableFrameLayout.LayoutParams;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
@@ -43,7 +44,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.FolderShape;
+import com.android.launcher3.graphics.ShiftedBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 
 import androidx.annotation.Nullable;
@@ -57,6 +60,8 @@
 
 public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
 
+    private static final Rect sTmpRect = new Rect();
+
     private Runnable mStartRunnable;
     private Runnable mEndRunnable;
 
@@ -77,7 +82,7 @@
 
     private final Rect mFinalDrawableBounds = new Rect();
     private final Rect mBgDrawableBounds = new Rect();
-    private final float mBgDrawableStartScale = 5f; // Magic number that can be tuned later.
+    private float mBgDrawableStartScale = 1f;
 
     private FloatingIconView(Context context) {
         super(context);
@@ -181,14 +186,22 @@
     }
 
     @WorkerThread
-    private void getIcon(Launcher launcher, ItemInfo info, boolean useDrawableAsIs,
+    private void getIcon(Launcher launcher, View v, ItemInfo info, boolean useDrawableAsIs,
             float aspectRatio) {
         final LayoutParams lp = (LayoutParams) getLayoutParams();
-        mDrawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height, new Object[1]);
 
-        if (ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-                && mDrawable instanceof AdaptiveIconDrawable) {
+        boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+        if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
+            // Similar to DragView, we simply use the BubbleTextView icon here.
+            mDrawable = ((BubbleTextView) v).getIcon();
+        }
+        if (mDrawable == null) {
+            mDrawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
+                    useDrawableAsIs, new Object[1]);
+        }
+
+        if (supportsAdaptiveIcons && mDrawable instanceof AdaptiveIconDrawable) {
             mIsAdaptiveIcon = true;
 
             AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) mDrawable;
@@ -205,6 +218,12 @@
 
             int offset = getOffsetForAdaptiveIconBounds();
             mFinalDrawableBounds.set(offset, offset, lp.width - offset, mOriginalHeight - offset);
+            if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
+                ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
+                ((FolderIcon) v).getPreviewBounds(sTmpRect);
+                sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
+                sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
+            }
             mForeground.setBounds(mFinalDrawableBounds);
             mBackground.setBounds(mFinalDrawableBounds);
 
@@ -216,8 +235,9 @@
                 lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
                 layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
                         + lp.height);
-                setBackgroundDrawableBounds(mBgDrawableStartScale);
             }
+            mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
+            setBackgroundDrawableBounds(mBgDrawableStartScale);
 
             // Set up outline
             mOutline.set(0, 0, lp.width, lp.height);
@@ -241,6 +261,9 @@
     private void setBackgroundDrawableBounds(float scale) {
         mBgDrawableBounds.set(mFinalDrawableBounds);
         Utilities.scaleRectAboutCenter(mBgDrawableBounds, scale);
+        // Since the drawable is at the top of the view, we need to offset to keep it centered.
+        mBgDrawableBounds.offsetTo(mBgDrawableBounds.left,
+                (int) (mFinalDrawableBounds.top * scale));
         mBackground.setBounds(mBgDrawableBounds);
     }
 
@@ -323,8 +346,8 @@
         // Must be called after matchPositionOf so that we know what size to load.
         if (originalView.getTag() instanceof ItemInfo) {
             new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
-                view.getIcon(launcher, (ItemInfo) originalView.getTag(), useDrawableAsIs,
-                        aspectRatio);
+                view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(),
+                        useDrawableAsIs, aspectRatio);
             });
         }
 
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index babb731..6fa8d62 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -41,18 +41,34 @@
         // Verify item add
         assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
 
-        new RestoreDbTask().migrateProfileId(db, 33);
+        new RestoreDbTask().migrateProfileId(db, 42, 33);
 
         // verify data migrated
         assertEquals(0, getCount(db, "select * from favorites where profileId = 42"));
         assertEquals(5, getCount(db, "select * from favorites where profileId = 33"));
+    }
+
+    @Test
+    public void testChangeDefaultColumn() throws Exception {
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+        // Add some dummy data
+        for (int i = 0; i < 5; i++) {
+            ContentValues values = new ContentValues();
+            values.put(Favorites._ID, i);
+            values.put(Favorites.TITLE, "item " + i);
+            db.insert(Favorites.TABLE_NAME, null, values);
+        }
+        // Verify default column is 42
+        assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
+
+        new RestoreDbTask().changeDefaultColumn(db, 33);
 
         // Verify default value changed
         ContentValues values = new ContentValues();
         values.put(Favorites._ID, 100);
         values.put(Favorites.TITLE, "item 100");
         db.insert(Favorites.TABLE_NAME, null, values);
-        assertEquals(6, getCount(db, "select * from favorites where profileId = 33"));
+        assertEquals(1, getCount(db, "select * from favorites where profileId = 33"));
     }
 
     private int getCount(SQLiteDatabase db, String sql) {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 02f5502..dd768fd 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -98,6 +98,10 @@
         }
         if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
         mLauncher = new LauncherInstrumentation(instrumentation);
+        try {
+            mDevice.executeShellCommand("settings delete secure assistant");
+        } catch (IOException e) {
+        }
     }
 
     @Rule
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 82ea8be..1353a23 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -29,7 +29,7 @@
 public class AllApps extends LauncherInstrumentation.VisibleContainer {
     private static final int MAX_SCROLL_ATTEMPTS = 40;
     private static final int MIN_INTERACT_SIZE = 100;
-    private static final int FLING_SPEED = 12000;
+    private static final int FLING_SPEED = 3000;
 
     private final int mHeight;
 
@@ -120,7 +120,7 @@
         final UiObject2 allAppsContainer = verifyActiveContainer();
         // Start the gesture in the center to avoid starting at elements near the top.
         allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
-        allAppsContainer.fling(Direction.DOWN, FLING_SPEED);
+        allAppsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         verifyActiveContainer();
     }
 
@@ -131,7 +131,7 @@
         final UiObject2 allAppsContainer = verifyActiveContainer();
         // Start the gesture in the center, for symmetry with forward.
         allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
-        allAppsContainer.fling(Direction.UP, FLING_SPEED);
+        allAppsContainer.fling(Direction.UP, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         verifyActiveContainer();
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 0ff3070..6e92dad 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -28,7 +28,7 @@
  * Common overview pane for both Launcher and fallback recents
  */
 public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
-    private static final int DEFAULT_FLING_SPEED = 15000;
+    private static final int FLING_SPEED = 1500;
     private static final int FLINGS_FOR_DISMISS_LIMIT = 5;
 
     BaseOverview(LauncherInstrumentation launcher) {
@@ -46,7 +46,7 @@
     public void flingForward() {
         final UiObject2 overview = verifyActiveContainer();
         LauncherInstrumentation.log("Overview.flingForward before fling");
-        overview.fling(Direction.LEFT, DEFAULT_FLING_SPEED);
+        overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         mLauncher.waitForIdle();
         verifyActiveContainer();
     }
@@ -73,7 +73,7 @@
     public void flingBackward() {
         final UiObject2 overview = verifyActiveContainer();
         LauncherInstrumentation.log("Overview.flingBackward before fling");
-        overview.fling(Direction.RIGHT, DEFAULT_FLING_SPEED);
+        overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         mLauncher.waitForIdle();
         verifyActiveContainer();
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 7473189..93b4cc6 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -93,8 +93,6 @@
     private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
 
     private final UiDevice mDevice;
-    private final boolean mSwipeUpEnabled;
-    private Boolean mSwipeUpEnabledOverride = null;
     private final Instrumentation mInstrumentation;
     private int mExpectedRotation = Surface.ROTATION_0;
 
@@ -104,13 +102,6 @@
     public LauncherInstrumentation(Instrumentation instrumentation) {
         mInstrumentation = instrumentation;
         mDevice = UiDevice.getInstance(instrumentation);
-        final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
-        mSwipeUpEnabled = SwipeUpSetting.isSwipeUpSettingAvailable() ?
-                Settings.Secure.getInt(
-                        instrumentation.getTargetContext().getContentResolver(),
-                        SWIPE_UP_SETTING_NAME,
-                        swipeUpEnabledDefaultValue ? 1 : 0) == 1 :
-                swipeUpEnabledDefaultValue;
 
         // Launcher should run in test harness so that custom accessibility protocol between
         // Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
@@ -119,17 +110,18 @@
                 TestHelpers.isInLauncherProcess() || ActivityManager.isRunningInTestHarness());
     }
 
-    // Used only by TaplTests.
-    public void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride) {
-        mSwipeUpEnabledOverride = swipeUpEnabledOverride;
-    }
-
     void setActiveContainer(VisibleContainer container) {
         sActiveContainer = new WeakReference<>(container);
     }
 
     public boolean isSwipeUpEnabled() {
-        return mSwipeUpEnabledOverride != null ? mSwipeUpEnabledOverride : mSwipeUpEnabled;
+        final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
+        return SwipeUpSetting.isSwipeUpSettingAvailable() ?
+                Settings.Secure.getInt(
+                        mInstrumentation.getTargetContext().getContentResolver(),
+                        SWIPE_UP_SETTING_NAME,
+                        swipeUpEnabledDefaultValue ? 1 : 0) == 1 :
+                swipeUpEnabledDefaultValue;
     }
 
     static void log(String message) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 77555a3..89affd1 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -23,7 +23,7 @@
  * All widgets container.
  */
 public final class Widgets extends LauncherInstrumentation.VisibleContainer {
-    private static final int FLING_SPEED = 6000;
+    private static final int FLING_SPEED = 1500;
 
     Widgets(LauncherInstrumentation launcher) {
         super(launcher);
@@ -36,7 +36,7 @@
     public void flingForward() {
         final UiObject2 widgetsContainer = verifyActiveContainer();
         widgetsContainer.setGestureMargin(100);
-        widgetsContainer.fling(Direction.DOWN, FLING_SPEED);
+        widgetsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         verifyActiveContainer();
     }
 
@@ -46,7 +46,7 @@
     public void flingBackward() {
         final UiObject2 widgetsContainer = verifyActiveContainer();
         widgetsContainer.setGestureMargin(100);
-        widgetsContainer.fling(Direction.UP, FLING_SPEED);
+        widgetsContainer.fling(Direction.UP, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         mLauncher.waitForIdle();
         verifyActiveContainer();
     }