Merge "add null check to prevent NPE in logging app launch" 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/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 979e3ef..bf9119e 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();
@@ -2155,18 +2164,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);
         }
     }
 
@@ -2175,6 +2172,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..dce6f67 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) {
@@ -65,9 +62,9 @@
         final boolean showLargeTaskSize = showOverviewActions(context);
 
         if (dp.isMultiWindowMode) {
-            getMultiWindowSize(context, dp, mTempPoint);
-            taskWidth = mTempPoint.x;
-            taskHeight = mTempPoint.y;
+            WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
+            taskWidth = bounds.availableSize.x;
+            taskHeight = bounds.availableSize.y;
             paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
         } else {
             taskWidth = dp.availableWidthPx;
@@ -109,27 +106,73 @@
                 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 taskWidth, taskHeight, paddingHorz;
+        Resources res = context.getResources();
+        Rect insets = dp.getInsets();
+
+        if (dp.isMultiWindowMode) {
+            getMultiWindowSize(context, dp, mTempPoint);
+            taskWidth = mTempPoint.x;
+            taskHeight = mTempPoint.y;
+            paddingHorz = res.getDimension(R.dimen.multi_window_task_card_horz_space);
+        } else {
+            taskWidth = dp.availableWidthPx;
+            taskHeight = dp.availableHeightPx;
+
+            final int paddingResId;
+            if (dp.isVerticalBarLayout()) {
+                paddingResId = R.dimen.landscape_task_card_horz_space;
+            } else {
+                paddingResId = R.dimen.portrait_modal_task_card_horz_space;
+            }
+            paddingHorz = res.getDimension(paddingResId);
+        }
+
+        // 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;
+        int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
+
+        // Calculate for the overview height.
+        float overviewActionsHeight = getOverviewActionsHeight(context);
+        float availableHeight = launcherVisibleHeight - overviewActionsHeight;
+        float availableWidth = launcherVisibleWidth - paddingHorz;
+
+        float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+        float outWidth = scale * taskWidth;
+        float outHeight = scale * taskHeight;
+
+        // Center in the visible space
+        float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
+        float y = insets.top + (launcherVisibleHeight - overviewActionsHeight - outHeight) / 2;
+        outRect.set(Math.round(x), Math.round(y),
+                Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+    }
+
+    /** 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 +181,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 +195,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/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/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);
+    }
+}