Merge "Add fallback for missing remote animation callbacks"
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 1d4ed4c..0cf9b4c 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -167,7 +167,7 @@
     public abstract boolean allowMinimizeSplitScreen();
 
     public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
-        return deviceState.isInDeferredGestureRegion(ev);
+        return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 0b09323..c455dc7 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -99,6 +99,8 @@
 
     default void onMotionEvent(MotionEvent ev) { }
 
+    default void onHoverEvent(MotionEvent ev) { }
+
     default void onKeyEvent(KeyEvent ev) { }
 
     default void onInputEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 719c2ae..38331a9 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -326,7 +326,8 @@
         if (uiController == null) {
             return super.deferStartingActivity(deviceState, ev);
         }
-        return uiController.isEventOverAnyTaskbarItem(ev);
+        return uiController.isEventOverAnyTaskbarItem(ev)
+                || super.deferStartingActivity(deviceState, ev);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 19baaf1..54ff6a0 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -34,6 +34,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -52,6 +53,7 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.graphics.Region;
+import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.Process;
 import android.os.RemoteException;
@@ -592,6 +594,12 @@
         return mRotationTouchHelper;
     }
 
+    /** Returns whether IME is rendering nav buttons, and IME is currently showing. */
+    public boolean isImeRenderingNavButtons() {
+        return InputMethodService.canImeRenderGesturalNavButtons() && mMode == NO_BUTTON
+                && ((mSystemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0);
+    }
+
     public void dump(PrintWriter pw) {
         pw.println("DeviceState:");
         pw.println("  canStartSystemGesture=" + canStartSystemGesture());
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index b5d6fae..6c71da9 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.SparseArray;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.WorkerThread;
@@ -62,7 +63,9 @@
 
     private final Context mContext;
     private final TaskKeyLruCache<TaskCacheEntry> mIconCache;
-    private final BitmapInfo[] mDefaultIcons = new BitmapInfo[1];
+    private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
+    private BitmapInfo mDefaultIconBase = null;
+
     private final IconProvider mIconProvider;
 
     private BaseIconFactory mIconFactory;
@@ -208,12 +211,23 @@
     @WorkerThread
     private Drawable getDefaultIcon(int userId) {
         synchronized (mDefaultIcons) {
-            if (mDefaultIcons[0] == null) {
+            if (mDefaultIconBase == null) {
                 try (BaseIconFactory bif = getIconFactory()) {
-                    mDefaultIcons[0] = bif.makeDefaultIcon();
+                    mDefaultIconBase = bif.makeDefaultIcon();
                 }
             }
-            return mDefaultIcons[0].clone().withUser(UserHandle.of(userId)).newIcon(mContext);
+
+            int index;
+            if ((index = mDefaultIcons.indexOfKey(userId)) >= 0) {
+                return mDefaultIcons.valueAt(index).newIcon(mContext);
+            } else {
+                try (BaseIconFactory li = getIconFactory()) {
+                    BitmapInfo info = mDefaultIconBase.withFlags(
+                            li.getBitmapFlagOp(new IconOptions().setUser(UserHandle.of(userId))));
+                    mDefaultIcons.put(userId, info);
+                    return info.newIcon(mContext);
+                }
+            }
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 0a09e34..db1d7e7 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -36,6 +36,7 @@
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
@@ -168,7 +169,7 @@
                 ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
         final RemoteAnimationTargets targets =
                 new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
-                        inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+                        !ENABLE_SHELL_TRANSITIONS && inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
         final RemoteAnimationTargetCompat navBarTarget = targets.getNavBarRemoteAnimationTarget();
 
         SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
@@ -282,7 +283,8 @@
             topMostSimulators = remoteTargetHandles;
         }
 
-        if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulators.length > 0) {
+        if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulators != null
+                && topMostSimulators.length > 0) {
             out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
 
             RemoteTargetHandle[] simulatorCopies = topMostSimulators;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index b0df286..02ac48e 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -101,6 +101,13 @@
     }
 
     @Override
+    public void onHoverEvent(MotionEvent ev) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivity.dispatchGenericMotionEvent(ev);
+        }
+    }
+
+    @Override
     public void onKeyEvent(KeyEvent ev) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             switch (ev.getKeyCode()) {
diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
index c2101a8..91b53c7 100644
--- a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -71,7 +71,16 @@
 
     private boolean onInputConsumerEvent(InputEvent ev) {
         if (ev instanceof MotionEvent) {
-            onInputConsumerMotionEvent((MotionEvent) ev);
+            MotionEvent event = (MotionEvent) ev;
+            int action = event.getActionMasked();
+            boolean isHoverEvent = action == MotionEvent.ACTION_HOVER_ENTER
+                    || action == MotionEvent.ACTION_HOVER_MOVE
+                    || action == MotionEvent.ACTION_HOVER_EXIT;
+            if (isHoverEvent) {
+                onInputConsumerHoverEvent(event);
+            } else {
+                onInputConsumerMotionEvent(event);
+            }
         } else if (ev instanceof KeyEvent) {
             initInputConsumerIfNeeded();
             mInputConsumer.onKeyEvent((KeyEvent) ev);
@@ -113,6 +122,15 @@
         return true;
     }
 
+    private void onInputConsumerHoverEvent(MotionEvent ev) {
+        initInputConsumerIfNeeded();
+        if (mInputConsumer != null) {
+            SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev,
+                    mRotationSupplier.get());
+            mInputConsumer.onHoverEvent(ev);
+        }
+    }
+
     public void destroy() {
         if (mTouchInProgress) {
             mDestroyPending = true;
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 0294828..fcc6272 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,6 +16,8 @@
 
 package com.android.quickstep.views;
 
+import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -33,6 +35,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.SysUINavigationMode;
@@ -152,7 +155,7 @@
     public void setInsets(Rect insets) {
         mInsets.set(insets);
         updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
-        updateHorizontalPadding();
+        updatePaddingAndTranslations();
     }
 
     public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) {
@@ -195,8 +198,37 @@
         return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
     }
 
-    private void updateHorizontalPadding() {
-        setPadding(mInsets.left, 0, mInsets.right, 0);
+    /**
+     * Aligns OverviewActionsView vertically with and offsets horizontal position based on
+     * 3 button nav container in taskbar.
+     */
+    private void updatePaddingAndTranslations() {
+        boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent &&
+                SysUINavigationMode.getMode(getContext()) == THREE_BUTTONS;
+        if (alignFor3ButtonTaskbar) {
+            // Add extra horizontal spacing
+            int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext());
+            if (isLayoutRtl()) {
+                setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0);
+            } else {
+                setPadding(mInsets.left, 0, mInsets.right + additionalPadding, 0);
+            }
+
+            // Align vertically, using taskbar height + mDp.taskbarOffsetY() to guestimate
+            // where the button nav top is
+            View startActionView = findViewById(R.id.action_screenshot);
+            int marginBottom = getOverviewActionsBottomMarginPx(
+                    SysUINavigationMode.getMode(getContext()), mDp);
+            int actionsTop = (mDp.heightPx - marginBottom - mInsets.bottom);
+            int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY());
+            int transY = navTop - actionsTop
+                    + ((mDp.taskbarSize - startActionView.getHeight()) / 2);
+            setTranslationY(transY);
+        } else {
+            setPadding(mInsets.left, 0, mInsets.right, 0);
+            setTranslationX(0);
+            setTranslationY(0);
+        }
     }
 
     /** Updates vertical margins for different navigation mode or configuration changes. */
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 02261af..13a2bda 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -4139,8 +4139,10 @@
             anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex),
                     mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation));
             int runningTaskIndex = recentsView.getRunningTaskIndex();
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get() && runningTaskIndex != -1
-                    && runningTaskIndex != taskIndex) {
+            if (ENABLE_QUICKSTEP_LIVE_TILE.get()
+                    && runningTaskIndex != -1
+                    && runningTaskIndex != taskIndex
+                    && recentsView.getRemoteTargetHandles() != null) {
                 for (RemoteTargetHandle remoteHandle : recentsView.getRemoteTargetHandles()) {
                     anim.play(ObjectAnimator.ofFloat(
                             remoteHandle.getTaskViewSimulator().taskPrimaryTranslation,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index bed4fa9..b1672e3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2366,6 +2366,9 @@
                 CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
                 if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
                     View v = cl.getChildAt(item.cellX, item.cellY);
+                    if (v == null) {
+                        Log.e(TAG, "bindItems failed when removing colliding item=" + item);
+                    }
                     Object tag = v.getTag();
                     String desc = "Collision while binding workspace item: " + item
                             + ". Collides with " + tag;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 83fb3d1..d47edff 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -112,7 +112,7 @@
                     activitiesLists.put(
                             packages[i], appsList.addPackage(context, packages[i], mUser));
                 }
-                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             }
             case OP_UPDATE:
@@ -134,7 +134,7 @@
                     }
                 }
                 // Since package was just updated, the target must be available now.
-                flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_REMOVE: {
                 for (int i = 0; i < N; i++) {
@@ -148,13 +148,12 @@
                     if (DEBUG) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
                     appsList.removePackage(packages[i], mUser);
                 }
-                flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                flagOp = FlagOp.NO_OP.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
             case OP_SUSPEND:
             case OP_UNSUSPEND:
-                flagOp = mOp == OP_SUSPEND ?
-                        FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED) :
-                        FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED);
+                flagOp = FlagOp.NO_OP.setFlag(
+                        WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED, mOp == OP_SUSPEND);
                 if (DEBUG) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
                 appsList.updateDisabledFlags(matcher, flagOp);
                 break;
@@ -162,9 +161,8 @@
                 UserManagerState ums = new UserManagerState();
                 ums.init(UserCache.INSTANCE.get(context),
                         context.getSystemService(UserManager.class));
-                flagOp = ums.isUserQuiet(mUser)
-                        ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
-                        : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
+                flagOp = FlagOp.NO_OP.setFlag(
+                        WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER, ums.isUserQuiet(mUser));
                 appsList.updateDisabledFlags(matcher, flagOp);
 
                 // We are not synchronizing here, as int operations are atomic
diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java
deleted file mode 100644
index bd40eb9..0000000
--- a/src/com/android/launcher3/util/FlagOp.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.android.launcher3.util;
-
-public interface FlagOp {
-
-    FlagOp NO_OP = i -> i;
-
-    int apply(int flags);
-
-    static FlagOp addFlag(int flag) {
-        return i -> i | flag;
-    }
-
-    static FlagOp removeFlag(int flag) {
-        return i -> i & ~flag;
-    }
-}