summaryrefslogtreecommitdiff
path: root/libs/WindowManager
diff options
context:
space:
mode:
author Winson Chung <winsonc@google.com> 2023-08-10 23:19:34 +0000
committer Winson Chung <winsonc@google.com> 2023-08-10 23:19:34 +0000
commit7af74e7d329c42b6087fc5d2c41339cd11cffa80 (patch)
tree0d576976930fe3f7b400ef949ac1a5ed53da2967 /libs/WindowManager
parent55517baec05791926b9ce04f5f9fb83a53b66d16 (diff)
Move split classes back into Shell
- These classes were previously used in WM and SysUI, but now only referenced by Shell code Bug: 283146235 Test: Builds Change-Id: I3915d6695272e5c531729dd663513444d2f77439
Diffstat (limited to 'libs/WindowManager')
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java476
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java140
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java1
5 files changed, 618 insertions, 6 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
new file mode 100644
index 000000000000..1901e0bbe700
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.common.split;
+
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Calculates the snap targets and the snap position given a position and a velocity. All positions
+ * here are to be interpreted as the left/top edge of the divider rectangle.
+ *
+ * @hide
+ */
+public class DividerSnapAlgorithm {
+
+ private static final int MIN_FLING_VELOCITY_DP_PER_SECOND = 400;
+ private static final int MIN_DISMISS_VELOCITY_DP_PER_SECOND = 600;
+
+ /**
+ * 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio
+ */
+ private static final int SNAP_MODE_16_9 = 0;
+
+ /**
+ * 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
+ */
+ private static final int SNAP_FIXED_RATIO = 1;
+
+ /**
+ * 1 snap target: 1:1
+ */
+ private static final int SNAP_ONLY_1_1 = 2;
+
+ /**
+ * 1 snap target: minimized height, (1 - minimized height)
+ */
+ private static final int SNAP_MODE_MINIMIZED = 3;
+
+ private final float mMinFlingVelocityPxPerSecond;
+ private final float mMinDismissVelocityPxPerSecond;
+ private final int mDisplayWidth;
+ private final int mDisplayHeight;
+ private final int mDividerSize;
+ private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
+ private final Rect mInsets = new Rect();
+ private final int mSnapMode;
+ private final boolean mFreeSnapMode;
+ private final int mMinimalSizeResizableTask;
+ private final int mTaskHeightInMinimizedMode;
+ private final float mFixedRatio;
+ private boolean mIsHorizontalDivision;
+
+ /** The first target which is still splitting the screen */
+ private final SnapTarget mFirstSplitTarget;
+
+ /** The last target which is still splitting the screen */
+ private final SnapTarget mLastSplitTarget;
+
+ private final SnapTarget mDismissStartTarget;
+ private final SnapTarget mDismissEndTarget;
+ private final SnapTarget mMiddleTarget;
+
+ public static DividerSnapAlgorithm create(Context ctx, Rect insets) {
+ DisplayInfo displayInfo = new DisplayInfo();
+ ctx.getSystemService(DisplayManager.class).getDisplay(
+ Display.DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
+ int dividerWindowWidth = ctx.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ int dividerInsets = ctx.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_insets);
+ return new DividerSnapAlgorithm(ctx.getResources(),
+ displayInfo.logicalWidth, displayInfo.logicalHeight,
+ dividerWindowWidth - 2 * dividerInsets,
+ ctx.getApplicationContext().getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT,
+ insets);
+ }
+
+ public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
+ boolean isHorizontalDivision, Rect insets) {
+ this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
+ DOCKED_INVALID, false /* minimized */, true /* resizable */);
+ }
+
+ public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
+ boolean isHorizontalDivision, Rect insets, int dockSide) {
+ this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets,
+ dockSide, false /* minimized */, true /* resizable */);
+ }
+
+ public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
+ boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode,
+ boolean isHomeResizable) {
+ mMinFlingVelocityPxPerSecond =
+ MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
+ mMinDismissVelocityPxPerSecond =
+ MIN_DISMISS_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
+ mDividerSize = dividerSize;
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ mIsHorizontalDivision = isHorizontalDivision;
+ mInsets.set(insets);
+ mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED :
+ res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode);
+ mFreeSnapMode = res.getBoolean(
+ com.android.internal.R.bool.config_dockedStackDividerFreeSnapMode);
+ mFixedRatio = res.getFraction(
+ com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
+ mMinimalSizeResizableTask = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.default_minimal_size_resizable_task);
+ mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
+ com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
+ calculateTargets(isHorizontalDivision, dockSide);
+ mFirstSplitTarget = mTargets.get(1);
+ mLastSplitTarget = mTargets.get(mTargets.size() - 2);
+ mDismissStartTarget = mTargets.get(0);
+ mDismissEndTarget = mTargets.get(mTargets.size() - 1);
+ mMiddleTarget = mTargets.get(mTargets.size() / 2);
+ mMiddleTarget.isMiddleTarget = true;
+ }
+
+ /**
+ * @return whether it's feasible to enable split screen in the current configuration, i.e. when
+ * snapping in the middle both tasks are larger than the minimal task size.
+ */
+ public boolean isSplitScreenFeasible() {
+ int statusBarSize = mInsets.top;
+ int navBarSize = mIsHorizontalDivision ? mInsets.bottom : mInsets.right;
+ int size = mIsHorizontalDivision
+ ? mDisplayHeight
+ : mDisplayWidth;
+ int availableSpace = size - navBarSize - statusBarSize - mDividerSize;
+ return availableSpace / 2 >= mMinimalSizeResizableTask;
+ }
+
+ public SnapTarget calculateSnapTarget(int position, float velocity) {
+ return calculateSnapTarget(position, velocity, true /* hardDismiss */);
+ }
+
+ /**
+ * @param position the top/left position of the divider
+ * @param velocity current dragging velocity
+ * @param hardDismiss if set, make it a bit harder to get reach the dismiss targets
+ */
+ public SnapTarget calculateSnapTarget(int position, float velocity, boolean hardDismiss) {
+ if (position < mFirstSplitTarget.position && velocity < -mMinDismissVelocityPxPerSecond) {
+ return mDismissStartTarget;
+ }
+ if (position > mLastSplitTarget.position && velocity > mMinDismissVelocityPxPerSecond) {
+ return mDismissEndTarget;
+ }
+ if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) {
+ return snap(position, hardDismiss);
+ }
+ if (velocity < 0) {
+ return mFirstSplitTarget;
+ } else {
+ return mLastSplitTarget;
+ }
+ }
+
+ public SnapTarget calculateNonDismissingSnapTarget(int position) {
+ SnapTarget target = snap(position, false /* hardDismiss */);
+ if (target == mDismissStartTarget) {
+ return mFirstSplitTarget;
+ } else if (target == mDismissEndTarget) {
+ return mLastSplitTarget;
+ } else {
+ return target;
+ }
+ }
+
+ public float calculateDismissingFraction(int position) {
+ if (position < mFirstSplitTarget.position) {
+ return 1f - (float) (position - getStartInset())
+ / (mFirstSplitTarget.position - getStartInset());
+ } else if (position > mLastSplitTarget.position) {
+ return (float) (position - mLastSplitTarget.position)
+ / (mDismissEndTarget.position - mLastSplitTarget.position - mDividerSize);
+ }
+ return 0f;
+ }
+
+ public SnapTarget getClosestDismissTarget(int position) {
+ if (position < mFirstSplitTarget.position) {
+ return mDismissStartTarget;
+ } else if (position > mLastSplitTarget.position) {
+ return mDismissEndTarget;
+ } else if (position - mDismissStartTarget.position
+ < mDismissEndTarget.position - position) {
+ return mDismissStartTarget;
+ } else {
+ return mDismissEndTarget;
+ }
+ }
+
+ public SnapTarget getFirstSplitTarget() {
+ return mFirstSplitTarget;
+ }
+
+ public SnapTarget getLastSplitTarget() {
+ return mLastSplitTarget;
+ }
+
+ public SnapTarget getDismissStartTarget() {
+ return mDismissStartTarget;
+ }
+
+ public SnapTarget getDismissEndTarget() {
+ return mDismissEndTarget;
+ }
+
+ private int getStartInset() {
+ if (mIsHorizontalDivision) {
+ return mInsets.top;
+ } else {
+ return mInsets.left;
+ }
+ }
+
+ private int getEndInset() {
+ if (mIsHorizontalDivision) {
+ return mInsets.bottom;
+ } else {
+ return mInsets.right;
+ }
+ }
+
+ private boolean shouldApplyFreeSnapMode(int position) {
+ if (!mFreeSnapMode) {
+ return false;
+ }
+ if (!isFirstSplitTargetAvailable() || !isLastSplitTargetAvailable()) {
+ return false;
+ }
+ return mFirstSplitTarget.position < position && position < mLastSplitTarget.position;
+ }
+
+ private SnapTarget snap(int position, boolean hardDismiss) {
+ if (shouldApplyFreeSnapMode(position)) {
+ return new SnapTarget(position, position, SnapTarget.FLAG_NONE);
+ }
+ int minIndex = -1;
+ float minDistance = Float.MAX_VALUE;
+ int size = mTargets.size();
+ for (int i = 0; i < size; i++) {
+ SnapTarget target = mTargets.get(i);
+ float distance = Math.abs(position - target.position);
+ if (hardDismiss) {
+ distance /= target.distanceMultiplier;
+ }
+ if (distance < minDistance) {
+ minIndex = i;
+ minDistance = distance;
+ }
+ }
+ return mTargets.get(minIndex);
+ }
+
+ private void calculateTargets(boolean isHorizontalDivision, int dockedSide) {
+ mTargets.clear();
+ int dividerMax = isHorizontalDivision
+ ? mDisplayHeight
+ : mDisplayWidth;
+ int startPos = -mDividerSize;
+ if (dockedSide == DOCKED_RIGHT) {
+ startPos += mInsets.left;
+ }
+ mTargets.add(new SnapTarget(startPos, startPos, SnapTarget.FLAG_DISMISS_START,
+ 0.35f));
+ switch (mSnapMode) {
+ case SNAP_MODE_16_9:
+ addRatio16_9Targets(isHorizontalDivision, dividerMax);
+ break;
+ case SNAP_FIXED_RATIO:
+ addFixedDivisionTargets(isHorizontalDivision, dividerMax);
+ break;
+ case SNAP_ONLY_1_1:
+ addMiddleTarget(isHorizontalDivision);
+ break;
+ case SNAP_MODE_MINIMIZED:
+ addMinimizedTarget(isHorizontalDivision, dockedSide);
+ break;
+ }
+ mTargets.add(new SnapTarget(dividerMax, dividerMax, SnapTarget.FLAG_DISMISS_END, 0.35f));
+ }
+
+ private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition,
+ int bottomPosition, int dividerMax) {
+ maybeAddTarget(topPosition, topPosition - getStartInset());
+ addMiddleTarget(isHorizontalDivision);
+ maybeAddTarget(bottomPosition,
+ dividerMax - getEndInset() - (bottomPosition + mDividerSize));
+ }
+
+ private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) {
+ int start = isHorizontalDivision ? mInsets.top : mInsets.left;
+ int end = isHorizontalDivision
+ ? mDisplayHeight - mInsets.bottom
+ : mDisplayWidth - mInsets.right;
+ int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
+ int topPosition = start + size;
+ int bottomPosition = end - size - mDividerSize;
+ addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
+ }
+
+ private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) {
+ int start = isHorizontalDivision ? mInsets.top : mInsets.left;
+ int end = isHorizontalDivision
+ ? mDisplayHeight - mInsets.bottom
+ : mDisplayWidth - mInsets.right;
+ int startOther = isHorizontalDivision ? mInsets.left : mInsets.top;
+ int endOther = isHorizontalDivision
+ ? mDisplayWidth - mInsets.right
+ : mDisplayHeight - mInsets.bottom;
+ float size = 9.0f / 16.0f * (endOther - startOther);
+ int sizeInt = (int) Math.floor(size);
+ int topPosition = start + sizeInt;
+ int bottomPosition = end - sizeInt - mDividerSize;
+ addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax);
+ }
+
+ /**
+ * Adds a target at {@param position} but only if the area with size of {@param smallerSize}
+ * meets the minimal size requirement.
+ */
+ private void maybeAddTarget(int position, int smallerSize) {
+ if (smallerSize >= mMinimalSizeResizableTask) {
+ mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ }
+ }
+
+ private void addMiddleTarget(boolean isHorizontalDivision) {
+ int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision,
+ mInsets, mDisplayWidth, mDisplayHeight, mDividerSize);
+ mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ }
+
+ private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) {
+ // In portrait offset the position by the statusbar height, in landscape add the statusbar
+ // height as well to match portrait offset
+ int position = mTaskHeightInMinimizedMode + mInsets.top;
+ if (!isHorizontalDivision) {
+ if (dockedSide == DOCKED_LEFT) {
+ position += mInsets.left;
+ } else if (dockedSide == DOCKED_RIGHT) {
+ position = mDisplayWidth - position - mInsets.right - mDividerSize;
+ }
+ }
+ mTargets.add(new SnapTarget(position, position, SnapTarget.FLAG_NONE));
+ }
+
+ public SnapTarget getMiddleTarget() {
+ return mMiddleTarget;
+ }
+
+ public SnapTarget getNextTarget(SnapTarget snapTarget) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1 && index < mTargets.size() - 1) {
+ return mTargets.get(index + 1);
+ }
+ return snapTarget;
+ }
+
+ public SnapTarget getPreviousTarget(SnapTarget snapTarget) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1 && index > 0) {
+ return mTargets.get(index - 1);
+ }
+ return snapTarget;
+ }
+
+ /**
+ * @return whether or not there are more than 1 split targets that do not include the two
+ * dismiss targets, used in deciding to display the middle target for accessibility
+ */
+ public boolean showMiddleSplitTargetForAccessibility() {
+ return (mTargets.size() - 2) > 1;
+ }
+
+ public boolean isFirstSplitTargetAvailable() {
+ return mFirstSplitTarget != mMiddleTarget;
+ }
+
+ public boolean isLastSplitTargetAvailable() {
+ return mLastSplitTarget != mMiddleTarget;
+ }
+
+ /**
+ * Cycles through all non-dismiss targets with a stepping of {@param increment}. It moves left
+ * if {@param increment} is negative and moves right otherwise.
+ */
+ public SnapTarget cycleNonDismissTarget(SnapTarget snapTarget, int increment) {
+ int index = mTargets.indexOf(snapTarget);
+ if (index != -1) {
+ SnapTarget newTarget = mTargets.get((index + mTargets.size() + increment)
+ % mTargets.size());
+ if (newTarget == mDismissStartTarget) {
+ return mLastSplitTarget;
+ } else if (newTarget == mDismissEndTarget) {
+ return mFirstSplitTarget;
+ } else {
+ return newTarget;
+ }
+ }
+ return snapTarget;
+ }
+
+ /**
+ * Represents a snap target for the divider.
+ */
+ public static class SnapTarget {
+ public static final int FLAG_NONE = 0;
+
+ /** If the divider reaches this value, the left/top task should be dismissed. */
+ public static final int FLAG_DISMISS_START = 1;
+
+ /** If the divider reaches this value, the right/bottom task should be dismissed */
+ public static final int FLAG_DISMISS_END = 2;
+
+ /** Position of this snap target. The right/bottom edge of the top/left task snaps here. */
+ public final int position;
+
+ /**
+ * Like {@link #position}, but used to calculate the task bounds which might be different
+ * from the stack bounds.
+ */
+ public final int taskPosition;
+
+ public final int flag;
+
+ public boolean isMiddleTarget;
+
+ /**
+ * Multiplier used to calculate distance to snap position. The lower this value, the harder
+ * it's to snap on this target
+ */
+ private final float distanceMultiplier;
+
+ public SnapTarget(int position, int taskPosition, int flag) {
+ this(position, taskPosition, flag, 1f);
+ }
+
+ public SnapTarget(int position, int taskPosition, int flag, float distanceMultiplier) {
+ this.position = position;
+ this.taskPosition = taskPosition;
+ this.flag = flag;
+ this.distanceMultiplier = distanceMultiplier;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 2dbc4445d606..0b0c6937553b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -53,7 +53,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
new file mode 100644
index 000000000000..f25dfeafb32c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 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.wm.shell.common.split;
+
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.content.res.Resources;
+import android.graphics.Rect;
+
+/**
+ * Utility functions for docked stack divider used by both window manager and System UI.
+ *
+ * @hide
+ */
+public class DockedDividerUtils {
+
+ public static void calculateBoundsForPosition(int position, int dockSide, Rect outRect,
+ int displayWidth, int displayHeight, int dividerSize) {
+ outRect.set(0, 0, displayWidth, displayHeight);
+ switch (dockSide) {
+ case DOCKED_LEFT:
+ outRect.right = position;
+ break;
+ case DOCKED_TOP:
+ outRect.bottom = position;
+ break;
+ case DOCKED_RIGHT:
+ outRect.left = position + dividerSize;
+ break;
+ case DOCKED_BOTTOM:
+ outRect.top = position + dividerSize;
+ break;
+ }
+ sanitizeStackBounds(outRect, dockSide == DOCKED_LEFT || dockSide == DOCKED_TOP);
+ }
+
+ /**
+ * Makes sure that the bounds are always valid, i. e. they are at least one pixel high and wide.
+ *
+ * @param bounds The bounds to sanitize.
+ * @param topLeft Pass true if the bounds are at the top/left of the screen, false if they are
+ * at the bottom/right. This is used to determine in which direction to extend
+ * the bounds.
+ */
+ public static void sanitizeStackBounds(Rect bounds, boolean topLeft) {
+
+ // If the bounds are either on the top or left of the screen, rather move it further to the
+ // left/top to make it more offscreen. If they are on the bottom or right, push them off the
+ // screen by moving it even more to the bottom/right.
+ if (topLeft) {
+ if (bounds.left >= bounds.right) {
+ bounds.left = bounds.right - 1;
+ }
+ if (bounds.top >= bounds.bottom) {
+ bounds.top = bounds.bottom - 1;
+ }
+ } else {
+ if (bounds.right <= bounds.left) {
+ bounds.right = bounds.left + 1;
+ }
+ if (bounds.bottom <= bounds.top) {
+ bounds.bottom = bounds.top + 1;
+ }
+ }
+ }
+
+ public static int calculatePositionForBounds(Rect bounds, int dockSide, int dividerSize) {
+ switch (dockSide) {
+ case DOCKED_LEFT:
+ return bounds.right;
+ case DOCKED_TOP:
+ return bounds.bottom;
+ case DOCKED_RIGHT:
+ return bounds.left - dividerSize;
+ case DOCKED_BOTTOM:
+ return bounds.top - dividerSize;
+ default:
+ return 0;
+ }
+ }
+
+ public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets,
+ int displayWidth, int displayHeight, int dividerSize) {
+ int start = isHorizontalDivision ? insets.top : insets.left;
+ int end = isHorizontalDivision
+ ? displayHeight - insets.bottom
+ : displayWidth - insets.right;
+ return start + (end - start) / 2 - dividerSize / 2;
+ }
+
+ public static int invertDockSide(int dockSide) {
+ switch (dockSide) {
+ case DOCKED_LEFT:
+ return DOCKED_RIGHT;
+ case DOCKED_TOP:
+ return DOCKED_BOTTOM;
+ case DOCKED_RIGHT:
+ return DOCKED_LEFT;
+ case DOCKED_BOTTOM:
+ return DOCKED_TOP;
+ default:
+ return DOCKED_INVALID;
+ }
+ }
+
+ /** Returns the inset distance from the divider window edge to the dividerview. */
+ public static int getDividerInsets(Resources res) {
+ return res.getDimensionPixelSize(com.android.internal.R.dimen.docked_stack_divider_insets);
+ }
+
+ /** Returns the size of the divider */
+ public static int getDividerSize(Resources res, int dividerInsets) {
+ final int windowWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ return windowWidth - 2 * dividerInsets;
+ }
+
+ /** Returns the docked-stack side */
+ public static int getDockSide(int displayWidth, int displayHeight) {
+ return displayWidth > displayHeight ? DOCKED_LEFT : DOCKED_TOP;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index e8fa638bea31..33b6c2bb4c9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -25,8 +25,8 @@ import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
-import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
-import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
+import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
+import static com.android.wm.shell.common.split.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -58,8 +58,6 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.internal.policy.DockedDividerUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.animation.Interpolators;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 443cea245a4f..fe2da5dd19fc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -38,7 +38,6 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;