Merge "Add spring animation when stashing transient taskbar." into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index 48352a2..331184a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -65,4 +65,10 @@
 
     /** Callback invoked when a popup is shown or closed within this context. */
     public abstract void onPopupVisibilityChanged(boolean isVisible);
+
+    /**
+     * Callback invoked when user attempts to split the screen through a long-press menu in Taskbar
+     * or AllApps.
+     */
+    public abstract void onSplitScreenMenuButtonClicked();
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index a8345dc..6bf83bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -79,6 +79,7 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
 import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.AutohideSuspendFlag;
 import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback;
@@ -524,6 +525,16 @@
         setTaskbarWindowFocusable(isVisible);
     }
 
+    @Override
+    public void onSplitScreenMenuButtonClicked() {
+        PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(this);
+        if (popup != null) {
+            popup.addOnCloseCallback(() -> {
+                mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+            });
+        }
+    }
+
     /**
      * Sets a new data-source for this taskbar instance
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 115b99e..0ed4538 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -65,6 +65,7 @@
 
 /**
  * Implements interfaces required to show and allow interacting with a PopupContainerWithArrow.
+ * Controls the long-press menu on Taskbar and AllApps icons.
  */
 public class TaskbarPopupController implements TaskbarControllers.LoggableTaskbarController {
 
@@ -174,6 +175,7 @@
                     deepShortcutCount,
                     mPopupDataProvider.getNotificationKeysForItem(item),
                     systemShortcuts);
+            icon.clearAccessibilityFocus();
         }
 
         container.addOnAttachStateChangeListener(
@@ -190,9 +192,8 @@
 
         // Make focusable to receive back events
         context.onPopupVisibilityChanged(true);
-        container.setOnCloseCallback(() -> {
+        container.addOnCloseCallback(() -> {
             context.getDragLayer().post(() -> context.onPopupVisibilityChanged(false));
-            container.setOnCloseCallback(null);
         });
 
         return container;
@@ -293,13 +294,19 @@
 
         @Override
         public void onClick(View view) {
+            // Add callbacks depending on what type of Taskbar context we're in (Taskbar or AllApps)
+            mTarget.onSplitScreenMenuButtonClicked();
             AbstractFloatingView.closeAllOpenViews(mTarget);
+
+            // Depending on what app state we're in, we either want to initiate the split screen
+            // staging process or immediately launch a split with an existing app.
+            // - Initiate the split screen staging process
              if (mAllowInitialSplitSelection) {
                  super.onClick(view);
                  return;
              }
 
-            // Initiate splitscreen from the in-app Taskbar or Taskbar All Apps
+            // - Immediately launch split with the running app
             Pair<InstanceId, com.android.launcher3.logging.InstanceId> instanceIds =
                     LogUtils.getShellShareableInstanceId();
             mTarget.getStatsLogManager().logger()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 062b4ce..dd88a37 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -138,7 +138,7 @@
      * Returns an animation to reset the taskbar translation to {@code 0}.
      */
     public ObjectAnimator createAnimToResetTranslation(long duration) {
-        ObjectAnimator animator = ObjectAnimator.ofFloat(mTranslationYForSwipe, VALUE, 0);
+        ObjectAnimator animator = mTranslationYForSwipe.animateToValue(0);
         animator.setInterpolator(Interpolators.LINEAR);
         animator.setDuration(duration);
         animator.addListener(new AnimatorListenerAdapter() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 0116e16..c5b6cdf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 import com.android.launcher3.views.IconButtonView;
@@ -66,6 +67,7 @@
     private final int mIconTouchSize;
     private final int mItemMarginLeftRight;
     private final int mItemPadding;
+    private final int mFolderLeaveBehindColor;
     private final boolean mIsRtl;
 
     private final TaskbarActivityContext mActivityContext;
@@ -132,6 +134,9 @@
         mItemMarginLeftRight = actualMargin - (mIconTouchSize - actualIconSize) / 2;
         mItemPadding = (mIconTouchSize - actualIconSize) / 2;
 
+        mFolderLeaveBehindColor = Themes.getAttrColor(mActivityContext,
+                android.R.attr.textColorTertiary);
+
         // Needed to draw folder leave-behind when opening one.
         setWillNotDraw(false);
 
@@ -523,7 +528,8 @@
         if (mLeaveBehindFolderIcon != null) {
             canvas.save();
             canvas.translate(mLeaveBehindFolderIcon.getLeft(), mLeaveBehindFolderIcon.getTop());
-            mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas);
+            mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas,
+                    mFolderLeaveBehindColor);
             canvas.restore();
         }
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 3edb375..66d5918 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.taskbar.overlay;
 
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
 import android.content.Context;
 import android.view.View;
 
@@ -33,10 +31,6 @@
 import com.android.launcher3.taskbar.TaskbarUIController;
 import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.recents.model.Task;
-
-import java.util.function.Consumer;
 
 /**
  * Window context for the taskbar overlays such as All Apps and EDU.
@@ -141,4 +135,8 @@
 
     @Override
     public void onPopupVisibilityChanged(boolean isVisible) {}
+
+    @Override
+    public void onSplitScreenMenuButtonClicked() {
+    }
 }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 38b0e08..37194b1 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -92,6 +92,9 @@
     private static final String TAG = "CellLayout";
     private static final boolean LOGD = false;
 
+    /** The color of the "leave-behind" shape when a folder is opened from Hotseat. */
+    private static final int FOLDER_LEAVE_BEHIND_COLOR = Color.argb(160, 245, 245, 245);
+
     protected final ActivityContext mActivity;
     @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCellWidth;
@@ -528,7 +531,7 @@
                     mFolderLeaveBehind.mDelegateCellY, mTempLocation);
             canvas.save();
             canvas.translate(mTempLocation[0], mTempLocation[1]);
-            mFolderLeaveBehind.drawLeaveBehind(canvas);
+            mFolderLeaveBehind.drawLeaveBehind(canvas, FOLDER_LEAVE_BEHIND_COLOR);
             canvas.restore();
         }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index c81ad01..abf5866 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -31,7 +31,11 @@
 import android.content.pm.LauncherApps;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.SparseArray;
+import android.widget.RemoteViews;
 
+import androidx.annotation.GuardedBy;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.config.FeatureFlags;
@@ -70,6 +74,12 @@
     private final InvariantDeviceProfile mInvariantDeviceProfile;
     private final RunnableList mOnTerminateCallback = new RunnableList();
 
+    // WORKAROUND: b/269335387 remove this after widget background listener is enabled
+    /* Array of RemoteViews cached by Launcher process */
+    @GuardedBy("itself")
+    @NonNull
+    public final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
+
     public static LauncherAppState getInstance(final Context context) {
         return INSTANCE.get(context);
     }
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3c316b8..27119ae 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -174,7 +174,7 @@
             mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
             ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
             popup.requestFocus();
-            popup.setOnCloseCallback(() -> {
+            popup.addOnCloseCallback(() -> {
                 host.requestFocus();
                 host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
                 host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 8f9fa8a..2465745 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -333,12 +333,15 @@
                 getOffsetX() + inset, getOffsetY() + inset, getScaledRadius() - inset, mPaint);
     }
 
-    public void drawLeaveBehind(Canvas canvas) {
+    /**
+     * Draws the leave-behind circle on the given canvas and in the given color.
+     */
+    public void drawLeaveBehind(Canvas canvas, int color) {
         float originalScale = mScale;
         mScale = 0.5f;
 
         mPaint.setStyle(Paint.Style.FILL);
-        mPaint.setColor(Color.argb(160, 245, 245, 245));
+        mPaint.setColor(color);
         getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
 
         mScale = originalScale;
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index be3a09b..9d06d4a 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -48,14 +48,13 @@
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
@@ -124,7 +123,7 @@
     private final GradientDrawable mRoundedTop;
     private final GradientDrawable mRoundedBottom;
 
-    @Nullable private Runnable mOnCloseCallback = null;
+    private RunnableList mOnCloseCallbacks = new RunnableList();
 
     // The rect string of the view that the arrow is attached to, in screen reference frame.
     protected int mArrowColor;
@@ -785,16 +784,14 @@
         mDeferContainerRemoval = false;
         getPopupContainer().removeView(this);
         getPopupContainer().removeView(mArrow);
-        if (mOnCloseCallback != null) {
-            mOnCloseCallback.run();
-        }
+        mOnCloseCallbacks.executeAllAndClear();
     }
 
     /**
-     * Callback to be called when the popup is closed
+     * Callbacks to be called when the popup is closed
      */
-    public void setOnCloseCallback(@Nullable Runnable callback) {
-        mOnCloseCallback = callback;
+    public void addOnCloseCallback(Runnable callback) {
+        mOnCloseCallbacks.add(callback);
     }
 
     protected BaseDragLayer getPopupContainer() {
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 8e67eb1..2ca825c 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -36,6 +36,7 @@
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
@@ -74,8 +75,6 @@
     private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
     @NonNull
     private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
-    @NonNull
-    private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
 
     protected int mFlags = FLAG_STATE_IS_NORMAL;
 
@@ -174,6 +173,12 @@
     public void deleteAppWidgetId(int appWidgetId) {
         mWidgetHost.deleteAppWidgetId(appWidgetId);
         mViews.remove(appWidgetId);
+        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+            final LauncherAppState state = LauncherAppState.getInstance(mContext);
+            synchronized (state.mCachedRemoteViews) {
+                state.mCachedRemoteViews.delete(appWidgetId);
+            }
+        }
     }
 
     /**
@@ -308,7 +313,17 @@
         if (WidgetsModel.GO_DISABLE_WIDGETS) {
             return;
         }
-
+        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+            // Cache the content from the widgets when Launcher stops listening to widget updates
+            final LauncherAppState state = LauncherAppState.getInstance(mContext);
+            synchronized (state.mCachedRemoteViews) {
+                for (int i = 0; i < mViews.size(); i++) {
+                    final int appWidgetId = mViews.keyAt(i);
+                    final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+                    state.mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
+                }
+            }
+        }
         mWidgetHost.stopListening();
         setListeningFlag(false);
     }
@@ -350,23 +365,24 @@
             // RemoteViews from system process.
             // TODO: have launcher always listens to widget updates in background so that this
             //  check can be removed altogether.
-            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
-                    && mCachedRemoteViews.get(appWidgetId) != null) {
-                // We've found RemoteViews from cache for this widget, so we will instantiate a
-                // widget host view and populate it with the cached RemoteViews.
-                final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
-                view.setAppWidget(appWidgetId, appWidget);
-                view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
-                mDeferredViews.put(appWidgetId, view);
-                mViews.put(appWidgetId, view);
-                return view;
-            } else {
-                // When cache misses, a placeholder for the widget will be returned instead.
-                DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
-                view.setAppWidget(appWidgetId, appWidget);
-                mViews.put(appWidgetId, view);
-                return view;
+            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+                final RemoteViews cachedRemoteViews = getCachedRemoteViews(appWidgetId);
+                if (cachedRemoteViews != null) {
+                    // We've found RemoteViews from cache for this widget, so we will instantiate a
+                    // widget host view and populate it with the cached RemoteViews.
+                    final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+                    view.setAppWidget(appWidgetId, appWidget);
+                    view.updateAppWidget(cachedRemoteViews);
+                    mDeferredViews.put(appWidgetId, view);
+                    mViews.put(appWidgetId, view);
+                    return view;
+                }
             }
+            // If cache misses or not enabled, a placeholder for the widget will be returned.
+            DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+            view.setAppWidget(appWidgetId, appWidget);
+            mViews.put(appWidgetId, view);
+            return view;
         } else {
             try {
                 return mWidgetHost.createView(context, appWidgetId, appWidget);
@@ -432,15 +448,8 @@
         LauncherAppWidgetHost tempHost = (LauncherAppWidgetHost) mWidgetHost;
         tempHost.clearViews();
         if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
-            // First, we clear any previously cached content from existing widgets
-            mCachedRemoteViews.clear();
+            // Clear previously cached content from existing widgets
             mDeferredViews.clear();
-            // Then we proceed to cache the content from the widgets
-            for (int i = 0; i < mViews.size(); i++) {
-                final int appWidgetId = mViews.keyAt(i);
-                final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
-                mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
-            }
         }
         mViews.clear();
     }
@@ -481,6 +490,14 @@
         return (flags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN;
     }
 
+    @Nullable
+    private RemoteViews getCachedRemoteViews(int appWidgetId) {
+        final LauncherAppState state = LauncherAppState.getInstance(mContext);
+        synchronized (state.mCachedRemoteViews) {
+            return state.mCachedRemoteViews.get(appWidgetId);
+        }
+    }
+
     /**
      * Returns the new LauncherWidgetHolder instance
      */