Merge "Using UI_HELPER_THREAD for various activity manager interactions"
diff --git a/gradle.properties b/gradle.properties
index 7a51375..7f4c609 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,4 +10,4 @@
 PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
 
 BUILD_TOOLS_VERSION=28.0.3
-COMPILE_SDK=android-R
+COMPILE_SDK=android-S
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8cef560..62f60a7 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -41,10 +41,8 @@
 import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
 import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
 import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -1483,13 +1481,11 @@
                 mRecentsAnimationTargets.apps,
                 mRecentsAnimationTargets.apps.length + 1);
         apps[apps.length - 1] = appearedTaskTarget;
-        boolean launcherClosing =
-                taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING);
 
         AnimatorSet anim = new AnimatorSet();
         TaskViewUtils.composeRecentsLaunchAnimator(
                 anim, taskView, apps,
-                mRecentsAnimationTargets.wallpapers, launcherClosing,
+                mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
                 mActivity.getStateManager(), mRecentsView,
                 mActivityInterface.getDepthController());
         anim.addListener(new AnimatorListenerAdapter(){
@@ -1557,11 +1553,6 @@
         mGestureEndCallback = gestureEndCallback;
     }
 
-    @Override
-    public long getStartTouchTime() {
-        return mTouchTimeMs;
-    }
-
     protected void linkRecentsViewScroll() {
         SurfaceTransactionApplier.create(mRecentsView, applier -> {
             mTransformParams.setSyncTransactionApplier(applier);
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index f788996..8d67ee6 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -142,6 +142,9 @@
     private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
     private int mLastStartedTaskId = -1;
 
+    /** The time when the swipe up gesture is triggered. */
+    private long mSwipeUpStartTimeMs;
+
     public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
         mHomeIntent = componentObserver.getHomeIntent();
         mOverviewIntent = componentObserver.getOverviewIntent();
@@ -343,6 +346,14 @@
         mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
     }
 
+    void setSwipeUpStartTimeMs(long uptimeMs) {
+        mSwipeUpStartTimeMs = uptimeMs;
+    }
+
+    long getSwipeUpStartTimeMs() {
+        return mSwipeUpStartTimeMs;
+    }
+
     public void dump(PrintWriter pw) {
         pw.println("GestureState:");
         pw.println("  gestureID=" + mGestureId);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index f319b94..a21c714 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -160,12 +160,5 @@
          * Callback made when a task started from the recents is ready for an app transition.
          */
         default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {}
-
-        /**
-         * The time in milliseconds of the touch event that starts the recents animation.
-         */
-        default long getStartTouchTime() {
-            return 0;
-        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 797797f..6f2f86e 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -119,7 +119,7 @@
                 }
             }
         });
-        final long eventTime = listener.getStartTouchTime();
+        final long eventTime = gestureState.getSwipeUpStartTimeMs();
         mCallbacks.addListener(gestureState);
         mCallbacks.addListener(listener);
         UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 3a47024..6677724 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -31,6 +31,7 @@
 import android.view.View;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.RequiresApi;
 
 import com.android.launcher3.BaseActivity;
@@ -157,9 +158,8 @@
                 getActionsView().setCallbacks(new OverlayUICallbacks() {
                     @Override
                     public void onShare() {
-                        endLiveTileMode(isAllowedByPolicy);
                         if (isAllowedByPolicy) {
-                            mImageApi.startShareActivity();
+                            endLiveTileMode(mImageApi::startShareActivity);
                         } else {
                             showBlockedByPolicyMessage();
                         }
@@ -168,8 +168,7 @@
                     @SuppressLint("NewApi")
                     @Override
                     public void onScreenshot() {
-                        endLiveTileMode(isAllowedByPolicy);
-                        saveScreenshot(task);
+                        endLiveTileMode(() -> saveScreenshot(task));
                     }
                 });
             }
@@ -178,17 +177,15 @@
         /**
          * End rendering live tile in Overview.
          *
-         * @param showScreenshot if it's true, we take a screenshot and switch to it.
+         * @param callback callback to run, after switching to screenshot
          */
-        public void endLiveTileMode(boolean showScreenshot) {
+        public void endLiveTileMode(@NonNull Runnable callback) {
             if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
                 RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
-                if (showScreenshot) {
-                    recentsView.switchToScreenshot(
-                            () -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
-                } else {
-                    recentsView.finishRecentsAnimation(true /* toRecents */, null);
-                }
+                recentsView.switchToScreenshot(
+                        () -> recentsView.finishRecentsAnimation(true /* toRecents */, callback));
+            } else {
+                callback.run();
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 46c7768..ed761cf 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -297,7 +297,6 @@
         PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
         createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
                 depthController, pa);
-        anim.play(pa.buildAnim());
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
@@ -330,7 +329,11 @@
                 }
             };
         }
-        anim.play(launcherAnim);
+        pa.add(launcherAnim);
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) {
+            pa.addOnFrameCallback(recentsView::redrawLiveTile);
+        }
+        anim.play(pa.buildAnim());
 
         // Set the current animation first, before adding windowAnimEndListener. Setting current
         // animation adds some listeners which need to be called before windowAnimEndListener
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1e4f297..8ce1f51 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -45,6 +45,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.InputEvent;
@@ -447,6 +448,7 @@
                 // onConsumerInactive and wipe the previous gesture state
                 GestureState prevGestureState = new GestureState(mGestureState);
                 GestureState newGestureState = createGestureState(mGestureState);
+                newGestureState.setSwipeUpStartTimeMs(SystemClock.uptimeMillis());
                 mConsumer.onConsumerAboutToBeSwitched();
                 mGestureState = newGestureState;
                 mConsumer = newConsumer(prevGestureState, mGestureState, event);
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 08fe48d..61ba411 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -52,16 +52,16 @@
 
     public static final IntProperty<TaskViewSimulator> SCROLL =
             new IntProperty<TaskViewSimulator>("scroll") {
-        @Override
-        public void setValue(TaskViewSimulator simulator, int i) {
-            simulator.setScroll(i);
-        }
+                @Override
+                public void setValue(TaskViewSimulator simulator, int scroll) {
+                    simulator.setScroll(scroll);
+                }
 
-        @Override
-        public Integer get(TaskViewSimulator simulator) {
-            return simulator.mScrollState.scroll;
-        }
-    };
+                @Override
+                public Integer get(TaskViewSimulator simulator) {
+                    return simulator.mScrollState.scroll;
+                }
+            };
 
     private final Rect mTmpCropRect = new Rect();
     private final RectF mTempRectF = new RectF();
@@ -72,7 +72,6 @@
     private final BaseActivityInterface mSizeStrategy;
 
     private final Rect mTaskRect = new Rect();
-    private float mOffsetY;
     private boolean mDrawsBelowRecents;
     private final PointF mPivot = new PointF();
     private DeviceProfile mDp;
@@ -89,6 +88,8 @@
     // TaskView properties
     private final FullscreenDrawParams mCurrentFullscreenParams;
     private float mCurveScale = 1;
+    public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat();
+    public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat();
 
     // RecentsView properties
     public final AnimatedFloat recentsViewScale = new AnimatedFloat();
@@ -178,10 +179,6 @@
         }
     }
 
-    public void setOffsetY(float offsetY) {
-        mOffsetY = offsetY;
-    }
-
     public void setDrawsBelowRecents(boolean drawsBelowRecents) {
         mDrawsBelowRecents = drawsBelowRecents;
     }
@@ -298,7 +295,11 @@
 
         // Apply TaskView matrix: scale, translate, scroll
         mMatrix.postScale(mCurveScale, mCurveScale, taskWidth / 2, taskHeight / 2);
-        mMatrix.postTranslate(mTaskRect.left, mTaskRect.top + mOffsetY);
+        mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
+        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+                taskPrimaryTranslation.value);
+        mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
+                taskSecondaryTranslation.value);
         mOrientationState.getOrientationHandler().set(
                 mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index af1f93a..f35eb1f 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -51,6 +51,7 @@
 import android.animation.LayoutTransition;
 import android.animation.LayoutTransition.TransitionListener;
 import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -99,7 +100,6 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PendingAnimation.EndState;
-import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.anim.SpringProperty;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
@@ -113,6 +113,7 @@
 import com.android.launcher3.util.ResourceBasedOverride.Overrides;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewPool;
+import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.RecentsAnimationController;
@@ -872,9 +873,10 @@
             // Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
             // to reset the params after it settles in Overview from swipe up so that we don't
             // render with obsolete param values.
+            mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0;
+            mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0;
             mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
             mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
-            mLiveTileTaskViewSimulator.setOffsetY(0);
 
             // Reset the live tile rect
             DeviceProfile deviceProfile = mActivity.getDeviceProfile();
@@ -1543,7 +1545,10 @@
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && getRunningTaskView() == taskView) {
             anim.addOnFrameCallback(() -> {
-                mLiveTileTaskViewSimulator.setOffsetY(taskView.getTranslationY());
+                mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
+                        mOrientationHandler.getSecondaryValue(
+                                taskView.getTranslationX(),
+                                taskView.getTranslationY());
                 redrawLiveTile();
             });
         }
@@ -2089,22 +2094,35 @@
         boolean launchingCenterTask = taskIndex == centerTaskIndex;
 
         float toScale = getMaxScaleForFullScreen();
+        RecentsView recentsView = tv.getRecentsView();
         if (launchingCenterTask) {
-            RecentsView recentsView = tv.getRecentsView();
             anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
             anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
         } else {
             // We are launching an adjacent task, so parallax the center and other adjacent task.
             float displacementX = tv.getWidth() * (toScale - tv.getCurveScale());
-            anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex), TRANSLATION_X,
-                    mIsRtl ? -displacementX : displacementX));
+            float primaryTranslation = mIsRtl ? -displacementX : displacementX;
+            anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
+                    mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
+            int runningTaskIndex = recentsView.getRunningTaskIndex();
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
+                    && runningTaskIndex != taskIndex) {
+                anim.play(ObjectAnimator.ofFloat(
+                        recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
+                        AnimatedFloat.VALUE,
+                        primaryTranslation));
+            }
 
             int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
             if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < getPageCount()) {
-                anim.play(new PropertyListBuilder()
-                        .translationX(mIsRtl ? -displacementX : displacementX)
-                        .scale(1)
-                        .build(getPageAt(otherAdjacentTaskIndex)));
+                PropertyValuesHolder[] properties = new PropertyValuesHolder[3];
+                properties[0] = PropertyValuesHolder.ofFloat(
+                        mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation);
+                properties[1] = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+                properties[2] = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+
+                anim.play(ObjectAnimator.ofPropertyValuesHolder(getPageAt(otherAdjacentTaskIndex),
+                        properties));
             }
         }
         return anim;
diff --git a/res/layout/search_result_play_item.xml b/res/layout/search_result_play_item.xml
index cdb793c..d70c56a 100644
--- a/res/layout/search_result_play_item.xml
+++ b/res/layout/search_result_play_item.xml
@@ -16,7 +16,7 @@
 <com.android.launcher3.views.SearchResultPlayItem xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:padding="8dp"
+    android:padding="4dp"
     android:orientation="horizontal">
     <View
         android:id="@+id/icon"
@@ -31,13 +31,18 @@
         android:layout_gravity="start|center_vertical"
         android:layout_weight="1"
         android:orientation="vertical"
-        android:padding="8dp">
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp">
 
         <TextView
             android:id="@+id/title_view"
             style="@style/TextHeadline"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:maxLines="1"
+            android:ellipsize="end"
             android:textAlignment="viewStart"
             android:textColor="?android:attr/textColorPrimary"
             android:textSize="16sp" />
diff --git a/res/layout/search_result_suggest.xml b/res/layout/search_result_suggest.xml
index a3227cb..1d8c803 100644
--- a/res/layout/search_result_suggest.xml
+++ b/res/layout/search_result_suggest.xml
@@ -21,12 +21,12 @@
     android:gravity="start|center_vertical"
     android:textAlignment="viewStart"
     android:textColor="?android:attr/textColorPrimary"
-    android:textSize="16sp"
+    android:textSize="18sp"
     android:padding="@dimen/dynamic_grid_edge_margin"
     launcher:iconDisplay="hero_app"
     android:drawableTint="?android:attr/textColorPrimary"
     launcher:customIcon="@drawable/ic_allapps_search"
-    launcher:iconSizeOverride="24dp"
+    launcher:iconSizeOverride="48dp"
     launcher:matchTextInsetWithQuery="true"
     launcher:layoutHorizontal="true"
     android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index a55c90d..8bf027d 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -86,13 +86,12 @@
             long mainProfileId = UserCache.INSTANCE.get(context)
                     .getSerialNumberForUser(myUserHandle());
             String oldWidgetId = Integer.toString(oldWidgetIds[i]);
-            int result = new ContentWriter(context, new ContentWriter.CommitParams(
-                    "appWidgetId=? and (restored & 1) = 1 and profileId=?",
-                    new String[] { oldWidgetId, Long.toString(mainProfileId) }))
+            final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
+            final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
+            int result = new ContentWriter(context, new ContentWriter.CommitParams(where, args))
                     .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
                     .put(LauncherSettings.Favorites.RESTORED, state)
                     .commit();
-
             if (result == 0) {
                 Cursor cursor = cr.query(Favorites.CONTENT_URI,
                         new String[] {Favorites.APPWIDGET_ID},
@@ -106,6 +105,11 @@
                     cursor.close();
                 }
             }
+            // attempt to update widget id in backup table as well
+            new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams(where, args))
+                    .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+                    .put(LauncherSettings.Favorites.RESTORED, state)
+                    .commit();
         }
 
         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1015a32..5cd6372 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -446,6 +446,7 @@
             canvas.restoreToCount(count);
         }
         super.onDraw(canvas);
+        drawDotIfNecessary(canvas);
     }
 
     protected void drawFocusHighlight(Canvas canvas) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ab8d7a5..699734c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1829,7 +1829,7 @@
                 mTouchInProgress = true;
                 break;
             case MotionEvent.ACTION_UP:
-                mLastTouchUpTime = ev.getEventTime();
+                mLastTouchUpTime = SystemClock.uptimeMillis();
                 // Follow through
             case MotionEvent.ACTION_CANCEL:
                 mTouchInProgress = false;
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index d2758f5..fe423ed 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -121,6 +121,12 @@
                 + LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
 
         /**
+         * The content:// style URL for "favorites_bakup" table
+         */
+        public static final Uri BACKUP_CONTENT_URI = Uri.parse("content://"
+                + LauncherProvider.AUTHORITY + "/" + BACKUP_TABLE_NAME);
+
+        /**
          * The content:// style URL for "favorites_preview" table
          */
         public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
index d7af5f1..08d573f 100644
--- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
@@ -29,13 +29,15 @@
 import androidx.annotation.RequiresApi;
 import androidx.core.os.BuildCompat;
 
+import com.android.launcher3.util.UiThreadHelper;
+
 /**
  * Handles IME over all apps to be synchronously transitioning along with the passed in
  * root inset.
  */
 public class AllAppsInsetTransitionController {
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
     private static final String TAG = "AllAppsInsetTransitionController";
     private static final Interpolator LINEAR = new LinearInterpolator();
 
@@ -53,6 +55,13 @@
     private float mDown, mCurrent;
     private View mApps;
 
+    // Only purpose of these states is to keep track of fast fling transition
+    enum State {
+        RESET, DRAG_START_BOTTOM, FLING_END_TOP,
+        DRAG_START_TOP, FLING_END_BOTTOM
+    }
+    private State mState;
+
     public AllAppsInsetTransitionController(float allAppsHeight, View appsView) {
         mAllAppsHeight = allAppsHeight;
         mApps = appsView;
@@ -64,6 +73,11 @@
         WindowInsets insets = mApps.getRootWindowInsets();
         if (insets == null) return;
 
+        boolean imeVisible = insets.isVisible(WindowInsets.Type.ime());
+
+        if (DEBUG) {
+            Log.d(TAG, "\nhide imeVisible=" +  imeVisible);
+        }
         if (insets.isVisible(WindowInsets.Type.ime())) {
             mApps.getWindowInsetsController().hide(WindowInsets.Type.ime());
         }
@@ -78,20 +92,23 @@
     @RequiresApi(api = Build.VERSION_CODES.R)
     public void onDragStart(float progress) {
         if (!BuildCompat.isAtLeastR()) return;
-        onAnimationEnd(progress);
 
+        // Until getRootWindowInsets().isVisible(...) method returns correct value,
+        // only support InsetController based IME transition during swipe up and
+        // NOT swipe down
+        if (Float.compare(progress, 0f) == 0) return;
+
+        setState(true, false, progress);
         mDown = progress * mAllAppsHeight;
 
         // Below two values are sometimes incorrect. Possibly a platform bug
-        mDownInsetBottom = mApps.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
-        mShownAtDown = mApps.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+        // mDownInsetBottom = mApps.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
+        // mShownAtDown = mApps.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
 
-        // override this value based on what it should actually be.
-        mShownAtDown = Float.compare(progress, 1f) == 0 ? false : true;
-        mDownInsetBottom = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
         if (DEBUG) {
-            Log.d(TAG, "\nonDragStart mDownInsets=" + mDownInsetBottom
-                    + " mShownAtDown =" + mShownAtDown);
+            Log.d(TAG, "\nonDragStart progress=" +  progress
+                    + " mDownInsets=" + mDownInsetBottom
+                    + " mShownAtDown=" + mShownAtDown);
         }
 
         mApps.getWindowInsetsController().controlWindowInsetsAnimation(
@@ -103,37 +120,63 @@
                         if (DEBUG) {
                             Log.d(TAG, "Listener.onReady " + (mCurrentRequest == this));
                         }
-                        if (mCurrentRequest == this) {
-                            mAnimationController = controller;
-                        } else {
-                            controller.finish(mShownAtDown);
+                        if (controller != null) {
+                            if (mCurrentRequest == this && !handleFinishOnFling(controller)) {
+                                    mAnimationController = controller;
+                            } else {
+                                controller.finish(false /* just don't show */);
+                            }
                         }
                     }
 
                     @Override
                     public void onFinished(WindowInsetsAnimationController controller) {
                         // when screen lock happens, then this method get called
-                        mAnimationController.finish(false);
-                        mAnimationController = null;
                         if (DEBUG) {
-                            Log.d(TAG, "Listener.onFinished ctrl=" + controller);
+                            Log.d(TAG, "Listener.onFinished ctrl=" + controller
+                                    + " mAnimationController=" + mAnimationController);
+                        }
+                        if (mAnimationController != null) {
+                            mAnimationController.finish(true);
+                            mAnimationController = null;
                         }
                     }
 
                     @Override
                     public void onCancelled(@Nullable WindowInsetsAnimationController controller) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Listener.onCancelled ctrl=" + controller
+                                    + " mAnimationController=" + mAnimationController);
+                        }
+                        if (mState == State.DRAG_START_BOTTOM) {
+                            mApps.getWindowInsetsController().show(WindowInsets.Type.ime());
+                        }
                         mAnimationController = null;
                         if (controller != null) {
-                            controller.finish(mShownAtDown);
+                            controller.finish(true);
                         }
-                        if (DEBUG) {
-                            Log.d(TAG, "Listener.onCancelled ctrl=" + controller);
-                        }
+
                     }
                 });
     }
 
     /**
+     * If IME bounds after touch sequence finishes, call finish.
+     */
+    private boolean handleFinishOnFling(WindowInsetsAnimationController controller) {
+        if (!BuildCompat.isAtLeastR()) return false;
+
+        if (mState == State.FLING_END_TOP) {
+            controller.finish(true);
+            return true;
+        } else if (mState == State.FLING_END_BOTTOM) {
+            controller.finish(false);
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Handles the translation using the progress.
      *
      * @param progress value between 0..1
@@ -153,18 +196,17 @@
 
         int inset = (int) (mDownInsetBottom + (mDown - mCurrent) - shift);
 
-        if (DEBUG) {
-            Log.d(TAG, "updateInset mCurrent=" + mCurrent + " mDown="
-                    + mDown + " hidden=" + mHiddenInsetBottom
-                    + " shown=" + mShownInsetBottom
-                    + " mDownInsets.bottom=" + mDownInsetBottom + " inset:" + inset
-                    + " shift: " + shift);
-        }
         final int start = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
         final int end = mShownAtDown ? mHiddenInsetBottom : mShownInsetBottom;
         inset = Math.max(inset, mHiddenInsetBottom);
         inset = Math.min(inset, mShownInsetBottom);
-        Log.d(TAG, "updateInset inset:" + inset);
+        if (DEBUG || false) {
+            Log.d(TAG, "updateInset mCurrent=" + mCurrent + " mDown="
+                    + mDown + " hidden=" + mHiddenInsetBottom
+                    + " shown=" + mShownInsetBottom
+                    + " mDownInsets.bottom=" + mDownInsetBottom + " inset=" + inset
+                    + " shift= " + shift);
+        }
 
         mAnimationController.setInsetsAndAlpha(
                 Insets.of(0, 0, 0, inset),
@@ -179,19 +221,60 @@
     @RequiresApi(api = 30)
     public void onAnimationEnd(float progress) {
         if (DEBUG) {
+            Log.d(TAG, "onAnimationEnd progress=" + progress
+                    + " mAnimationController=" + mAnimationController);
+        }
+        if (mState == null) {
+            // only called when launcher restarting.
+            UiThreadHelper.hideKeyboardAsync(mApps.getContext(), mApps.getWindowToken());
+        }
+        setState(false, true, progress);
+        if (mAnimationController == null) {
+            return;
+        }
+
+        /* handle finish */
+        if (mState == State.FLING_END_TOP) {
+            mAnimationController.finish(true /* show */);
+        } else {
+            if (Float.compare(progress, 1f) == 0 /* bottom */) {
+                mAnimationController.finish(false /* gone */);
+            } else {
+                mAnimationController.finish(mShownAtDown);
+            }
+        }
+        /* handle finish */
+
+        if (DEBUG) {
             Log.d(TAG, "endTranslation progress=" + progress
                     + " mAnimationController=" + mAnimationController);
         }
-
-        if (mAnimationController == null) return;
-
-        if (Float.compare(progress, 1f) == 0 /* bottom */) {
-            mAnimationController.finish(false /* gone */);
-        }
-        if (Float.compare(progress, 0f) == 0 /* top */) {
-            mAnimationController.finish(true /* show */);
-        }
         mAnimationController = null;
         mCurrentRequest = null;
+        setState(false, false, progress);
+    }
+
+    private void setState(boolean start, boolean end, float progress) {
+        State state = State.RESET;
+        if (start && end) {
+            throw new IllegalStateException("drag start and end cannot happen in same call");
+        }
+        if (start) {
+            if (Float.compare(progress, 1f) == 0) {
+                state = State.DRAG_START_BOTTOM;
+            } else if (Float.compare(progress, 0f) == 0) {
+                state = State.DRAG_START_TOP;
+            }
+        } else if (end) {
+            if (Float.compare(progress, 1f) == 0 && mState == State.DRAG_START_TOP) {
+                state = State.FLING_END_BOTTOM;
+            } else if (Float.compare(progress, 0f) == 0 && mState == State.DRAG_START_BOTTOM) {
+                state = State.FLING_END_TOP;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "setState " + mState + " -> " + state);
+        }
+        mState = state;
     }
 }
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index eb68592..ad3f8df 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -132,6 +132,12 @@
         mLoadingStatePlaceholder.draw(canvas);
     }
 
+    @Override
+    protected void drawDotIfNecessary(Canvas canvas) {
+        // This view (with the text label to the side of the icon) is not designed for a dot to be
+        // drawn on top of it, so never draw one even if a notification for this shortcut exists.
+    }
+
     private void showLoadingState(boolean loading) {
         if (loading == mShowLoadingState) {
             return;
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 9fd53e2..57c0ad9 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -266,7 +266,7 @@
         if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()
                 && (mToState == ALL_APPS || mToState == NORMAL)) {
             mLauncher.getAllAppsController().getInsetController().onDragStart(
-                    mToState == ALL_APPS ? 0 : 1);
+                    mFromState == NORMAL ? 1f : 0f);
         }
     }
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 17f02be..1d7f747 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -42,12 +42,12 @@
 public class LandscapePagedViewHandler implements PagedOrientationHandler {
 
     @Override
-    public int getPrimaryValue(int x, int y) {
+    public <T> T getPrimaryValue(T x, T y) {
         return y;
     }
 
     @Override
-    public int getSecondaryValue(int x, int y) {
+    public <T> T getSecondaryValue(T x, T y) {
         return x;
     }
 
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 114b75a..a9c50cd 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -84,8 +84,8 @@
     boolean getRecentsRtlSetting(Resources resources);
     float getDegreesRotated();
     int getRotation();
-    int getPrimaryValue(int x, int y);
-    int getSecondaryValue(int x, int y);
+    <T> T getPrimaryValue(T x, T y);
+    <T> T getSecondaryValue(T x, T y);
     void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);
     /** Uses {@params pagedView}.getScroll[X|Y]() method for the secondary amount*/
     void delegateScrollTo(PagedView pagedView, int primaryScroll);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 5f5b2d1..587e35a 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -40,12 +40,12 @@
 public class PortraitPagedViewHandler implements PagedOrientationHandler {
 
     @Override
-    public int getPrimaryValue(int x, int y) {
+    public <T> T getPrimaryValue(T x, T y) {
         return x;
     }
 
     @Override
-    public int getSecondaryValue(int x, int y) {
+    public <T> T getSecondaryValue(T x, T y) {
         return y;
     }
 
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 30c9ff9..ee64e98 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -113,14 +113,26 @@
 
     public static final class CommitParams {
 
-        final Uri mUri = LauncherSettings.Favorites.CONTENT_URI;
-        String mWhere;
-        String[] mSelectionArgs;
+        final Uri mUri;
+        final String mWhere;
+        final String[] mSelectionArgs;
 
         public CommitParams(String where, String[] selectionArgs) {
+            this(LauncherSettings.Favorites.CONTENT_URI, where, selectionArgs);
+        }
+
+        private CommitParams(Uri uri, String where, String[] selectionArgs) {
+            mUri = uri;
             mWhere = where;
             mSelectionArgs = selectionArgs;
         }
 
+        /**
+         * Creates commit params for backup table.
+         */
+        public static CommitParams backupCommitParams(String where, String[] selectionArgs) {
+            return new CommitParams(
+                    LauncherSettings.Favorites.BACKUP_CONTENT_URI, where, selectionArgs);
+        }
     }
 }
diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java
index 30f686c..f0884f8 100644
--- a/src/com/android/launcher3/views/SearchSettingsRowView.java
+++ b/src/com/android/launcher3/views/SearchSettingsRowView.java
@@ -81,7 +81,6 @@
         Bundle bundle = searchTarget.getExtras();
         mIntent = bundle.getParcelable("intent");
         showIfAvailable(mTitleView, bundle.getString("title"));
-        showIfAvailable(mDescriptionView, bundle.getString("description"));
         ArrayList<String> breadcrumbs = bundle.getStringArrayList("breadcrumbs");
         //TODO: implement RTL friendly breadcrumbs view
         showIfAvailable(mBreadcrumbsView, breadcrumbs != null