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:
@@ -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
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user