Flexible 2-app split: Pinned Taskbar

Adds the ability for DividerSnapAlgorithm to recalculate when Taskbar is pinned or unpinned. The divider will animate appropriately, if needed.

Also cleans up and adds a specific SNAP_MODE for flexible split (no longer shares a SNAP_MODE with SNAP_FIXED_RATIO).

Bug: 349828130
Test: When flag is off, behavior is unchanged and continues to pass tests
Flag: com.android.wm.shell.enable_flexible_two_app_split
Change-Id: I1a3276b23acd7c2d22df1388004abd3a60a6d10f
This commit is contained in:
Jeremy Sim
2024-10-29 17:18:49 -07:00
parent 428aef4059
commit 3029745805
2 changed files with 97 additions and 28 deletions

View File

@@ -71,6 +71,11 @@ public class DividerSnapAlgorithm {
*/
private static final int SNAP_MODE_MINIMIZED = 3;
/**
* A mode where apps can be "flexibly offscreen" on smaller displays.
*/
private static final int SNAP_FLEXIBLE_SPLIT = 4;
private final float mMinFlingVelocityPxPerSecond;
private final float mMinDismissVelocityPxPerSecond;
private final int mDisplayWidth;
@@ -78,6 +83,7 @@ public class DividerSnapAlgorithm {
private final int mDividerSize;
private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
private final Rect mInsets = new Rect();
private final Rect mPinnedTaskbarInsets = new Rect();
private final int mSnapMode;
private final boolean mFreeSnapMode;
private final int mMinimalSizeResizableTask;
@@ -88,6 +94,8 @@ public class DividerSnapAlgorithm {
/** Allows split ratios that go offscreen (a.k.a. "flexible split") */
private final boolean mAllowOffscreenRatios;
private final boolean mIsLeftRightSplit;
/** In SNAP_MODE_MINIMIZED, the side of the screen on which an app will "dock" when minimized */
private final int mDockSide;
/** The first target which is still splitting the screen */
private final SnapTarget mFirstSplitTarget;
@@ -101,14 +109,14 @@ public class DividerSnapAlgorithm {
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
boolean isLeftRightSplit, Rect insets, int dockSide) {
boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide) {
this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets,
dockSide, false /* minimized */, true /* resizable */);
pinnedTaskbarInsets, dockSide, false /* minimized */, true /* resizable */);
}
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode,
boolean isHomeResizable) {
boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide,
boolean isMinimizedMode, boolean isHomeResizable) {
mMinFlingVelocityPxPerSecond =
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
mMinDismissVelocityPxPerSecond =
@@ -117,10 +125,11 @@ public class DividerSnapAlgorithm {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
mIsLeftRightSplit = isLeftRightSplit;
mDockSide = dockSide;
mInsets.set(insets);
mPinnedTaskbarInsets.set(pinnedTaskbarInsets);
if (Flags.enableFlexibleTwoAppSplit()) {
// In flexible split, we always use a fixed ratio (50%, 66%, or 90%) for splitting
mSnapMode = SNAP_FIXED_RATIO;
mSnapMode = SNAP_FLEXIBLE_SPLIT;
} else {
// Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config
mSnapMode = isMinimizedMode
@@ -140,7 +149,7 @@ public class DividerSnapAlgorithm {
mAllowOffscreenRatios = SplitScreenUtils.allowOffscreenRatios(res);
mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
calculateTargets(isLeftRightSplit, dockSide);
calculateTargets();
mFirstSplitTarget = mTargets.get(1);
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
mDismissStartTarget = mTargets.get(0);
@@ -276,28 +285,31 @@ public class DividerSnapAlgorithm {
return mTargets.get(minIndex);
}
private void calculateTargets(boolean isLeftRightSplit, int dockedSide) {
private void calculateTargets() {
mTargets.clear();
int dividerMax = isLeftRightSplit
int dividerMax = mIsLeftRightSplit
? mDisplayWidth
: mDisplayHeight;
int startPos = -mDividerSize;
if (dockedSide == DOCKED_RIGHT) {
if (mDockSide == DOCKED_RIGHT) {
startPos += mInsets.left;
}
mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
switch (mSnapMode) {
case SNAP_MODE_16_9:
addRatio16_9Targets(isLeftRightSplit, dividerMax);
addRatio16_9Targets(mIsLeftRightSplit, dividerMax);
break;
case SNAP_FIXED_RATIO:
addFixedDivisionTargets(isLeftRightSplit, dividerMax);
addFixedDivisionTargets(mIsLeftRightSplit, dividerMax);
break;
case SNAP_ONLY_1_1:
addMiddleTarget(isLeftRightSplit);
addMiddleTarget(mIsLeftRightSplit);
break;
case SNAP_MODE_MINIMIZED:
addMinimizedTarget(isLeftRightSplit, dockedSide);
addMinimizedTarget(mIsLeftRightSplit, mDockSide);
break;
case SNAP_FLEXIBLE_SPLIT:
addFlexSplitTargets(mIsLeftRightSplit, dividerMax);
break;
}
mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
@@ -321,18 +333,9 @@ public class DividerSnapAlgorithm {
? mDisplayWidth - mInsets.right
: mDisplayHeight - mInsets.bottom;
int size;
if (Flags.enableFlexibleTwoAppSplit()) {
float ratio = areOffscreenRatiosSupported()
? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
: SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
size = (int) (ratio * (end - start)) - mDividerSize / 2;
} else {
size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
if (mCalculateRatiosBasedOnAvailableSpace) {
size = Math.max(size, mMinimalSizeResizableTask);
}
int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
if (mCalculateRatiosBasedOnAvailableSpace) {
size = Math.max(size, mMinimalSizeResizableTask);
}
int topPosition = start + size;
@@ -340,6 +343,24 @@ public class DividerSnapAlgorithm {
addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
}
private void addFlexSplitTargets(boolean isLeftRightSplit, int dividerMax) {
int start = 0;
int end = isLeftRightSplit ? mDisplayWidth : mDisplayHeight;
int pinnedTaskbarShiftStart = isLeftRightSplit
? mPinnedTaskbarInsets.left : mPinnedTaskbarInsets.top;
int pinnedTaskbarShiftEnd = isLeftRightSplit
? mPinnedTaskbarInsets.right : mPinnedTaskbarInsets.bottom;
float ratio = areOffscreenRatiosSupported()
? SplitLayout.OFFSCREEN_ASYMMETRIC_RATIO
: SplitLayout.ONSCREEN_ONLY_ASYMMETRIC_RATIO;
int size = (int) (ratio * (end - start)) - mDividerSize / 2;
int leftTopPosition = start + pinnedTaskbarShiftStart + size;
int rightBottomPosition = end - pinnedTaskbarShiftEnd - size - mDividerSize;
addNonDismissingTargets(isLeftRightSplit, leftTopPosition, rightBottomPosition, dividerMax);
}
private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
int start = isLeftRightSplit ? mInsets.left : mInsets.top;
int end = isLeftRightSplit

View File

@@ -49,10 +49,13 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.view.Display;
import android.view.InsetsController;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.RoundedCorner;
@@ -153,6 +156,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private final ResizingEffectPolicy mSurfaceEffectPolicy;
private final ShellTaskOrganizer mTaskOrganizer;
private final InsetsState mInsetsState = new InsetsState();
private Insets mPinnedTaskbarInsets = Insets.NONE;
private Context mContext;
@VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
@@ -523,6 +527,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
@Override
public void insetsChanged(InsetsState insetsState) {
mInsetsState.set(insetsState);
if (!mInitialized) {
return;
}
@@ -531,9 +536,51 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// flicker.
return;
}
// Check to see if insets changed in such a way that the divider algorithm needs to be
// recalculated.
Insets pinnedTaskbarInsets = calculatePinnedTaskbarInsets(insetsState);
if (!mPinnedTaskbarInsets.equals(pinnedTaskbarInsets)) {
mPinnedTaskbarInsets = pinnedTaskbarInsets;
// Refresh the DividerSnapAlgorithm.
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
// If the divider is no longer placed on a snap point, animate it to the nearest one.
DividerSnapAlgorithm.SnapTarget snapTarget =
findSnapTarget(mDividerPosition, 0, false /* hardDismiss */);
if (snapTarget.position != mDividerPosition) {
snapToTarget(mDividerPosition, snapTarget,
InsetsController.ANIMATION_DURATION_RESIZE,
InsetsController.RESIZE_INTERPOLATOR);
}
}
mSplitWindowManager.onInsetsChanged(insetsState);
}
/**
* Calculates the insets that might trigger a divider algorithm recalculation. Currently, only
* pinned Taskbar does this, and only when the IME is not showing.
*/
private Insets calculatePinnedTaskbarInsets(InsetsState insetsState) {
if (insetsState.isSourceOrDefaultVisible(InsetsSource.ID_IME, WindowInsets.Type.ime())) {
return Insets.NONE;
}
// If IME is not showing...
for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
final InsetsSource source = insetsState.sourceAt(i);
// and Taskbar is pinned...
if (source.getType() == WindowInsets.Type.navigationBars()
&& source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
// Return Insets representing the pinned taskbar state.
return source.calculateVisibleInsets(mRootBounds);
}
}
// Else, divider can calculate based on the full display.
return Insets.NONE;
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
@@ -631,8 +678,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
/**
* Same as {@link #snapToTarget(int, SnapTarget)}, with default animation duration and
* interpolator.
* Same as {@link #snapToTarget(int, SnapTarget, int, Interpolator)}, with default animation
* duration and interpolator.
*/
public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
snapToTarget(currentPosition, snapTarget, FLING_RESIZE_DURATION,
@@ -683,6 +730,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mDividerSize,
mIsLeftRightSplit,
insets,
mPinnedTaskbarInsets.toRect(),
mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
}