Animate SplitPlaceholderView when entering split from overview
Bugs tracked in b/181704764
Bug: 181704764
Test: Tested on phone and large screen in multiple orientations
Change-Id: I07509006ae3d1f4425dc5119d0c8ed52b41a3bc2
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index f54070e..5fc969d 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -43,7 +43,6 @@
<dimen name="overview_grid_side_margin">54dp</dimen>
<dimen name="overview_grid_row_spacing">42dp</dimen>
<dimen name="overview_grid_focus_vertical_margin">40dp</dimen>
- <dimen name="split_placeholder_size">110dp</dimen>
<!-- These speeds are in dp/s -->
<dimen name="max_task_dismiss_drag_velocity">2.25dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index ecd38b4..5250d18 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -271,12 +271,10 @@
SysUINavigationMode.INSTANCE.get(this).updateMode();
mActionsView = findViewById(R.id.overview_actions_view);
- mSplitPlaceholderView = findViewById(R.id.split_placeholder);
RecentsView overviewPanel = (RecentsView) getOverviewPanel();
- mSplitPlaceholderView.init(
- new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this))
- );
- overviewPanel.init(mActionsView, mSplitPlaceholderView);
+ SplitSelectStateController controller =
+ new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this));
+ overviewPanel.init(mActionsView, controller);
mActionsView.setDp(getDeviceProfile());
mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 6cad3dd..1f744e1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -18,13 +18,11 @@
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.LauncherState.SPLIT_PLACHOLDER_VIEW;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
-import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import android.annotation.TargetApi;
@@ -110,11 +108,6 @@
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
-
- float splitPlaceholderAlpha = state.areElementsVisible(mLauncher, SPLIT_PLACHOLDER_VIEW) ?
- 0.85f : 0;
- propertySetter.setFloat(mRecentsView.getSplitPlaceholder(), ALPHA_FLOAT,
- splitPlaceholderAlpha, LINEAR);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
index 6968494..1882a0c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java
@@ -43,7 +43,7 @@
@Override
public float getSplitSelectTranslation(Launcher launcher) {
RecentsView recentsView = launcher.getOverviewPanel();
- int splitPosition = recentsView.getSplitPlaceholder().getSplitController()
+ int splitPosition = recentsView.getSplitPlaceholder()
.getActiveSplitPositionOption().mStagePosition;
if (!recentsView.shouldShiftThumbnailsForSplitSelect(splitPosition)) {
return 0f;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 051485a..0603ba5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -227,7 +227,8 @@
if (goingUp) {
currentInterpolator = Interpolators.LINEAR;
pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
- true /* animateTaskView */, true /* removeTask */, maxDuration);
+ true /* animateTaskView */, true /* removeTask */, maxDuration,
+ false /* dismissingForSplitSelection*/);
mEndDisplacement = -secondaryTaskDimension;
} else {
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 9dfcd12..95be45a 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -122,13 +122,10 @@
mActionsView = findViewById(R.id.overview_actions_view);
SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
- SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
- splitPlaceholderView.init(
- new SplitSelectStateController(mUiHandler, SystemUiProxy.INSTANCE.get(this))
- );
-
+ SplitSelectStateController controller =
+ new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this));
mDragLayer.recreateControllers();
- mFallbackRecentsView.init(mActionsView, splitPlaceholderView);
+ mFallbackRecentsView.init(mActionsView, controller);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 3bf79f1..de79372 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -37,6 +37,7 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.SplitPlaceholderView;
@@ -62,8 +63,8 @@
}
@Override
- public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
- super.init(actionsView, splitPlaceholderView);
+ public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
+ super.init(actionsView, splitController);
setOverviewStateEnabled(true);
setOverlayEnabled(true);
}
@@ -96,7 +97,8 @@
if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
TaskView tv = getTaskView(mHomeTaskInfo.taskId);
if (tv != null) {
- PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
+ PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150,
+ false /* dismissingForSplitSelection*/);
pa.addEndListener(e -> setCurrentTask(-1));
AnimatorPlaybackController controller = pa.createPlaybackController();
controller.dispatchOnStart();
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 2351a4e..16c925a 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -56,6 +56,7 @@
private final SystemUiProxy mSystemUiProxy;
private TaskView mInitialTaskView;
+ private TaskView mSecondTaskView;
private SplitPositionOption mInitialPosition;
private Rect mInitialBounds;
private final Handler mHandler;
@@ -79,23 +80,19 @@
* To be called after second task selected
*/
public void setSecondTaskId(TaskView taskView) {
- if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
- // Assume initial task is for top/left part of screen
- final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
- : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
+ mSecondTaskView = taskView;
+ // Assume initial task is for top/left part of screen
+ final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
+ ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
+ : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
+ if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) {
RemoteSplitLaunchTransitionRunner animationRunner =
new RemoteSplitLaunchTransitionRunner(mInitialTaskView, taskView);
mSystemUiProxy.startTasks(taskIds[0], null /* mainOptions */, taskIds[1],
null /* sideOptions */, STAGE_POSITION_BOTTOM_OR_RIGHT,
new RemoteTransitionCompat(animationRunner, MAIN_EXECUTOR));
} else {
- // Assume initial task is for top/left part of screen
- final int[] taskIds = mInitialPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT
- ? new int[]{mInitialTaskView.getTask().key.id, taskView.getTask().key.id}
- : new int[]{taskView.getTask().key.id, mInitialTaskView.getTask().key.id};
-
RemoteSplitLaunchAnimationRunner animationRunner =
new RemoteSplitLaunchAnimationRunner(mInitialTaskView, taskView);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
@@ -191,12 +188,17 @@
*/
public void resetState() {
mInitialTaskView = null;
+ mSecondTaskView = null;
mInitialPosition = null;
mInitialBounds = null;
}
+ /**
+ * @return {@code true} if first task has been selected and waiting for the second task to be
+ * chosen
+ */
public boolean isSplitSelectActive() {
- return mInitialTaskView != null;
+ return mInitialTaskView != null && mSecondTaskView == null;
}
public Rect getInitialBounds() {
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
new file mode 100644
index 0000000..a1befc5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -0,0 +1,241 @@
+package com.android.quickstep.views;
+
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.util.MultiValueUpdateListener;
+
+/**
+ * Create an instance via {@link #getFloatingTaskView(StatefulActivity, TaskView, RectF)} to
+ * which will have the thumbnail from the provided existing TaskView overlaying the taskview itself.
+ *
+ * Can then animate the taskview using
+ * {@link #addAnimation(PendingAnimation, RectF, Rect, View, boolean)}
+ * giving a starting and ending bounds. Currently this is set to use the split placeholder view,
+ * but it could be generified.
+ *
+ * TODO: Figure out how to copy thumbnail data from existing TaskView to this view.
+ */
+public class FloatingTaskView extends FrameLayout {
+
+ private SplitPlaceholderView mSplitPlaceholderView;
+ private RectF mStartingPosition;
+ private final Launcher mLauncher;
+ private final boolean mIsRtl;
+ private final Rect mOutline = new Rect();
+ private PagedOrientationHandler mOrientationHandler;
+ private ImageView mImageView;
+
+ public FloatingTaskView(Context context) {
+ this(context, null);
+ }
+
+ public FloatingTaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FloatingTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+ mIsRtl = Utilities.isRtl(getResources());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mImageView = findViewById(R.id.thumbnail);
+ mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ mImageView.setLayerType(LAYER_TYPE_HARDWARE, null);
+ mSplitPlaceholderView = findViewById(R.id.split_placeholder);
+ mSplitPlaceholderView.setAlpha(0);
+ mSplitPlaceholderView.setBackgroundColor(getResources().getColor(android.R.color.white));
+ }
+
+ public static FloatingTaskView getFloatingTaskView(StatefulActivity launcher,
+ TaskView originalView, RectF positionOut) {
+ final BaseDragLayer dragLayer = launcher.getDragLayer();
+ ViewGroup parent = (ViewGroup) dragLayer.getParent();
+ final FloatingTaskView floatingView = (FloatingTaskView) launcher.getLayoutInflater()
+ .inflate(R.layout.floating_split_select_view, parent, false);
+
+ floatingView.mStartingPosition = positionOut;
+ floatingView.updateInitialPositionForView(originalView);
+ final InsettableFrameLayout.LayoutParams lp =
+ (InsettableFrameLayout.LayoutParams) floatingView.getLayoutParams();
+
+ floatingView.mSplitPlaceholderView.setLayoutParams(
+ new FrameLayout.LayoutParams(lp.width, lp.height));
+ positionOut.round(floatingView.mOutline);
+ floatingView.setPivotX(0);
+ floatingView.setPivotY(0);
+
+ // Copy bounds of exiting thumbnail into ImageView
+ TaskThumbnailView thumbnail = originalView.getThumbnail();
+ floatingView.mImageView.setImageBitmap(thumbnail.getThumbnail());
+ floatingView.mImageView.setVisibility(VISIBLE);
+
+ floatingView.mOrientationHandler =
+ originalView.getRecentsView().getPagedOrientationHandler();
+ floatingView.mSplitPlaceholderView.setIcon(originalView.getIconView());
+ floatingView.mSplitPlaceholderView.getIcon()
+ .setRotation(floatingView.mOrientationHandler.getDegreesRotated());
+ parent.addView(floatingView);
+ return floatingView;
+ }
+
+ public void updateInitialPositionForView(TaskView originalView) {
+ View thumbnail = originalView.getThumbnail();
+ Rect viewBounds = new Rect(0, 0, thumbnail.getWidth(), thumbnail.getHeight());
+ Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), thumbnail, viewBounds,
+ true /* ignoreTransform */, null /* recycle */,
+ mStartingPosition);
+ mStartingPosition.offset(originalView.getTranslationX(), originalView.getTranslationY());
+ final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
+ Math.round(mStartingPosition.width()),
+ Math.round(mStartingPosition.height()));
+ initPosition(mStartingPosition, lp);
+ setLayoutParams(lp);
+ }
+
+ // TODO(194414938) set correct corner radii
+ public void update(RectF position, float progress, float windowRadius) {
+ MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+
+ float dX = mIsRtl
+ ? position.left - (lp.getMarginStart() - lp.width)
+ : position.left - lp.getMarginStart();
+ float dY = position.top - lp.topMargin;
+
+ setTranslationX(dX);
+ setTranslationY(dY);
+
+ float scaleX = position.width() / lp.width;
+ float scaleY = position.height() / lp.height;
+ setScaleX(scaleX);
+ setScaleY(scaleY);
+ float childScaleX = 1f / scaleX;
+ float childScaleY = 1f / scaleY;
+
+ invalidate();
+ // TODO(194414938) seems like this scale value could be fine tuned, some stretchiness
+ mImageView.setScaleX(1f / scaleX + scaleX * progress);
+ mImageView.setScaleY(1f / scaleY + scaleY * progress);
+ mOrientationHandler.setPrimaryScale(mSplitPlaceholderView.getIcon(), childScaleX);
+ mOrientationHandler.setSecondaryScale(mSplitPlaceholderView.getIcon(), childScaleY);
+ }
+
+ protected void initPosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
+ mStartingPosition.set(pos);
+ lp.ignoreInsets = true;
+ // Position the floating view exactly on top of the original
+ lp.topMargin = Math.round(pos.top);
+ if (mIsRtl) {
+ lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right));
+ } else {
+ lp.setMarginStart(Math.round(pos.left));
+ }
+ // Set the properties here already to make sure they are available when running the first
+ // animation frame.
+ int left = mIsRtl
+ ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+ : lp.leftMargin;
+ layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
+ }
+
+ public void addAnimation(PendingAnimation animation, RectF startingBounds, Rect endBounds,
+ View viewToCover, boolean fadeWithThumbnail) {
+ final BaseDragLayer dragLayer = mLauncher.getDragLayer();
+ int[] dragLayerBounds = new int[2];
+ dragLayer.getLocationOnScreen(dragLayerBounds);
+ SplitOverlayProperties prop = new SplitOverlayProperties(endBounds,
+ startingBounds, viewToCover, dragLayerBounds[0],
+ dragLayerBounds[1]);
+
+ ValueAnimator transitionAnimator = ValueAnimator.ofFloat(0, 1);
+ animation.add(transitionAnimator);
+ long animDuration = animation.getDuration();
+ Rect crop = new Rect();
+ RectF floatingTaskViewBounds = new RectF();
+ final float initialWindowRadius = supportsRoundedCornersOnWindows(getResources())
+ ? Math.max(crop.width(), crop.height()) / 2f
+ : 0f;
+
+ if (fadeWithThumbnail) {
+ animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT,
+ 0, 1, ACCEL);
+ animation.addFloat(mImageView, LauncherAnimUtils.VIEW_ALPHA,
+ 1, 0, DEACCEL_3);
+ }
+
+ MultiValueUpdateListener listener = new MultiValueUpdateListener() {
+ final FloatProp mWindowRadius = new FloatProp(initialWindowRadius,
+ initialWindowRadius, 0, animDuration, LINEAR);
+ final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR);
+ final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR);
+ final FloatProp mTaskViewScaleX = new FloatProp(prop.initialTaskViewScaleX,
+ prop.finalTaskViewScaleX, 0, animDuration, LINEAR);
+ final FloatProp mTaskViewScaleY = new FloatProp(prop.initialTaskViewScaleY,
+ prop.finalTaskViewScaleY, 0, animDuration, LINEAR);
+ @Override
+ public void onUpdate(float percent, boolean initOnly) {
+ // Calculate the icon position.
+ floatingTaskViewBounds.set(startingBounds);
+ floatingTaskViewBounds.offset(mDx.value, mDy.value);
+ Utilities.scaleRectFAboutCenter(floatingTaskViewBounds, mTaskViewScaleX.value,
+ mTaskViewScaleY.value);
+
+ update(floatingTaskViewBounds, percent, mWindowRadius.value * 1);
+ }
+ };
+ transitionAnimator.addUpdateListener(listener);
+ }
+
+ private static class SplitOverlayProperties {
+
+ private final float initialTaskViewScaleX;
+ private final float initialTaskViewScaleY;
+ private final float finalTaskViewScaleX;
+ private final float finalTaskViewScaleY;
+ private final float dX;
+ private final float dY;
+
+ SplitOverlayProperties(Rect endBounds, RectF startTaskViewBounds, View view,
+ int dragLayerLeft, int dragLayerTop) {
+ float maxScaleX = endBounds.width() / startTaskViewBounds.width();
+ float maxScaleY = endBounds.height() / startTaskViewBounds.height();
+
+ initialTaskViewScaleX = view.getScaleX();
+ initialTaskViewScaleY = view.getScaleY();
+ finalTaskViewScaleX = maxScaleX;
+ finalTaskViewScaleY = maxScaleY;
+
+ // Animate the app icon to the center of the window bounds in screen coordinates.
+ float centerX = endBounds.centerX() - dragLayerLeft;
+ float centerY = endBounds.centerY() - dragLayerTop;
+
+ dX = centerX - startTaskViewBounds.centerX();
+ dY = centerY - startTaskViewBounds.centerY();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 65956d5..152c2bd 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -38,6 +38,7 @@
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.LauncherActivityInterface;
+import com.android.quickstep.util.SplitSelectStateController;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
@@ -81,7 +82,8 @@
}
@Override
- public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+ public void init(OverviewActionsView actionsView,
+ SplitSelectStateController splitPlaceholderView) {
super.init(actionsView, splitPlaceholderView);
setContentAlpha(0);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index edda439..be6e3ff 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -545,15 +545,18 @@
/**
* Placeholder view indicating where the first split screen selected app will be placed
*/
- private SplitPlaceholderView mSplitPlaceholderView;
+ private SplitSelectStateController mSplitSelectStateController;
/**
* The first task that split screen selection was initiated with. When split select state is
* initialized, we create a
- * {@link #createTaskDismissAnimation(TaskView, boolean, boolean, long)} for this TaskView but
- * don't actually remove the task since the user might back out. As such, we also ensure this
- * View doesn't go back into the {@link #mTaskViewPool}, see {@link #onViewRemoved(View)}
+ * {@link #createTaskDismissAnimation(TaskView, boolean, boolean, long, boolean)} for this
+ * TaskView but don't actually remove the task since the user might back out. As such, we also
+ * ensure this View doesn't go back into the {@link #mTaskViewPool},
+ * see {@link #onViewRemoved(View)}
*/
private TaskView mSplitHiddenTaskView;
+ private TaskView mSecondSplitHiddenTaskView;
+
/**
* Keeps track of the index of the TaskView that split screen was initialized with so we know
* where to insert it back into list of taskViews in case user backs out of entering split
@@ -563,6 +566,9 @@
* removed from recentsView
*/
private int mSplitHiddenTaskViewIndex;
+ private FloatingTaskView mFirstFloatingTaskView;
+ private FloatingTaskView mSecondFloatingTaskView;
+
/**
* The task to be removed and immediately re-added. Should not be added to task pool.
*/
@@ -776,18 +782,18 @@
updateTaskStackListenerState();
}
- public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
+ public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
mActionsView = actionsView;
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
- mSplitPlaceholderView = splitPlaceholderView;
+ mSplitSelectStateController = splitController;
}
- public SplitPlaceholderView getSplitPlaceholder() {
- return mSplitPlaceholderView;
+ public SplitSelectStateController getSplitPlaceholder() {
+ return mSplitSelectStateController;
}
public boolean isSplitSelectionActive() {
- return mSplitPlaceholderView.getSplitController().isSplitSelectActive();
+ return mSplitSelectStateController.isSplitSelectActive();
}
@Override
@@ -984,7 +990,7 @@
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTask = null;
- if (mSplitPlaceholderView.getSplitController().isSplitSelectActive()) {
+ if (mSplitSelectStateController.isSplitSelectActive()) {
cancelSplitSelect(false);
}
}
@@ -2286,50 +2292,23 @@
PendingAnimation anim) {
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
- anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && taskView.isRunningTask()) {
anim.setFloat(mLiveTileParams, TransformParams.TARGET_ALPHA, 0,
clampToProgress(ACCEL, 0, 0.5f));
}
- SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+ anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
+ FloatProperty<TaskView> secondaryViewTranslate =
+ taskView.getSecondaryDissmissTranslationProperty();
+ int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
+ int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
ResourceProvider rp = DynamicResource.provider(mActivity);
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
.setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
.setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
- FloatProperty<TaskView> dismissingTaskViewTranslate =
- taskView.getSecondaryDissmissTranslationProperty();
- // TODO(b/186800707) translate entire grid size distance
- int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
- int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
- if (splitController.isSplitSelectActive()) {
- // Have the task translate towards whatever side was just pinned
- int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitController
- .getActiveSplitPositionOption(), mActivity.getDeviceProfile());
- switch (dir) {
- case PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE:
- dismissingTaskViewTranslate = taskView
- .getSecondaryDissmissTranslationProperty();
- positiveNegativeFactor = -1;
- break;
- case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE:
- dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
- positiveNegativeFactor = 1;
- break;
-
- case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_NEGATIVE:
- dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
- positiveNegativeFactor = -1;
- break;
- default:
- throw new IllegalStateException("Invalid split task translation: " + dir);
- }
- }
- // Double translation distance so dismissal drag is the full height, as we only animate
- // the drag for the first half of the progress.
- anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
- positiveNegativeFactor * translateDistance * 2).setDuration(duration), LINEAR, sp);
+ anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
+ verticalFactor * secondaryTaskDimension * 2).setDuration(duration), LINEAR, sp);
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
&& taskView.isRunningTask()) {
@@ -2344,15 +2323,37 @@
}
/**
+ * Places an {@link FloatingTaskView} on top of the thumbnail for {@link #mSplitHiddenTaskView}
+ * and then animates it into the split position that was desired
+ */
+ private void createInitialSplitSelectAnimation(PendingAnimation anim) {
+ float placeholderHeight = getResources().getDimension(R.dimen.split_placeholder_size);
+ mOrientationHandler.getInitialSplitPlaceholderBounds((int) placeholderHeight,
+ mActivity.getDeviceProfile(),
+ mSplitSelectStateController.getActiveSplitPositionOption(), mTempRect);
+
+ RectF startingTaskRect = new RectF();
+ mSplitHiddenTaskView.setVisibility(INVISIBLE);
+ mFirstFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+ mSplitHiddenTaskView, startingTaskRect);
+ mFirstFloatingTaskView.setAlpha(1);
+ mFirstFloatingTaskView.addAnimation(anim, startingTaskRect,
+ mTempRect, mSplitHiddenTaskView, true /*fadeWithThumbnail*/);
+ }
+
+ /**
* Creates a {@link PendingAnimation} for dismissing the specified {@link TaskView}.
* @param dismissedTaskView the {@link TaskView} to be dismissed
* @param animateTaskView whether the {@link TaskView} to be dismissed should be animated
* @param shouldRemoveTask whether the associated {@link Task} should be removed from
* ActivityManager after dismissal
* @param duration duration of the animation
+ * @param dismissingForSplitSelection task dismiss animation is used for entering split
+ * selection state from app icon
*/
public PendingAnimation createTaskDismissAnimation(TaskView dismissedTaskView,
- boolean animateTaskView, boolean shouldRemoveTask, long duration) {
+ boolean animateTaskView, boolean shouldRemoveTask, long duration,
+ boolean dismissingForSplitSelection) {
if (mPendingAnimation != null) {
mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
}
@@ -2419,7 +2420,11 @@
View child = getChildAt(i);
if (child == dismissedTaskView) {
if (animateTaskView) {
- addDismissedTaskAnimations(dismissedTaskView, duration, anim);
+ if (dismissingForSplitSelection) {
+ createInitialSplitSelectAnimation(anim);
+ } else {
+ addDismissedTaskAnimations(dismissedTaskView, duration, anim);
+ }
}
} else if (!showAsGrid) {
// Compute scroll offsets from task dismissal for animation.
@@ -2602,7 +2607,7 @@
/**
* @return {@code true} if one of the task thumbnails would intersect/overlap with the
- * {@link #mSplitPlaceholderView}
+ * {@link #mFirstFloatingTaskView}
*/
public boolean shouldShiftThumbnailsForSplitSelect(@SplitConfigurationOptions.StagePosition
int stagePosition) {
@@ -2705,7 +2710,7 @@
public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
runDismissAnimation(createTaskDismissAnimation(taskView, animateTaskView, removeTask,
- DISMISS_TASK_DURATION));
+ DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/));
}
@SuppressWarnings("unused")
@@ -3107,8 +3112,11 @@
protected void setTaskViewsSecondarySplitTranslation(float translation) {
mTaskViewsSecondarySplitTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView task = getTaskViewAt(i);
- task.getSecondarySplitTranslationProperty().set(task, translation);
+ TaskView taskView = getTaskViewAt(i);
+ if (taskView == mSplitHiddenTaskView) {
+ continue;
+ }
+ taskView.getSecondarySplitTranslationProperty().set(taskView, translation);
}
}
@@ -3124,15 +3132,11 @@
public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
mSplitHiddenTaskView = taskView;
- SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
taskView.getBottom());
- splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
+ mSplitSelectStateController.setInitialTaskSelect(taskView,
+ splitPositionOption, initialBounds);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
- mSplitPlaceholderView.setLayoutParams(
- splitController.getLayoutParamsForActivePosition(getResources(),
- mActivity.getDeviceProfile()));
- mSplitPlaceholderView.setIcon(taskView.getIconView());
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
finishRecentsAnimation(true, null);
}
@@ -3140,17 +3144,48 @@
public PendingAnimation createSplitSelectInitAnimation() {
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
- return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration);
+ return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration,
+ true /* dismissingForSplitSelection*/);
}
public void confirmSplitSelect(TaskView taskView) {
- mSplitPlaceholderView.getSplitController().setSecondTaskId(taskView);
- resetTaskVisuals();
- setTranslationY(0);
+ RectF secondTaskStartingBounds = new RectF();
+ Rect secondTaskEndingBounds = new Rect();
+ // TODO(194414938) starting bounds seem slightly off, investigate
+ Rect firstTaskStartingBounds = new Rect();
+ Rect firstTaskEndingBounds = mTempRect;
+ int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
+ PendingAnimation pendingAnimation = new PendingAnimation(duration);
+
+ int halfDividerSize = getResources()
+ .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
+ mOrientationHandler.getFinalSplitPlaceholderBounds(halfDividerSize,
+ mActivity.getDeviceProfile(),
+ mSplitSelectStateController.getActiveSplitPositionOption(), firstTaskEndingBounds,
+ secondTaskEndingBounds);
+
+ mFirstFloatingTaskView.getBoundsOnScreen(firstTaskStartingBounds);
+ mFirstFloatingTaskView.addAnimation(pendingAnimation,
+ new RectF(firstTaskStartingBounds), firstTaskEndingBounds, mFirstFloatingTaskView,
+ false /*fadeWithThumbnail*/);
+
+ mSecondFloatingTaskView = FloatingTaskView.getFloatingTaskView(mActivity,
+ taskView, secondTaskStartingBounds);
+ mSecondFloatingTaskView.setAlpha(1);
+ mSecondFloatingTaskView.addAnimation(pendingAnimation, secondTaskStartingBounds,
+ secondTaskEndingBounds, taskView.getThumbnail(),
+ true /*fadeWithThumbnail*/);
+ pendingAnimation.addEndListener(aBoolean -> {
+ mSplitSelectStateController.setSecondTaskId(taskView);
+ resetFromSplitSelectionState();
+ });
+ mSecondSplitHiddenTaskView = taskView;
+ taskView.setVisibility(INVISIBLE);
+ pendingAnimation.buildAnim().start();
}
public PendingAnimation cancelSplitSelect(boolean animate) {
- SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+ SplitSelectStateController splitController = mSplitSelectStateController;
SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
Rect initialBounds = splitController.getInitialBounds();
splitController.resetState();
@@ -3263,8 +3298,19 @@
}
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
resetTaskVisuals();
+ mSplitHiddenTaskView.setVisibility(VISIBLE);
mSplitHiddenTaskView = null;
+ mSecondSplitHiddenTaskView.setVisibility(VISIBLE);
+ mSecondSplitHiddenTaskView = null;
mSplitHiddenTaskViewIndex = -1;
+ if (mFirstFloatingTaskView != null) {
+ mActivity.getRootView().removeView(mFirstFloatingTaskView);
+ mFirstFloatingTaskView = null;
+ }
+ if (mSecondFloatingTaskView != null) {
+ mActivity.getRootView().removeView(mSecondFloatingTaskView);
+ mSecondFloatingTaskView = null;
+ }
}
private void updateDeadZoneRects() {
diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
index bb8bc11..a712d1a 100644
--- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
+++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java
@@ -22,6 +22,8 @@
import android.view.Gravity;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
import com.android.quickstep.util.SplitSelectStateController;
public class SplitPlaceholderView extends FrameLayout {
@@ -55,6 +57,11 @@
return mSplitController;
}
+ @Nullable
+ public IconView getIcon() {
+ return mIcon;
+ }
+
public void setIcon(IconView icon) {
if (mIcon == null) {
mIcon = new IconView(getContext());
diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml
new file mode 100644
index 0000000..e184b91
--- /dev/null
+++ b/res/layout/floating_split_select_view.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.quickstep.views.FloatingTaskView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/thumbnail"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" />
+
+ <com.android.quickstep.views.SplitPlaceholderView
+ android:id="@+id/split_placeholder"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/split_placeholder_size"
+ android:background="@android:color/white"
+ android:visibility="gone" />
+
+</com.android.quickstep.views.FloatingTaskView>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 46570f0..dd27685 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -326,6 +326,7 @@
<dimen name="overview_task_margin">0dp</dimen>
<dimen name="overview_actions_bottom_margin_gesture">0dp</dimen>
<dimen name="overview_actions_bottom_margin_three_button">0dp</dimen>
+ <dimen name="split_placeholder_size">110dp</dimen>
<!-- Workspace grid visualization parameters -->
<dimen name="grid_visualization_rounding_radius">22dp</dimen>
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 3cabc87..7d818d2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -381,6 +381,21 @@
}
/**
+ * Similar to {@link #scaleRectAboutCenter(Rect, float)} except this allows different scales
+ * for X and Y
+ */
+ public static void scaleRectFAboutCenter(RectF r, float scaleX, float scaleY) {
+ float px = r.centerX();
+ float py = r.centerY();
+ r.offset(-px, -py);
+ r.left = r.left * scaleX;
+ r.top = r.top * scaleY;
+ r.right = r.right * scaleX;
+ r.bottom = r.bottom * scaleY;
+ r.offset(px, py);
+ }
+
+ /**
* Maps t from one range to another range.
* @param t The value to map.
* @param fromMin The lower bound of the range that t is being mapped from.
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 01f7de6..3ab893b 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -56,6 +56,10 @@
mAnim = new AnimatorSet();
}
+ public long getDuration() {
+ return mDuration;
+ }
+
/**
* Utility method to sent an interpolator on an animation and add it to the list
*/
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index d047eca..816e5dc 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -205,6 +205,16 @@
}
@Override
+ public void setPrimaryScale(View view, float scale) {
+ view.setScaleY(scale);
+ }
+
+ @Override
+ public void setSecondaryScale(View view, float scale) {
+ view.setScaleX(scale);
+ }
+
+ @Override
public int getChildStart(View view) {
return view.getTop();
}
@@ -353,6 +363,25 @@
}
@Override
+ public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
+ SplitPositionOption splitPositionOption, Rect out) {
+ // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
+ // which is the same bounds as 0 rotation.
+ int width = dp.widthPx;
+ out.set(0, 0, width, placeholderHeight);
+ }
+
+ @Override
+ public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
+ SplitPositionOption initialSplitOption, Rect out1, Rect out2) {
+ // In fake land/seascape, the window bounds are always top and bottom half
+ int screenHeight = dp.heightPx;
+ int screenWidth = dp.widthPx;
+ out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize);
+ out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight);
+ }
+
+ @Override
public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
DeviceProfile deviceProfile) {
return primary;
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 266e05f..dae2dde 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -99,6 +99,8 @@
boolean getRecentsRtlSetting(Resources resources);
float getDegreesRotated();
int getRotation();
+ void setPrimaryScale(View view, float scale);
+ void setSecondaryScale(View view, float scale);
<T> T getPrimaryValue(T x, T y);
<T> T getSecondaryValue(T x, T y);
@@ -114,6 +116,22 @@
DeviceProfile deviceProfile);
int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
+ /**
+ * @param splitholderSize height of placeholder view in portrait, width in landscape
+ */
+ void getInitialSplitPlaceholderBounds(int splitholderSize, DeviceProfile dp,
+ SplitPositionOption splitPositionOption, Rect out);
+
+ /**
+ * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
+ * @param initialSplitOption the split position option (top/left, bottom/right) of the first
+ * task selected for entering split
+ * @param out1 the bounds for where the first selected app will be
+ * @param out2 the bounds for where the second selected app will be, complimentary to
+ * {@param out1} based on {@param initialSplitOption}
+ */
+ void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
+ SplitPositionOption initialSplitOption, Rect out1, Rect out2);
// Overview TaskMenuView methods
float getTaskMenuX(float x, View thumbnailView, int overScroll);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index dd97af5..1253589 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
import android.content.res.Resources;
+import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -47,6 +48,9 @@
public class PortraitPagedViewHandler implements PagedOrientationHandler {
+ private final Matrix mTmpMatrix = new Matrix();
+ private final RectF mTmpRectF = new RectF();
+
@Override
public <T> T getPrimaryValue(T x, T y) {
return x;
@@ -207,6 +211,16 @@
}
@Override
+ public void setPrimaryScale(View view, float scale) {
+ view.setScaleX(scale);
+ }
+
+ @Override
+ public void setSecondaryScale(View view, float scale) {
+ view.setScaleY(scale);
+ }
+
+ @Override
public int getChildStart(View view) {
return view.getLeft();
}
@@ -398,6 +412,62 @@
}
@Override
+ public void getInitialSplitPlaceholderBounds(int placeholderHeight, DeviceProfile dp,
+ SplitPositionOption splitPositionOption, Rect out) {
+ int width = dp.widthPx;
+ out.set(0, 0, width, placeholderHeight);
+ if (!dp.isLandscape) {
+ // portrait, phone or tablet - spans width of screen, nothing else to do
+ return;
+ }
+
+ // Now we rotate the portrait rect depending on what side we want pinned
+ boolean pinToRight = splitPositionOption.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+
+ int screenHeight = dp.heightPx;
+ float postRotateScale = (float) screenHeight / width;
+ mTmpMatrix.reset();
+ mTmpMatrix.postRotate(pinToRight ? 90 : 270);
+ mTmpMatrix.postTranslate(pinToRight ? width : 0, pinToRight ? 0 : width);
+ // The placeholder height stays constant after rotation, so we don't change width scale
+ mTmpMatrix.postScale(1, postRotateScale);
+
+ mTmpRectF.set(out);
+ mTmpMatrix.mapRect(mTmpRectF);
+ mTmpRectF.roundOut(out);
+ }
+
+ @Override
+ public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
+ SplitPositionOption initialSplitOption, Rect out1, Rect out2) {
+ int screenHeight = dp.heightPx;
+ int screenWidth = dp.widthPx;
+ out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize);
+ out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight);
+ if (!dp.isLandscape) {
+ // Portrait - the window bounds are always top and bottom half
+ return;
+ }
+
+ // Now we rotate the portrait rect depending on what side we want pinned
+ boolean pinToRight = initialSplitOption.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
+ float postRotateScale = (float) screenHeight / screenWidth;
+
+ mTmpMatrix.reset();
+ mTmpMatrix.postRotate(pinToRight ? 90 : 270);
+ mTmpMatrix.postTranslate(pinToRight ? screenHeight : 0, pinToRight ? 0 : screenWidth);
+ mTmpMatrix.postScale(1 / postRotateScale, postRotateScale);
+
+ mTmpRectF.set(out1);
+ mTmpMatrix.mapRect(mTmpRectF);
+ mTmpRectF.roundOut(out1);
+
+ mTmpRectF.set(out2);
+ mTmpMatrix.mapRect(mTmpRectF);
+ mTmpRectF.roundOut(out2);
+ }
+
+ @Override
public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary,
DeviceProfile dp) {
if (dp.isLandscape) { // or seascape