Merge "Revert "Removing AM.getTasks call on UI thread during touch down"" into tm-dev
diff --git a/Android.bp b/Android.bp
index a523a62..b3027bc 100644
--- a/Android.bp
+++ b/Android.bp
@@ -107,7 +107,6 @@
         "androidx.cardview_cardview",
         "com.google.android.material_material",
         "iconloader_base",
-        "modules-utils-build",
     ],
     manifest: "AndroidManifest-common.xml",
     sdk_version: "current",
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 36c9d13..5b912ad 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -244,8 +244,7 @@
         mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
         mHandler = new Handler(Looper.getMainLooper());
         mDeviceProfile = mLauncher.getDeviceProfile();
-        mBackAnimationController = new LauncherBackAnimationController(
-                mDeviceProfile, mLauncher, this);
+        mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
 
         Resources res = mLauncher.getResources();
         mContentScale = res.getFloat(R.dimen.content_scale);
@@ -1441,6 +1440,10 @@
                 }
             };
             anim.addOnUpdateListener(runner);
+        } else {
+            // If no floating icon or widget is present, animate the to the default window
+            // target rect.
+            anim.addOnUpdateListener(new SpringAnimRunner(targets, targetRect, windowTargetBounds));
         }
 
         // Use a fixed velocity to start the animation.
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 3b02599..eda0823 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -53,6 +53,7 @@
 public class DepthController implements StateHandler<LauncherState>,
         BaseActivity.MultiWindowModeChangedListener {
 
+    private static final boolean OVERLAY_SCROLL_ENABLED = false;
     public static final FloatProperty<DepthController> DEPTH =
             new FloatProperty<DepthController>("depth") {
                 @Override
@@ -294,6 +295,9 @@
     }
 
     public void onOverlayScrollChanged(float progress) {
+        if (!OVERLAY_SCROLL_ENABLED) {
+            return;
+        }
         // Add some padding to the progress, such we don't change the depth on the last frames of
         // the animation. It's possible that a user flinging the feed quickly would scroll
         // horizontally by accident, causing the device to enter client composition unnecessarily.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index a22398d..89e54b8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -156,14 +156,7 @@
         updateIconSize(resources);
         mTaskbarHeightForIme = resources.getDimensionPixelSize(R.dimen.taskbar_ime_size);
 
-        // Inflate views.
-        mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
-                R.layout.taskbar, null, false);
-        TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
-        TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
-        FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
-        StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
-
+        // Get display and corners first, as views might use them in constructor.
         Display display = windowContext.getDisplay();
         Context c = display.getDisplayId() == Display.DEFAULT_DISPLAY
                 ? windowContext.getApplicationContext()
@@ -172,6 +165,14 @@
         mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
         mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
 
+        // Inflate views.
+        mDragLayer = (TaskbarDragLayer) mLayoutInflater.inflate(
+                R.layout.taskbar, null, false);
+        TaskbarView taskbarView = mDragLayer.findViewById(R.id.taskbar_view);
+        TaskbarScrimView taskbarScrimView = mDragLayer.findViewById(R.id.taskbar_scrim);
+        FrameLayout navButtonsView = mDragLayer.findViewById(R.id.navbuttons_view);
+        StashedHandleView stashedHandleView = mDragLayer.findViewById(R.id.stashed_handle);
+
         mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
 
         // Construct controllers.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
new file mode 100644
index 0000000..1177bdb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2022 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.taskbar
+
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Path
+import com.android.launcher3.R
+
+/**
+ * Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners.
+ */
+class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
+
+    val paint: Paint = Paint()
+    var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
+
+    private val leftCornerRadius = context.leftCornerRadius.toFloat()
+    private val rightCornerRadius = context.rightCornerRadius.toFloat()
+    private val invertedLeftCornerPath: Path = Path()
+    private val invertedRightCornerPath: Path = Path()
+
+    init {
+        paint.color = context.getColor(R.color.taskbar_background)
+        paint.flags = Paint.ANTI_ALIAS_FLAG
+        paint.style = Paint.Style.FILL
+
+        // Create the paths for the inverted rounded corners above the taskbar. Start with a filled
+        // square, and then subtract out a circle from the appropriate corner.
+        val square = Path()
+        square.addRect(0f, 0f, leftCornerRadius, leftCornerRadius, Path.Direction.CW)
+        val circle = Path()
+        circle.addCircle(leftCornerRadius, 0f, leftCornerRadius, Path.Direction.CW)
+        invertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE)
+        square.reset()
+        square.addRect(0f, 0f, rightCornerRadius, rightCornerRadius, Path.Direction.CW)
+        circle.reset()
+        circle.addCircle(0f, 0f, rightCornerRadius, Path.Direction.CW)
+        invertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE)
+    }
+
+    /**
+     * Draws the background with the given paint and height, on the provided canvas.
+     */
+    fun draw(canvas: Canvas) {
+        canvas.save()
+        canvas.translate(0f, canvas.height - backgroundHeight)
+
+        // Draw the background behind taskbar content.
+        canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
+
+        // Draw the inverted rounded corners above the taskbar.
+        canvas.translate(0f, -leftCornerRadius)
+        canvas.drawPath(invertedLeftCornerPath, paint)
+        canvas.translate(0f, leftCornerRadius)
+        canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
+        canvas.drawPath(invertedRightCornerPath, paint)
+
+        canvas.restore()
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 9ba4a65..5c10565 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -477,7 +477,6 @@
                 tx.setScale(dragSurface, scale, scale);
                 tx.setAlpha(dragSurface, alpha);
                 tx.apply();
-                tx.close();
             }
         });
         mReturnAnimator.addListener(new AnimatorListenerAdapter() {
@@ -498,6 +497,7 @@
             }
 
             private void cleanUpSurface() {
+                tx.close();
                 maybeOnDragEnd();
                 // Synchronize removing the drag surface with the next draw after calling
                 // maybeOnDragEnd()
@@ -508,7 +508,6 @@
                 syncer.addToSync(syncId, viewRoot.getView());
                 syncer.addTransactionToSync(syncId, transaction);
                 syncer.markSyncReady(syncId);
-
                 mReturnAnimator = null;
             }
         });
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 4a80665..089c26d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -20,8 +20,6 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -31,7 +29,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.R;
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.views.BaseDragLayer;
@@ -44,13 +41,11 @@
  */
 public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
 
-    private final Paint mTaskbarBackgroundPaint;
-    private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
+    private final TaskbarBackgroundRenderer mBackgroundRenderer;
     private final OnComputeInsetsListener mTaskbarInsetsComputer = this::onComputeTaskbarInsets;
 
     // Initialized in init.
     private TaskbarDragLayerController.TaskbarDragLayerCallbacks mControllerCallbacks;
-    private float mLeftCornerRadius, mRightCornerRadius;
 
     private float mTaskbarBackgroundOffset;
 
@@ -70,35 +65,13 @@
     public TaskbarDragLayer(@NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, 1 /* alphaChannelCount */);
-        mTaskbarBackgroundPaint = new Paint();
-        mTaskbarBackgroundPaint.setColor(getResources().getColor(R.color.taskbar_background));
-        mTaskbarBackgroundPaint.setAlpha(0);
-        mTaskbarBackgroundPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-        mTaskbarBackgroundPaint.setStyle(Paint.Style.FILL);
-
-        // Will be set in init(), but this ensures they are always non-null.
-        mInvertedLeftCornerPath = new Path();
-        mInvertedRightCornerPath = new Path();
+        mBackgroundRenderer = new TaskbarBackgroundRenderer(mActivity);
+        mBackgroundRenderer.getPaint().setAlpha(0);
     }
 
     public void init(TaskbarDragLayerController.TaskbarDragLayerCallbacks callbacks) {
         mControllerCallbacks = callbacks;
 
-        // Create the paths for the inverted rounded corners above the taskbar. Start with a filled
-        // square, and then subtracting out a circle from the appropriate corner.
-        mLeftCornerRadius = mActivity.getLeftCornerRadius();
-        mRightCornerRadius = mActivity.getRightCornerRadius();
-        Path square = new Path();
-        square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
-        Path circle = new Path();
-        circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
-        mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
-        square.reset();
-        square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
-        circle.reset();
-        circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
-        mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
-
         recreateControllers();
     }
 
@@ -151,20 +124,8 @@
     protected void dispatchDraw(Canvas canvas) {
         float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
                 * (1f - mTaskbarBackgroundOffset);
-        canvas.save();
-        canvas.translate(0, canvas.getHeight() - backgroundHeight);
-
-        // Draw the background behind taskbar content.
-        canvas.drawRect(0, 0, canvas.getWidth(), backgroundHeight, mTaskbarBackgroundPaint);
-
-        // Draw the inverted rounded corners above the taskbar.
-        canvas.translate(0, -mLeftCornerRadius);
-        canvas.drawPath(mInvertedLeftCornerPath, mTaskbarBackgroundPaint);
-        canvas.translate(0, mLeftCornerRadius);
-        canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
-        canvas.drawPath(mInvertedRightCornerPath, mTaskbarBackgroundPaint);
-
-        canvas.restore();
+        mBackgroundRenderer.setBackgroundHeight(backgroundHeight);
+        mBackgroundRenderer.draw(canvas);
         super.dispatchDraw(canvas);
     }
 
@@ -173,7 +134,7 @@
      * @param alpha 0 is fully transparent, 1 is fully opaque.
      */
     protected void setTaskbarBackgroundAlpha(float alpha) {
-        mTaskbarBackgroundPaint.setAlpha((int) (alpha * 255));
+        mBackgroundRenderer.getPaint().setAlpha((int) (alpha * 255));
         invalidate();
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
index 94a3307..1d3757f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -17,22 +17,19 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.launcher3.views.ActivityContext;
+
 /**
  * View that handles scrimming the taskbar and the inverted corners it draws. The scrim is used
  * when bubbles is expanded.
  */
 public class TaskbarScrimView extends View {
-    private final Paint mTaskbarScrimPaint;
-    private final Path mInvertedLeftCornerPath, mInvertedRightCornerPath;
+    private final TaskbarBackgroundRenderer mRenderer;
 
     private boolean mShowScrim;
-    private float mLeftCornerRadius, mRightCornerRadius;
-    private float mBackgroundHeight;
 
     public TaskbarScrimView(Context context) {
         this(context, null);
@@ -49,14 +46,9 @@
     public TaskbarScrimView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-
-        mTaskbarScrimPaint = new Paint();
-        mTaskbarScrimPaint.setColor(getResources().getColor(android.R.color.system_neutral1_1000));
-        mTaskbarScrimPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-        mTaskbarScrimPaint.setStyle(Paint.Style.FILL);
-
-        mInvertedLeftCornerPath = new Path();
-        mInvertedRightCornerPath = new Path();
+        mRenderer = new TaskbarBackgroundRenderer(ActivityContext.lookupContext(context));
+        mRenderer.getPaint().setColor(getResources().getColor(
+                android.R.color.system_neutral1_1000));
     }
 
     @Override
@@ -64,31 +56,7 @@
         super.onDraw(canvas);
 
         if (mShowScrim) {
-            canvas.save();
-            canvas.translate(0, canvas.getHeight() - mBackgroundHeight);
-
-            // Scrim the taskbar itself.
-            canvas.drawRect(0, 0, canvas.getWidth(), mBackgroundHeight, mTaskbarScrimPaint);
-
-            // Scrim the inverted rounded corners above the taskbar.
-            canvas.translate(0, -mLeftCornerRadius);
-            canvas.drawPath(mInvertedLeftCornerPath, mTaskbarScrimPaint);
-            canvas.translate(0, mLeftCornerRadius);
-            canvas.translate(canvas.getWidth() - mRightCornerRadius, -mRightCornerRadius);
-            canvas.drawPath(mInvertedRightCornerPath, mTaskbarScrimPaint);
-
-            canvas.restore();
-        }
-    }
-
-    /**
-     * Sets the height of the taskbar background.
-     * @param height the height of the background.
-     */
-    protected void setBackgroundHeight(float height) {
-        mBackgroundHeight = height;
-        if (mShowScrim) {
-            invalidate();
+            mRenderer.draw(canvas);
         }
     }
 
@@ -98,32 +66,7 @@
      */
     protected void setScrimAlpha(float alpha) {
         mShowScrim = alpha > 0f;
-        mTaskbarScrimPaint.setAlpha((int) (alpha * 255));
+        mRenderer.getPaint().setAlpha((int) (alpha * 255));
         invalidate();
     }
-
-    /**
-     * Sets the radius of the left and right corners above the taskbar.
-     * @param leftCornerRadius the radius of the left corner.
-     * @param rightCornerRadius the radius of the right corner.
-     */
-    protected void setCornerSizes(float leftCornerRadius, float rightCornerRadius) {
-        mLeftCornerRadius = leftCornerRadius;
-        mRightCornerRadius = rightCornerRadius;
-
-        Path square = new Path();
-        square.addRect(0, 0, mLeftCornerRadius, mLeftCornerRadius, Path.Direction.CW);
-        Path circle = new Path();
-        circle.addCircle(mLeftCornerRadius, 0, mLeftCornerRadius, Path.Direction.CW);
-        mInvertedLeftCornerPath.op(square, circle, Path.Op.DIFFERENCE);
-        square.reset();
-        square.addRect(0, 0, mRightCornerRadius, mRightCornerRadius, Path.Direction.CW);
-        circle.reset();
-        circle.addCircle(0, 0, mRightCornerRadius, Path.Direction.CW);
-        mInvertedRightCornerPath.op(square, circle, Path.Op.DIFFERENCE);
-
-        if (mShowScrim) {
-            invalidate();
-        }
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 02bbae4..58ace17 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -49,9 +49,6 @@
     public TaskbarScrimViewController(TaskbarActivityContext activity, TaskbarScrimView scrimView) {
         mActivity = activity;
         mScrimView = scrimView;
-        mScrimView.setCornerSizes(mActivity.getLeftCornerRadius(),
-                mActivity.getRightCornerRadius());
-        mScrimView.setBackgroundHeight(mActivity.getDeviceProfile().taskbarSize);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 7abcbdb..cc79f4a 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -25,6 +25,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.graphics.Matrix;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Handler;
@@ -33,11 +34,12 @@
 import android.util.Pair;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.window.BackEvent;
 import android.window.IOnBackInvokedCallback;
 
 import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -62,34 +64,36 @@
  *
  */
 public class LauncherBackAnimationController {
-    private static final int CANCEL_TRANSITION_DURATION = 150;
+    private static final int CANCEL_TRANSITION_DURATION = 233;
+    private static final float MIN_WINDOW_SCALE = 0.7f;
     private static final String TAG = "LauncherBackAnimationController";
-    private final DeviceProfile mDeviceProfile;
     private final QuickstepTransitionManager mQuickstepTransitionManager;
     private final Matrix mTransformMatrix = new Matrix();
-    private final RectF mTargetRectF = new RectF();
-    private final RectF mStartRectF = new RectF();
+    /** The window position at the beginning of the back animation. */
+    private final Rect mStartRect = new Rect();
+    /** The window position when the back gesture is cancelled. */
+    private final RectF mCancelRect = new RectF();
+    /** The current window position. */
     private final RectF mCurrentRect = new RectF();
     private final BaseQuickstepLauncher mLauncher;
     private final int mWindowScaleMarginX;
-    private final int mWindowScaleMarginY;
+    /** Max window translation in the Y axis. */
+    private final int mWindowMaxDeltaY;
     private final float mWindowScaleEndCornerRadius;
     private final float mWindowScaleStartCornerRadius;
+    private final Interpolator mCancelInterpolator;
+    private final PointF mInitialTouchPos = new PointF();
 
     private RemoteAnimationTargetCompat mBackTarget;
     private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
     private boolean mSpringAnimationInProgress = false;
     private boolean mAnimatorSetInProgress = false;
-    @BackEvent.SwipeEdge
-    private int mSwipeEdge;
     private float mBackProgress = 0;
     private boolean mBackInProgress = false;
 
     public LauncherBackAnimationController(
-            DeviceProfile deviceProfile,
             BaseQuickstepLauncher launcher,
             QuickstepTransitionManager quickstepTransitionManager) {
-        mDeviceProfile = deviceProfile;
         mLauncher = launcher;
         mQuickstepTransitionManager = quickstepTransitionManager;
         mWindowScaleEndCornerRadius = QuickStepContract.supportsRoundedCornersOnWindows(
@@ -100,8 +104,10 @@
         mWindowScaleStartCornerRadius = QuickStepContract.getWindowCornerRadius(mLauncher);
         mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
                 R.dimen.swipe_back_window_scale_x_margin);
-        mWindowScaleMarginY = mLauncher.getResources().getDimensionPixelSize(
-                R.dimen.swipe_back_window_scale_y_margin);
+        mWindowMaxDeltaY = mLauncher.getResources().getDimensionPixelSize(
+                R.dimen.swipe_back_window_max_delta_y);
+        mCancelInterpolator =
+                AnimationUtils.loadInterpolator(mLauncher, R.interpolator.back_cancel);
     }
 
     /**
@@ -136,7 +142,7 @@
                         if (!mBackInProgress) {
                             startBack(backEvent);
                         } else {
-                            updateBackProgress(mBackProgress);
+                            updateBackProgress(mBackProgress, backEvent);
                         }
                     }
 
@@ -145,11 +151,13 @@
     }
 
     private void resetPositionAnimated() {
-        ValueAnimator cancelAnimator = ValueAnimator.ofFloat(mBackProgress, 0);
+        ValueAnimator cancelAnimator = ValueAnimator.ofFloat(0, 1);
+        mCancelRect.set(mCurrentRect);
         cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
+        cancelAnimator.setInterpolator(mCancelInterpolator);
         cancelAnimator.addUpdateListener(
                 animation -> {
-                    updateBackProgress((float) animation.getAnimatedValue());
+                    updateCancelProgress((float) animation.getAnimatedValue());
                 });
         cancelAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -179,50 +187,70 @@
         mTransaction.show(appTarget.leash).apply();
         mTransaction.setAnimationTransaction();
         mBackTarget = new RemoteAnimationTargetCompat(appTarget);
-        mSwipeEdge = backEvent.getSwipeEdge();
-        float screenWidth = mDeviceProfile.widthPx;
-        float screenHeight = mDeviceProfile.heightPx;
-        float targetHeight = screenHeight - 2 * mWindowScaleMarginY;
-        float targetWidth = targetHeight * screenWidth / screenHeight;
-        float left;
-        if (mSwipeEdge == BackEvent.EDGE_LEFT) {
-            left = screenWidth - targetWidth - mWindowScaleMarginX;
-        } else {
-            left = mWindowScaleMarginX;
-        }
-        float top = mWindowScaleMarginY;
+        mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+
         // TODO(b/218916755): Offset start rectangle in multiwindow mode.
-        mStartRectF.set(0, 0, screenWidth, screenHeight);
-        mTargetRectF.set(left, top, targetWidth + left, targetHeight + top);
+        mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
     }
 
-    private void updateBackProgress(float progress) {
+    private void updateBackProgress(float progress, BackEvent event) {
         if (mBackTarget == null) {
             return;
         }
+        float screenWidth = mStartRect.width();
+        float screenHeight = mStartRect.height();
+        float dX = Math.abs(event.getTouchX() - mInitialTouchPos.x);
+        // The 'follow width' is the width of the window if it completely matches
+        // the gesture displacement.
+        float followWidth = screenWidth - dX;
+        // The 'progress width' is the width of the window if it strictly linearly interpolates
+        // to minimum scale base on progress.
+        float progressWidth = MathUtils.lerp(1, MIN_WINDOW_SCALE, progress) * screenWidth;
+        // The final width is derived from interpolating between the follow with and progress width
+        // using gesture progress.
+        float width = MathUtils.lerp(followWidth, progressWidth, progress);
+        float height = screenHeight / screenWidth * width;
+        float deltaYRatio = (event.getTouchY() - mInitialTouchPos.y) / screenHeight;
+        // Base the window movement in the Y axis on the touch movement in the Y axis.
+        float deltaY = (float) Math.sin(deltaYRatio * Math.PI * 0.5f) * mWindowMaxDeltaY;
+        // Move the window along the Y axis.
+        float top = (screenHeight - height) * 0.5f + deltaY;
+        // Move the window along the X axis.
+        float left = event.getSwipeEdge() == BackEvent.EDGE_RIGHT
+                ? progress * mWindowScaleMarginX
+                : screenWidth - progress * mWindowScaleMarginX - width;
 
-        mCurrentRect.set(
-                MathUtils.lerp(mStartRectF.left, mTargetRectF.left, progress),
-                MathUtils.lerp(mStartRectF.top, mTargetRectF.top, progress),
-                MathUtils.lerp(mStartRectF.right, mTargetRectF.right, progress),
-                MathUtils.lerp(mStartRectF.bottom, mTargetRectF.bottom, progress));
-        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
-                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
-
-        Rect currentRect = new Rect();
-        mCurrentRect.round(currentRect);
-
-        // Scale the target window to match the currentRectF.
-        final float scale = mCurrentRect.width() / mStartRectF.width();
-        mTransformMatrix.reset();
-        mTransformMatrix.setScale(scale, scale);
-        mTransformMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top);
-        Rect startRect = new Rect();
-        mStartRectF.round(startRect);
+        mCurrentRect.set(left, top, left + width, top + height);
         float cornerRadius = Utilities.mapRange(
                 progress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
+        applyTransform(mCurrentRect, cornerRadius);
+    }
+
+    private void updateCancelProgress(float progress) {
+        if (mBackTarget == null) {
+            return;
+        }
+        mCurrentRect.set(
+                MathUtils.lerp(mCancelRect.left, mStartRect.left, progress),
+                MathUtils.lerp(mCancelRect.top, mStartRect.top, progress),
+                MathUtils.lerp(mCancelRect.right, mStartRect.right, progress),
+                MathUtils.lerp(mCancelRect.bottom, mStartRect.bottom, progress));
+
+        float cornerRadius = Utilities.mapRange(
+                progress, mWindowScaleEndCornerRadius, mWindowScaleStartCornerRadius);
+        applyTransform(mCurrentRect, cornerRadius);
+    }
+
+    /** Transform the target window to match the target rect. */
+    private void applyTransform(RectF targetRect, float cornerRadius) {
+        SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder builder =
+                new SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder(mBackTarget.leash);
+        final float scale = targetRect.width() / mStartRect.width();
+        mTransformMatrix.reset();
+        mTransformMatrix.setScale(scale, scale);
+        mTransformMatrix.postTranslate(targetRect.left, targetRect.top);
         builder.withMatrix(mTransformMatrix)
-                .withWindowCrop(startRect)
+                .withWindowCrop(mStartRect)
                 .withCornerRadius(cornerRadius);
         SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = builder.build();
 
@@ -263,11 +291,11 @@
         mBackTarget = null;
         mBackInProgress = false;
         mBackProgress = 0;
-        mSwipeEdge = BackEvent.EDGE_LEFT;
         mTransformMatrix.reset();
-        mTargetRectF.setEmpty();
+        mCancelRect.setEmpty();
         mCurrentRect.setEmpty();
-        mStartRectF.setEmpty();
+        mStartRect.setEmpty();
+        mInitialTouchPos.set(0, 0);
         mAnimatorSetInProgress = false;
         mSpringAnimationInProgress = false;
         SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ed00b8e..e5cbbee 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -106,6 +106,7 @@
 import android.widget.ListView;
 import android.widget.OverScroller;
 import android.widget.Toast;
+import android.window.PictureInPictureSurfaceTransaction;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -161,6 +162,7 @@
 import com.android.quickstep.TaskViewUtils;
 import com.android.quickstep.ViewUtils;
 import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.LauncherSplitScreenListener;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RecentsOrientedState;
 import com.android.quickstep.util.SplitScreenBounds;
@@ -4507,6 +4509,18 @@
             final SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(getContext());
             systemUiProxy.notifySwipeToHomeFinished();
             systemUiProxy.setShelfHeight(true, mActivity.getDeviceProfile().hotseatBarSizePx);
+            // Transaction to hide the task to avoid flicker for entering PiP from split-screen.
+            // See also {@link AbsSwipeUpHandler#maybeFinishSwipeToHome}.
+            PictureInPictureSurfaceTransaction tx =
+                    new PictureInPictureSurfaceTransaction.Builder()
+                            .setAlpha(0f)
+                            .build();
+            int[] taskIds =
+                    LauncherSplitScreenListener.INSTANCE.getNoCreate().getRunningSplitTaskIds();
+            for (int taskId : taskIds) {
+                mRecentsAnimationController.setFinishTaskTransaction(taskId,
+                        tx, null /* overlay */);
+            }
         }
         mRecentsAnimationController.finish(toRecents, () -> {
             if (onFinishComplete != null) {
diff --git a/res/interpolator/back_cancel.xml b/res/interpolator/back_cancel.xml
new file mode 100644
index 0000000..2165457
--- /dev/null
+++ b/res/interpolator/back_cancel.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.2"
+    android:controlY1="0"
+    android:controlX2="0"
+    android:controlY2="1"/>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 0ed2d85..5ecd929 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -166,6 +166,6 @@
 
     <!-- Swipe back to home related -->
     <dimen name="swipe_back_window_scale_x_margin">10dp</dimen>
-    <dimen name="swipe_back_window_scale_y_margin">80dp</dimen>
+    <dimen name="swipe_back_window_max_delta_y">160dp</dimen>
     <dimen name="swipe_back_window_corner_radius">40dp</dimen>
 </resources>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 6d39857..8358f2a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -53,6 +53,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.Message;
@@ -72,6 +73,7 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import androidx.annotation.ChecksSdkIntAtLeast;
 import androidx.annotation.NonNull;
 import androidx.core.graphics.ColorUtils;
 
@@ -93,7 +95,6 @@
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
-import com.android.modules.utils.build.SdkLevel;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -122,15 +123,20 @@
     public static final String[] EMPTY_STRING_ARRAY = new String[0];
     public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
 
+    @ChecksSdkIntAtLeast(api = VERSION_CODES.P)
     public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
 
+    @ChecksSdkIntAtLeast(api = VERSION_CODES.Q)
     public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
 
-    public static final boolean ATLEAST_R = SdkLevel.isAtLeastR();
+    @ChecksSdkIntAtLeast(api = VERSION_CODES.R)
+    public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
 
-    public static final boolean ATLEAST_S = SdkLevel.isAtLeastS();
+    @ChecksSdkIntAtLeast(api = VERSION_CODES.S)
+    public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
 
-    public static final boolean ATLEAST_T = SdkLevel.isAtLeastT();
+    @ChecksSdkIntAtLeast(api = VERSION_CODES.TIRAMISU, codename = "T")
+    public static final boolean ATLEAST_T = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
 
     /**
      * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index bcb0d14..6203cea 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -27,6 +27,7 @@
 import android.os.UserManager;
 import android.util.Log;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -147,6 +148,9 @@
             workFabMarginBottom +=
                     mWorkModeSwitch.getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
         }
+        if (!mAllApps.mActivityContext.getDeviceProfile().isGestureMode){
+            workFabMarginBottom += mAllApps.mActivityContext.getDeviceProfile().getInsets().bottom;
+        }
         ((ViewGroup.MarginLayoutParams) mWorkModeSwitch.getLayoutParams()).bottomMargin =
                 workFabMarginBottom;
         if (mWorkModeSwitch.getParent() != mAllApps) {
@@ -158,7 +162,6 @@
         mWorkModeSwitch.updateCurrentState(mCurrentState == STATE_ENABLED);
         return true;
     }
-
     /**
      * Removes work profile toggle button from {@link BaseAllAppsContainerView}
      */
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 74d9a22..6f295e6 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -20,9 +20,13 @@
 
 import android.annotation.TargetApi;
 import android.graphics.Bitmap;
-import android.graphics.Matrix;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.Path.Direction;
+import android.graphics.Picture;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
@@ -31,10 +35,11 @@
 import android.util.Log;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
 
+import com.android.launcher3.Utilities;
 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;
 import com.android.launcher3.views.ActivityContext;
@@ -69,79 +74,104 @@
         return mBadge;
     }
 
+    @TargetApi(Build.VERSION_CODES.P)
     public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
-            ActivityContext activity, int folderId, Point dragViewSize) {
+            ActivityContext activity, int folderId, Point size) {
         Preconditions.assertNonUiThread();
+        if (!Utilities.ATLEAST_P) {
+            return null;
+        }
 
-        // Create the actual drawable on the UI thread to avoid race conditions with
+        // assume square
+        if (size.x != size.y) {
+            return null;
+        }
+        int requestedSize = size.x;
+
+        // Only use the size actually needed for drawing the folder icon
+        int drawingSize = activity.getDeviceProfile().folderIconSizePx;
+        int foregroundSize = Math.max(requestedSize, drawingSize);
+        float shift = foregroundSize - requestedSize;
+
+        Picture background = new Picture();
+        Picture foreground = new Picture();
+        Picture badge = new Picture();
+
+        Canvas bgCanvas = background.beginRecording(requestedSize, requestedSize);
+        Canvas badgeCanvas = badge.beginRecording(requestedSize, requestedSize);
+
+        Canvas fgCanvas = foreground.beginRecording(foregroundSize, foregroundSize);
+        fgCanvas.translate(shift, shift);
+
+        // Do not clip the folder drawing since the icon previews extend outside the background.
+        Path mask = new Path();
+        mask.addRect(-shift, -shift, requestedSize + shift, requestedSize + shift,
+                Direction.CCW);
+
+        // Initialize the actual draw commands on the UI thread to avoid race conditions with
         // FolderIcon draw pass
         try {
-            return MAIN_EXECUTOR.submit(() -> {
+            MAIN_EXECUTOR.submit(() -> {
                 FolderIcon icon = activity.findFolderIcon(folderId);
-                return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize);
-
+                if (icon == null) {
+                    throw new IllegalArgumentException("Folder not found with id: " + folderId);
+                }
+                initLayersOnUiThread(icon, requestedSize, bgCanvas, fgCanvas, badgeCanvas);
             }).get();
         } catch (Exception e) {
             Log.e(TAG, "Unable to create folder icon", e);
             return null;
+        } finally {
+            background.endRecording();
+            foreground.endRecording();
+            badge.endRecording();
         }
+
+        // Only convert foreground to a bitmap as it can contain multiple draw commands. Other
+        // layers either draw a nothing or a single draw call.
+        Bitmap fgBitmap = Bitmap.createBitmap(foreground);
+        Paint foregroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        // Do not use PictureDrawable as it moves the picture to the canvas bounds, whereas we want
+        // to draw it at (0,0)
+        return new FolderAdaptiveIcon(
+                new BitmapRendererDrawable(c -> c.drawPicture(background)),
+                new BitmapRendererDrawable(
+                        c -> c.drawBitmap(fgBitmap, -shift, -shift, foregroundPaint)),
+                new BitmapRendererDrawable(c -> c.drawPicture(badge)),
+                mask);
     }
 
-    private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
-                                                               Point dragViewSize) {
-        Preconditions.assertUIThread();
-
+    @UiThread
+    private static void initLayersOnUiThread(FolderIcon icon, int size,
+            Canvas backgroundCanvas, Canvas foregroundCanvas, Canvas badgeCanvas) {
         icon.getPreviewBounds(sTmpRect);
-
-        PreviewBackground bg = icon.getFolderBackground();
-
-        // assume square
-        assert (dragViewSize.x == dragViewSize.y);
         final int previewSize = sTmpRect.width();
 
-        final int margin = (dragViewSize.x - previewSize) / 2;
+        PreviewBackground bg = icon.getFolderBackground();
+        final int margin = (size - previewSize) / 2;
         final float previewShiftX = -sTmpRect.left + margin;
         final float previewShiftY = -sTmpRect.top + margin;
 
         // Initialize badge, which consists of the outline stroke, shadow and dot; these
         // must be rendered above the foreground
-        Bitmap badgeBmp = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
-                (canvas) -> {
-                    canvas.save();
-                    canvas.translate(previewShiftX, previewShiftY);
-                    bg.drawShadow(canvas);
-                    bg.drawBackgroundStroke(canvas);
-                    icon.drawDot(canvas);
-                    canvas.restore();
-                });
+        badgeCanvas.save();
+        badgeCanvas.translate(previewShiftX, previewShiftY);
+        icon.drawDot(badgeCanvas);
+        badgeCanvas.restore();
 
-        // Initialize mask
-        Path mask = new Path();
-        Matrix m = new Matrix();
-        m.setTranslate(previewShiftX, previewShiftY);
-        bg.getClipPath().transform(m, mask);
+        // Draw foreground
+        foregroundCanvas.save();
+        foregroundCanvas.translate(previewShiftX, previewShiftY);
+        icon.getPreviewItemManager().draw(foregroundCanvas);
+        foregroundCanvas.restore();
 
-        Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
-                (canvas) -> {
-                    canvas.save();
-                    canvas.translate(previewShiftX, previewShiftY);
-                    icon.getPreviewItemManager().draw(canvas);
-                    canvas.restore();
-                });
-
-        Bitmap bgBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
-                (canvas) -> {
-                    Paint p = new Paint();
-                    p.setColor(bg.getBgColor());
-
-                    canvas.drawCircle(dragViewSize.x / 2f, dragViewSize.y / 2f, bg.getRadius(), p);
-                });
-
-        ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0);
-        ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0);
-        ShiftedBitmapDrawable background = new ShiftedBitmapDrawable(bgBitmap, 0, 0);
-
-        return new FolderAdaptiveIcon(background, foreground, badge, mask);
+        // Draw background
+        Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        backgroundPaint.setColor(bg.getBgColor());
+        bg.drawShadow(backgroundCanvas);
+        backgroundCanvas.drawCircle(size / 2f, size / 2f, bg.getRadius(), backgroundPaint);
+        bg.drawBackgroundStroke(backgroundCanvas);
     }
 
     @Override
@@ -174,4 +204,52 @@
                     & mBadge.getChangingConfigurations();
         }
     }
+
+    private static class BitmapRendererDrawable extends Drawable {
+
+        private final BitmapRenderer mRenderer;
+
+        BitmapRendererDrawable(BitmapRenderer renderer) {
+            mRenderer = renderer;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            mRenderer.draw(canvas);
+        }
+
+        @Override
+        public void setAlpha(int i) { }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {  }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+
+        @Override
+        public ConstantState getConstantState() {
+            return new MyConstantState(mRenderer);
+        }
+
+        private static class MyConstantState extends ConstantState {
+            private final BitmapRenderer mRenderer;
+
+            MyConstantState(BitmapRenderer renderer) {
+                mRenderer = renderer;
+            }
+
+            @Override
+            public Drawable newDrawable() {
+                return new BitmapRendererDrawable(mRenderer);
+            }
+
+            @Override
+            public int getChangingConfigurations() {
+                return 0;
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java b/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
deleted file mode 100644
index f8583b8..0000000
--- a/src/com/android/launcher3/graphics/ShiftedBitmapDrawable.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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;
-
-    private final ConstantState mConstantState;
-
-    public ShiftedBitmapDrawable(Bitmap bitmap, float shiftX, float shiftY) {
-        mBitmap = bitmap;
-        mShiftX = shiftX;
-        mShiftY = shiftY;
-
-        mConstantState = new MyConstantState(mBitmap, mShiftX, mShiftY);
-    }
-
-    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;
-    }
-
-    @Override
-    public ConstantState getConstantState() {
-        return mConstantState;
-    }
-
-    private static class MyConstantState extends ConstantState {
-        private final Bitmap mBitmap;
-        private float mShiftX;
-        private float mShiftY;
-
-        MyConstantState(Bitmap bitmap, float shiftX, float shiftY) {
-            mBitmap = bitmap;
-            mShiftX = shiftX;
-            mShiftY = shiftY;
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            return new ShiftedBitmapDrawable(mBitmap, mShiftX, mShiftY);
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return 0;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/model/StringCache.java b/src/com/android/launcher3/model/StringCache.java
index 2fc852d..663a463 100644
--- a/src/com/android/launcher3/model/StringCache.java
+++ b/src/com/android/launcher3/model/StringCache.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.model;
 
+import android.annotation.SuppressLint;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.os.Build;
@@ -215,6 +216,7 @@
                 context, DISABLED_BY_ADMIN_MESSAGE, R.string.msg_disabled_by_admin);
     }
 
+    @SuppressLint("NewApi")
     private String getEnterpriseString(
             Context context, String updatableStringId, int defaultStringId) {
         return Utilities.ATLEAST_T
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index bbeb886..04eb38a 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -35,6 +35,7 @@
 import android.util.Pair;
 
 import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
@@ -154,14 +155,9 @@
             case MSG_NOTIFICATION_FULL_REFRESH:
                 List<StatusBarNotification> activeNotifications = null;
                 if (sIsConnected) {
-                    try {
-                        activeNotifications = Arrays.stream(getActiveNotifications())
-                                .filter(this::notificationIsValidForUI)
-                                .collect(Collectors.toList());
-                    } catch (SecurityException ex) {
-                        Log.e(TAG, "SecurityException: failed to fetch notifications");
-                        activeNotifications = new ArrayList<>();
-                    }
+                    activeNotifications = Arrays.stream(getActiveNotificationsSafely(null))
+                            .filter(this::notificationIsValidForUI)
+                            .collect(Collectors.toList());
                 } else {
                     activeNotifications = new ArrayList<>();
                 }
@@ -175,7 +171,7 @@
             }
             case MSG_RANKING_UPDATE: {
                 String[] keys = ((RankingMap) message.obj).getOrderedKeys();
-                for (StatusBarNotification sbn : getActiveNotifications(keys)) {
+                for (StatusBarNotification sbn : getActiveNotificationsSafely(keys)) {
                     updateGroupKeyIfNecessary(sbn);
                 }
                 return true;
@@ -214,6 +210,16 @@
         return true;
     }
 
+    private @NonNull StatusBarNotification[] getActiveNotificationsSafely(@Nullable String[] keys) {
+        StatusBarNotification[] result = null;
+        try {
+            result = getActiveNotifications(keys);
+        } catch (SecurityException e) {
+            Log.e(TAG, "SecurityException: failed to fetch notifications");
+        }
+        return result == null ? new StatusBarNotification[0] : result;
+    }
+
     @Override
     public void onListenerConnected() {
         super.onListenerConnected();
@@ -313,9 +319,8 @@
      */
     @WorkerThread
     public List<StatusBarNotification> getNotificationsForKeys(List<NotificationKeyData> keys) {
-        StatusBarNotification[] notifications = getActiveNotifications(
-                keys.stream().map(n -> n.notificationKey).toArray(String[]::new));
-        return notifications == null ? Collections.emptyList() : Arrays.asList(notifications);
+        return Arrays.asList(getActiveNotificationsSafely(
+                keys.stream().map(n -> n.notificationKey).toArray(String[]::new)));
     }
 
     /**
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 56a1d37..babe607 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -49,7 +49,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.FastBitmapDrawable;
@@ -413,8 +412,7 @@
     @WorkerThread
     @SuppressWarnings("WrongThread")
     private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
-        if (!(drawable instanceof AdaptiveIconDrawable)
-                || (drawable instanceof FolderAdaptiveIcon)) {
+        if (!(drawable instanceof AdaptiveIconDrawable)) {
             return 0;
         }
         int blurSizeOutline =