Merge "Update recents child views RTL handling" into ub-launcher3-rvc-dev
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 7520688..6761148 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -46,6 +46,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 
 /**
@@ -65,6 +66,7 @@
     private final int mNormalizedIconRadius;
     private final BlurMaskFilter mShadowFilter;
     private int mPlateColor;
+    boolean mDrawForDrag = false;
 
 
     public PredictedAppIcon(Context context) {
@@ -188,6 +190,10 @@
     }
 
     private void drawEffect(Canvas canvas, boolean isBadged) {
+        // Don't draw ring effect if item is about to be dragged.
+        if (mDrawForDrag) {
+            return;
+        }
         mRingPath.reset();
         getShape().addToPath(mRingPath, getOutlineOffsetX(), getOutlineOffsetY(),
                 mNormalizedIconRadius);
@@ -208,6 +214,26 @@
         canvas.drawPath(mRingPath, mIconRingPaint);
     }
 
+    @Override
+    public void getSourceVisualDragBounds(Rect bounds) {
+        super.getSourceVisualDragBounds(bounds);
+        if (!mIsPinned) {
+            int internalSize = (int) (bounds.width() * RING_EFFECT_RATIO);
+            bounds.inset(internalSize, internalSize);
+        }
+    }
+
+    @Override
+    public SafeCloseable prepareDrawDragView() {
+        mDrawForDrag = true;
+        invalidate();
+        SafeCloseable r = super.prepareDrawDragView();
+        return () -> {
+            r.close();
+            mDrawForDrag = false;
+        };
+    }
+
     /**
      * Creates and returns a new instance of PredictedAppIcon from WorkspaceItemInfo
      */
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 414d389..0be2486 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -21,7 +21,6 @@
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.views.RecentsView;
 
@@ -64,10 +63,10 @@
         Rect out = new Rect();
         activity.<RecentsView>getOverviewPanel().getTaskSize(out);
         int taskHeight = out.height();
-        float topMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float bottomMargin = res.getDimension(R.dimen.overview_actions_top_margin);
-        float newHeight = taskHeight + topMargin + bottomMargin;
-        float scale = newHeight / taskHeight;
+        activity.<RecentsView>getOverviewPanel().getModalTaskSize(out);
+        int newHeight = out.height();
+
+        float scale = (float) newHeight / taskHeight;
 
         return new float[] {scale, NO_OFFSET};
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index e7fe142..0bf81ef 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -724,7 +724,7 @@
         if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
             return false;
         }
-        if (mGestureState.getEndTarget() == NEW_TASK
+        if (mStateCallback.hasStates(STATE_START_NEW_TASK)
                 && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
             reset();
             return true;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 4b2fc75..258d60c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -38,6 +38,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Icon;
 import android.os.Build;
@@ -69,6 +70,7 @@
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.OnboardingPrefs;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
 import com.android.quickstep.inputconsumers.AssistantInputConsumer;
 import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
@@ -81,6 +83,7 @@
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.AssistantUtilities;
 import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.SplitScreenBounds;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.plugins.OverscrollPlugin;
 import com.android.systemui.plugins.PluginListener;
@@ -118,7 +121,7 @@
 /**
  * Service connected by system-UI for handling touch interaction.
  */
-@TargetApi(Build.VERSION_CODES.Q)
+@TargetApi(Build.VERSION_CODES.R)
 public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
         ProtoTraceable<LauncherTraceProto> {
 
@@ -229,6 +232,11 @@
             MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
         }
 
+        public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets)  {
+            WindowBounds wb = new WindowBounds(bounds, insets);
+            MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
+        }
+
         /** Deprecated methods **/
         public void onQuickStep(MotionEvent motionEvent) { }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
index 7201b02..f06a6a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -145,8 +145,6 @@
 
     /** Updates vertical margins for different navigation mode. */
     public void updateVerticalMarginForNavModeChange(Mode mode) {
-        int topMargin = getResources()
-                .getDimensionPixelSize(R.dimen.overview_actions_top_margin);
         int bottomMargin = 0;
         if (mode == Mode.THREE_BUTTONS) {
             bottomMargin = getResources()
@@ -157,6 +155,6 @@
         }
         LayoutParams params = (LayoutParams) getLayoutParams();
         params.setMargins(
-                params.leftMargin, topMargin, params.rightMargin, bottomMargin);
+                params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index bcf4660..54ed40f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -126,6 +126,7 @@
 import com.android.quickstep.util.AppWindowAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.util.RecentsOrientedState;
+import com.android.quickstep.util.SplitScreenBounds;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.WindowSizeStrategy;
 import com.android.systemui.plugins.ResourceProvider;
@@ -147,7 +148,8 @@
 @TargetApi(Build.VERSION_CODES.P)
 public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
-        InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener {
+        InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
+        SplitScreenBounds.OnChangeListener {
 
     private static final String TAG = RecentsView.class.getSimpleName();
 
@@ -510,6 +512,7 @@
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
                 mIPinnedStackAnimationListener);
         mOrientationState.initListeners();
+        SplitScreenBounds.INSTANCE.addOnChangeListener(this);
     }
 
     @Override
@@ -523,6 +526,7 @@
         RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
         mIdp.removeOnChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
+        SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
         mIPinnedStackAnimationListener.setActivity(null);
         mOrientationState.destroyListeners();
     }
@@ -839,6 +843,11 @@
         mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
     }
 
+    /** Gets the task size for modal state. */
+    public void getModalTaskSize(Rect outRect) {
+        mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
+    }
+
     @Override
     protected boolean computeScrollHelper() {
         boolean scrolling = super.computeScrollHelper();
@@ -2160,18 +2169,6 @@
         updatePageOffsets();
         if (getCurrentPageTaskView() != null) {
             getCurrentPageTaskView().setModalness(modalness);
-            TaskView tv = getCurrentPageTaskView();
-
-            // Move the task view up as it scales...
-            // ...the icon on taskview is hidden in modal state, so consider the top of the task
-            mTempFloatPoint[0] = 0;
-            mTempFloatPoint[1] = tv.getTop() + mTaskTopMargin;
-            // ...find the top after the transformation
-            getMatrix().mapPoints(mTempFloatPoint);
-
-            // ...make it match the top inset
-            float calcOffset = (mInsets.top - mTempFloatPoint[1]) * mTaskModalness;
-            tv.setTranslationY(calcOffset);
         }
     }
 
@@ -2180,6 +2177,13 @@
         return null;
     }
 
+    @Override
+    public void onSecondaryWindowBoundsChanged() {
+        // Invalidate the task view size
+        setInsets(mInsets);
+        requestLayout();
+    }
+
     /**
      * Enables or disables modal state for RecentsView
      * @param isModalState
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 6c521fc..72275c8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,7 +24,6 @@
 
     <!-- Overrideable in overlay that provides the Overview Actions. -->
     <dimen name="overview_actions_height">66dp</dimen>
-    <dimen name="overview_actions_top_margin">44dp</dimen>
     <dimen name="overview_actions_bottom_margin_gesture">16dp</dimen>
     <dimen name="overview_actions_bottom_margin_three_button">8dp</dimen>
     <dimen name="overview_actions_horizontal_margin">16dp</dimen>
@@ -64,6 +63,7 @@
     <dimen name="task_card_menu_horizontal_padding">0dp</dimen>
     <dimen name="portrait_task_card_horz_space">136dp</dimen>
     <dimen name="portrait_task_card_horz_space_big_overview">96dp</dimen>
+    <dimen name="portrait_modal_task_card_horz_space">60dp</dimen>
     <dimen name="landscape_task_card_horz_space">200dp</dimen>
     <dimen name="multi_window_task_card_horz_space">100dp</dimen>
     <!-- Copied from framework resource:
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 8368817..c841170 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -129,12 +129,12 @@
     <!-- Feedback shown during interactive parts of Home gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
     <string name="home_gesture_feedback_wrong_swipe_direction" translatable="false">Make sure you swipe straight up</string>
 
-  <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
-  <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
-    <!-- Button text shown on a button on the confirm screen. [CHAR LIMIT=14] -->
-    <string name="gesture_tutorial_action_button_label" translatable="false">Done</string>
-    <!-- Button text shown on a text button on the confirm screen. [CHAR LIMIT=14] -->
-    <string name="gesture_tutorial_action_text_button_label" translatable="false">Settings</string>
+    <!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
+    <string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
+    <!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] -->
+    <string name="gesture_tutorial_action_button_label_done" translatable="false">Done</string>
+    <!-- Button text shown on a button to go to Settings. [CHAR LIMIT=14] -->
+    <string name="gesture_tutorial_action_button_label_settings" translatable="false">Settings</string>
 
     <!-- ******* Overview ******* -->
     <!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index b0ce8e6..2d9c56f 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -154,8 +154,7 @@
      * Loads and creates a list of all the recent tasks.
      */
     @VisibleForTesting
-    ArrayList<Task> loadTasksInBackground(int numTasks,
-            boolean loadKeysOnly) {
+    ArrayList<Task> loadTasksInBackground(int numTasks, boolean loadKeysOnly) {
         int currentUserId = Process.myUserHandle().getIdentifier();
         ArrayList<Task> allTasks = new ArrayList<>();
         List<ActivityManager.RecentTaskInfo> rawTasks =
@@ -174,9 +173,7 @@
             }
         };
 
-        int taskCount = rawTasks.size();
-        for (int i = 0; i < taskCount; i++) {
-            ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
+        for (ActivityManager.RecentTaskInfo rawTask : rawTasks) {
             Task.TaskKey taskKey = new Task.TaskKey(rawTask);
             Task task;
             if (!loadKeysOnly) {
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index f3cefb9..58870ed 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -68,13 +68,16 @@
     @Override
     Integer getActionButtonStringId() {
         if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label;
+            return R.string.gesture_tutorial_action_button_label_done;
         }
         return null;
     }
 
     @Override
     Integer getActionTextButtonStringId() {
+        if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
+            return R.string.gesture_tutorial_action_button_label_settings;
+        }
         return null;
     }
 
@@ -86,7 +89,6 @@
     @Override
     void onActionTextButtonClicked(View button) {
         mTutorialFragment.startSystemNavigationSetting();
-        mTutorialFragment.closeTutorial();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0e45376..524cbaf 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -60,7 +60,7 @@
     @Override
     Integer getActionButtonStringId() {
         if (mTutorialType == HOME_NAVIGATION_COMPLETE) {
-            return R.string.gesture_tutorial_action_button_label;
+            return R.string.gesture_tutorial_action_button_label_done;
         }
         return null;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 3a56b0e..44c1a5d 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep.interaction;
 
-import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.graphics.Insets;
 import android.os.Bundle;
@@ -35,8 +34,6 @@
 import com.android.launcher3.R;
 import com.android.quickstep.interaction.TutorialController.TutorialType;
 
-import java.net.URISyntaxException;
-
 abstract class TutorialFragment extends Fragment implements OnTouchListener {
 
     private static final String LOG_TAG = "TutorialFragment";
@@ -182,14 +179,6 @@
     }
 
     void startSystemNavigationSetting() {
-        try {
-            startActivityForResult(
-                    Intent.parseUri(SYSTEM_NAVIGATION_SETTING_INTENT, /* flags= */ 0),
-                    /* requestCode= */ 0);
-        } catch (URISyntaxException e) {
-            Log.e(LOG_TAG, "The launch Intent Uri is wrong syntax: " + e);
-        } catch (ActivityNotFoundException e) {
-            Log.e(LOG_TAG, "The launch Activity not found: " + e);
-        }
+        startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
     }
 }
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index fffbb34..e7ff48f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -51,6 +51,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.WindowBounds;
 
 import java.lang.annotation.Retention;
 import java.util.function.IntConsumer;
@@ -361,7 +362,8 @@
         float fullHeight = dp.heightPx - insets.top - insets.bottom;
 
         if (dp.isMultiWindowMode) {
-            mSizeStrategy.getMultiWindowSize(mContext, dp, outPivot);
+            WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(mContext);
+            outPivot.set(bounds.availableSize.x, bounds.availableSize.y);
         } else {
             outPivot.set(fullWidth, fullHeight);
         }
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
new file mode 100644
index 0000000..a770e8e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.WindowInsets.Type;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.WindowBounds;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to hold the information abound a window bounds for split screen
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public class SplitScreenBounds {
+
+    public static final SplitScreenBounds INSTANCE = new SplitScreenBounds();
+    private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
+
+    @Nullable
+    private WindowBounds mBounds;
+
+    private SplitScreenBounds() { }
+
+    @UiThread
+    public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) {
+        if (!bounds.equals(mBounds)) {
+            mBounds = bounds;
+            for (OnChangeListener listener : mListeners) {
+                listener.onSecondaryWindowBoundsChanged();
+            }
+        }
+    }
+
+    public @NonNull WindowBounds getSecondaryWindowBounds(Context context) {
+        if (mBounds == null) {
+            mBounds = createDefaultWindowBounds(context);
+        }
+        return mBounds;
+    }
+
+    /**
+     * Creates window bounds as 50% of device size
+     */
+    private static WindowBounds createDefaultWindowBounds(Context context) {
+        WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
+        Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+
+        WindowBounds bounds = new WindowBounds(wm.getBounds(),
+                new Rect(insets.left, insets.top, insets.right, insets.bottom));
+        int rotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
+        int halfDividerSize = context.getResources()
+                .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+
+        if (rotation == ROTATION_0 || rotation == ROTATION_180) {
+            bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize;
+            bounds.insets.top = 0;
+        } else {
+            bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize;
+            bounds.insets.left = 0;
+        }
+        return new WindowBounds(bounds.bounds, bounds.insets);
+    }
+
+    public void addOnChangeListener(OnChangeListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeOnChangeListener(OnChangeListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Interface to receive window bounds changes
+     */
+    public interface OnChangeListener {
+
+        /**
+         * Called when window bounds for secondary window changes
+         */
+        void onSecondaryWindowBoundsChanged();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
index 1557dfc..b088ba8 100644
--- a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
+++ b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
@@ -20,13 +20,15 @@
 import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
 import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Build;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.SysUINavigationMode.Mode;
 
 /**
@@ -34,9 +36,9 @@
  * TODO: Merge is with {@link com.android.quickstep.BaseActivityInterface} once we remove the
  * state dependent members from {@link com.android.quickstep.LauncherActivityInterface}
  */
+@TargetApi(Build.VERSION_CODES.R)
 public abstract class WindowSizeStrategy {
 
-    private final PointF mTempPoint = new PointF();
     public final boolean rotationSupportedByActivity;
 
     private WindowSizeStrategy(boolean rotationSupportedByActivity) {
@@ -44,11 +46,6 @@
     }
 
     /**
-     * Sets the expected window size in multi-window mode
-     */
-    public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
-
-    /**
      * Calculates the taskView size for the provided device configuration
      */
     public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
@@ -59,35 +56,41 @@
 
     private void calculateTaskSize(
             Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
-        float taskWidth, taskHeight, paddingHorz;
         Resources res = context.getResources();
-        Rect insets = dp.getInsets();
         final boolean showLargeTaskSize = showOverviewActions(context);
 
+        final int paddingResId;
         if (dp.isMultiWindowMode) {
-            getMultiWindowSize(context, dp, mTempPoint);
-            taskWidth = mTempPoint.x;
-            taskHeight = mTempPoint.y;
-            paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
+            paddingResId = R.dimen.multi_window_task_card_horz_space;
+        } else if (dp.isVerticalBarLayout()) {
+            paddingResId = R.dimen.landscape_task_card_horz_space;
+        } else if (showLargeTaskSize) {
+            paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
+        } else {
+            paddingResId = R.dimen.portrait_task_card_horz_space;
+        }
+        float paddingHorz = res.getDimension(paddingResId);
+        float paddingVert = showLargeTaskSize
+                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
+
+        calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
+                res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
+    }
+
+    private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
+            float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
+            Rect outRect) {
+        float taskWidth, taskHeight;
+        Rect insets = dp.getInsets();
+        if (dp.isMultiWindowMode) {
+            WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
+            taskWidth = bounds.availableSize.x;
+            taskHeight = bounds.availableSize.y;
         } else {
             taskWidth = dp.availableWidthPx;
             taskHeight = dp.availableHeightPx;
-
-            final int paddingResId;
-            if (dp.isVerticalBarLayout()) {
-                paddingResId = R.dimen.landscape_task_card_horz_space;
-            } else if (showLargeTaskSize) {
-                paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
-            } else {
-                paddingResId = R.dimen.portrait_task_card_horz_space;
-            }
-            paddingHorz = res.getDimension(paddingResId);
         }
 
-        float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float paddingVert = showLargeTaskSize
-                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
-
         // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
         // we override the insets ourselves.
         int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
@@ -109,27 +112,42 @@
                 Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
     }
 
+    /**
+     * Calculates the modal taskView size for the provided device configuration
+     */
+    public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+        float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
+                ? R.dimen.multi_window_task_card_horz_space
+                : dp.isVerticalBarLayout()
+                        ? R.dimen.landscape_task_card_horz_space
+                        : R.dimen.portrait_modal_task_card_horz_space);
+        float extraVerticalSpace = getOverviewActionsHeight(context);
+        float paddingVert = 0;
+        float topIconMargin = 0;
+        calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
+                topIconMargin, outRect);
+    }
+
+    /** Gets the space that the overview actions will take, including margins. */
+    public float getOverviewActionsHeight(Context context) {
+        Resources res = context.getResources();
+        float actionsBottomMargin = 0;
+        if (getMode(context) == Mode.THREE_BUTTONS) {
+            actionsBottomMargin = res.getDimensionPixelSize(
+                R.dimen.overview_actions_bottom_margin_three_button);
+        } else {
+            actionsBottomMargin = res.getDimensionPixelSize(
+                R.dimen.overview_actions_bottom_margin_gesture);
+        }
+        float overviewActionsHeight = actionsBottomMargin
+                + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+        return overviewActionsHeight;
+    }
 
     public static final WindowSizeStrategy LAUNCHER_ACTIVITY_SIZE_STRATEGY =
             new WindowSizeStrategy(true) {
 
         @Override
-        public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
-            DeviceProfile fullDp = dp.getFullScreenProfile();
-            // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
-            // account for system insets
-            out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
-            float halfDividerSize = context.getResources()
-                    .getDimension(R.dimen.multi_window_task_divider_size) / 2;
-
-            if (fullDp.isLandscape) {
-                out.x = out.x / 2 - halfDividerSize;
-            } else {
-                out.y = out.y / 2 - halfDividerSize;
-            }
-        }
-
-        @Override
         float getExtraSpace(Context context, DeviceProfile dp) {
             if (dp.isVerticalBarLayout()) {
                 return  0;
@@ -138,17 +156,7 @@
                 if (showOverviewActions(context)) {
                     //TODO: this needs to account for the swipe gesture height and accessibility
                     // UI when shown.
-                    float actionsBottomMargin = 0;
-                    if (getMode(context) == Mode.THREE_BUTTONS) {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                                R.dimen.overview_actions_bottom_margin_three_button);
-                    } else {
-                        actionsBottomMargin = res.getDimensionPixelSize(
-                                R.dimen.overview_actions_bottom_margin_gesture);
-                    }
-                    float actionsHeight = actionsBottomMargin
-                            + res.getDimensionPixelSize(R.dimen.overview_actions_height);
-                    return actionsHeight;
+                    return getOverviewActionsHeight(context);
                 } else {
                     return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
                             + res.getDimensionPixelSize(
@@ -162,10 +170,6 @@
 
     public static final WindowSizeStrategy FALLBACK_RECENTS_SIZE_STRATEGY =
             new WindowSizeStrategy(false) {
-        @Override
-        public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
-            out.set(dp.widthPx, dp.heightPx);
-        }
 
         @Override
         float getExtraSpace(Context context, DeviceProfile dp) {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 79ed2b8..60b6da6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -60,6 +60,7 @@
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.model.data.PromiseAppInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.IconLabelDotView;
 
@@ -744,11 +745,12 @@
     }
 
     @Override
-    public void prepareDrawDragView() {
+    public SafeCloseable prepareDrawDragView() {
         if (getIcon() instanceof FastBitmapDrawable) {
             FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
             icon.setScale(1f);
         }
         setForceHideDot(true);
+        return () -> { };
     }
 }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index b4c5f96..d75d712 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
 
@@ -27,6 +29,7 @@
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
@@ -38,6 +41,8 @@
 
 public class DeleteDropTarget extends ButtonDropTarget {
 
+    private final StatsLogManager mStatsLogManager;
+
     private int mControlType = ControlType.DEFAULT_CONTROLTYPE;
 
     public DeleteDropTarget(Context context, AttributeSet attrs) {
@@ -46,6 +51,7 @@
 
     public DeleteDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        this.mStatsLogManager = StatsLogManager.newInstance(context);
     }
 
     @Override
@@ -120,6 +126,11 @@
             d.dragInfo.container = NO_ID;
         }
         super.onDrop(d, options);
+        mStatsLogManager.log(
+                mControlType == ControlType.REMOVE_TARGET
+                        ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+                        : LAUNCHER_ITEM_DROPPED_ON_CANCEL,
+                d.logInstanceId);
     }
 
     @Override
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 9dbb5fc..fbac0bd 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -7,6 +7,10 @@
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DISMISS_PREDICTION;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
 import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_UNINSTALL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_CANCELLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_UNINSTALL_COMPLETED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_SYSTEM_NO;
 
@@ -34,6 +38,7 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -58,7 +63,7 @@
 
     private static final long CACHE_EXPIRE_TIMEOUT = 5000;
     private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
-
+    private final StatsLogManager mStatsLogManager;
     private final Alarm mCacheExpireAlarm;
     private boolean mHadPendingAlarm;
 
@@ -69,8 +74,8 @@
 
     public SecondaryDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-
         mCacheExpireAlarm = new Alarm();
+        mStatsLogManager = StatsLogManager.newInstance(context);
     }
 
     @Override
@@ -214,6 +219,11 @@
         // Defer onComplete
         d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
         super.onDrop(d, options);
+        if (mCurrentAccessibilityAction == UNINSTALL) {
+            mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL, d.logInstanceId);
+        } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
+            mStatsLogManager.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST, d.logInstanceId);
+        }
     }
 
     @Override
@@ -338,8 +348,10 @@
                     mDragObject.dragInfo.user, PackageManager.MATCH_UNINSTALLED_PACKAGES) == null) {
                 mDragObject.dragSource = mOriginal;
                 mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
+                mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_COMPLETED, mDragObject.logInstanceId);
             } else {
                 sendFailure();
+                mStatsLogManager.log(LAUNCHER_ITEM_UNINSTALL_CANCELLED, mDragObject.logInstanceId);
             }
         }
 
diff --git a/src/com/android/launcher3/dragndrop/DraggableView.java b/src/com/android/launcher3/dragndrop/DraggableView.java
index 287c781..f7dcf6b 100644
--- a/src/com/android/launcher3/dragndrop/DraggableView.java
+++ b/src/com/android/launcher3/dragndrop/DraggableView.java
@@ -18,6 +18,10 @@
 
 import android.graphics.Rect;
 
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.util.SafeCloseable;
+
 /**
  * Interface defining methods required for drawing and previewing DragViews, drag previews, and
  * related animations
@@ -42,9 +46,12 @@
     int getViewType();
 
     /**
-     * Before rendering as a DragView bitmap, some views need a preparation step.
+     * Before rendering as a DragView bitmap, some views need a preparation step. Returns a
+     * callback to clear any preparation work
      */
-    default void prepareDrawDragView() { }
+    @NonNull default SafeCloseable prepareDrawDragView() {
+        return () -> { };
+    }
 
     /**
      * If an actual View subclass, this method returns the rectangle (within the View's coordinates)
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 634d07e..21822a3 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import java.nio.ByteBuffer;
@@ -76,11 +77,12 @@
 
         if (mView instanceof DraggableView) {
             DraggableView dv = (DraggableView) mView;
-            dv.prepareDrawDragView();
-            dv.getSourceVisualDragBounds(mTempRect);
-            destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
-                    blurSizeOutline / 2 - mTempRect.top);
-            mView.draw(destCanvas);
+            try (SafeCloseable t = dv.prepareDrawDragView()) {
+                dv.getSourceVisualDragBounds(mTempRect);
+                destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
+                        blurSizeOutline / 2 - mTempRect.top);
+                mView.draw(destCanvas);
+            }
         }
         destCanvas.restoreToCount(saveCount);
     }
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 20eec9a..c62f308 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -29,6 +29,7 @@
 import android.view.Display;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
 
 import com.android.launcher3.InvariantDeviceProfile;
 
@@ -37,6 +38,8 @@
 /** Render preview using surface view. */
 public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
 
+    private static final int FADE_IN_ANIMATION_DURATION = 200;
+
     private static final String KEY_HOST_TOKEN = "host_token";
     private static final String KEY_VIEW_WIDTH = "width";
     private static final String KEY_VIEW_HEIGHT = "height";
@@ -99,6 +102,11 @@
             view.setPivotY(0);
             view.setTranslationX((mWidth - scale * view.getWidth()) / 2);
             view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
+            view.setAlpha(0);
+            view.animate().alpha(1)
+                    .setInterpolator(new AccelerateDecelerateInterpolator())
+                    .setDuration(FADE_IN_ANIMATION_DURATION)
+                    .start();
             mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(),
                     view.getMeasuredHeight());
         });
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index fdd32b8..9455bd3 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -16,20 +16,24 @@
 package com.android.launcher3.logging;
 
 import android.content.Context;
+import android.util.Log;
 
 import com.android.launcher3.R;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ItemInfo;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
  * Handles the user event logging in R+.
- * All of the event id is defined here.
+ * All of the event ids are defined here.
  * Most of the methods are dummy methods for Launcher3
  * Actual call happens only for Launcher variant that implements QuickStep.
  */
 public class StatsLogManager implements ResourceBasedOverride {
 
+    private static final String TAG = "StatsLogManager";
+
     interface EventEnum {
         int getId();
     }
@@ -37,19 +41,46 @@
     public enum LauncherEvent implements EventEnum {
         @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
         LAUNCHER_APP_LAUNCH_TAP(338),
+
         @LauncherUiEvent(doc = "Task launched from overview using TAP")
         LAUNCHER_TASK_LAUNCH_TAP(339),
+
         @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
         LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
+
         @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
         LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
+
         @LauncherUiEvent(doc = "User dragged a launcher item")
         LAUNCHER_ITEM_DRAG_STARTED(383),
+
         @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
         LAUNCHER_ITEM_DROP_COMPLETED(385),
+
         @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
-                + "resulting in new folder creation")
-        LAUNCHER_ITEM_DROP_FOLDER_CREATED(386);
+                + "resulting in a new folder creation")
+        LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
+
+        @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
+        LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
+
+        @LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
+        LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
+
+        @LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+                + " button in the target bar")
+        LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
+
+        @LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
+        LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
+
+        @LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
+                + "the icon onto 'Uninstall' button in the target bar")
+        LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
+
+        @LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
+                + "the icon onto 'Uninstall' button in the target bar")
+        LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
         // ADD MORE
 
         private final int mId;
@@ -78,6 +109,14 @@
     }
 
     /**
+     * Logs an event and accompanying {@link ItemInfo}
+     */
+    public void log(LauncherEvent event, InstanceId instanceId) {
+        Log.d(TAG, String.format("%s(InstanceId:%s)", event.name(), instanceId));
+        // Call StatsLog method
+    }
+
+    /**
      * Logs an event and accompanying {@link LauncherAtom.ItemInfo}
      */
     public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) { }
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index da081a0..7818ff5 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -145,7 +145,9 @@
         LauncherEvent event = newLauncherEvent(action,  targets);
         ItemInfo info = v == null ? null : (ItemInfo) v.getTag();
         if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
-            FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
+            final String pkg = info.getTargetComponent() != null
+                    ? info.getTargetComponent().getPackageName() : "unknown";
+            FileLog.d(TAG, "appLaunch: packageName:" + pkg
                     + ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
                     userHandle)) + ",launchLocation:" + info.container);
         }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 4359f25..7611ee7 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -303,7 +303,7 @@
                     break;
             }
             itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
-        } else {
+        } else if (getContainerInfo().getContainerCase().getNumber() > 0) {
             itemBuilder.setContainerInfo(getContainerInfo());
         }
         return itemBuilder.build();
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
new file mode 100644
index 0000000..3c2fb62
--- /dev/null
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 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.util;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Utility class to hold information about window position and layout
+ */
+public class WindowBounds {
+
+    public final Rect bounds;
+    public final Rect insets;
+    public final Point availableSize;
+
+    public WindowBounds(Rect bounds, Rect insets) {
+        this.bounds = bounds;
+        this.insets = insets;
+        availableSize = new Point(bounds.width() - insets.left - insets.right,
+                bounds.height() - insets.top - insets.bottom);
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (!(obj instanceof WindowBounds)) {
+            return false;
+        }
+        WindowBounds other = (WindowBounds) obj;
+        return other.bounds.equals(bounds) && other.insets.equals(insets);
+    }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 94ab780..ce94a3e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -86,6 +86,11 @@
                         zeroButtonToOverviewGestureStartsInLauncher()
                                 ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
                                 : LauncherInstrumentation.GestureScope.OUTSIDE;
+
+                // b/156044202
+                mLauncher.log("Hierarchy before swiping up to overview:");
+                mLauncher.dumpViewHierarchy();
+
                 mLauncher.sendPointer(
                         downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
                 mLauncher.executeAndWaitForEvent(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 6e9c5a0..14212be 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -436,6 +436,13 @@
                 sEventChecker.finishNoWait();
             }
         }
+        // b/156287114
+        try {
+            log("Input: " + mDevice.executeShellCommand("dumpsys input"));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
         log("Hierarchy dump for: " + message);
         dumpViewHierarchy();
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index ac90b1b..79d20ac 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -207,7 +207,9 @@
 
     // Workaround for b/154157191
     private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
-        return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
+        // b/156287114
+        return false;
+//        return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
     }
 
     // If the list of actual events matches the list of expected events, returns -1, otherwise