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;
|
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 mMinFlingVelocityPxPerSecond;
|
||||||
private final float mMinDismissVelocityPxPerSecond;
|
private final float mMinDismissVelocityPxPerSecond;
|
||||||
private final int mDisplayWidth;
|
private final int mDisplayWidth;
|
||||||
@@ -78,6 +83,7 @@ public class DividerSnapAlgorithm {
|
|||||||
private final int mDividerSize;
|
private final int mDividerSize;
|
||||||
private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
|
private final ArrayList<SnapTarget> mTargets = new ArrayList<>();
|
||||||
private final Rect mInsets = new Rect();
|
private final Rect mInsets = new Rect();
|
||||||
|
private final Rect mPinnedTaskbarInsets = new Rect();
|
||||||
private final int mSnapMode;
|
private final int mSnapMode;
|
||||||
private final boolean mFreeSnapMode;
|
private final boolean mFreeSnapMode;
|
||||||
private final int mMinimalSizeResizableTask;
|
private final int mMinimalSizeResizableTask;
|
||||||
@@ -88,6 +94,8 @@ public class DividerSnapAlgorithm {
|
|||||||
/** Allows split ratios that go offscreen (a.k.a. "flexible split") */
|
/** Allows split ratios that go offscreen (a.k.a. "flexible split") */
|
||||||
private final boolean mAllowOffscreenRatios;
|
private final boolean mAllowOffscreenRatios;
|
||||||
private final boolean mIsLeftRightSplit;
|
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 */
|
/** The first target which is still splitting the screen */
|
||||||
private final SnapTarget mFirstSplitTarget;
|
private final SnapTarget mFirstSplitTarget;
|
||||||
@@ -101,14 +109,14 @@ public class DividerSnapAlgorithm {
|
|||||||
|
|
||||||
|
|
||||||
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
|
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,
|
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,
|
public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize,
|
||||||
boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode,
|
boolean isLeftRightSplit, Rect insets, Rect pinnedTaskbarInsets, int dockSide,
|
||||||
boolean isHomeResizable) {
|
boolean isMinimizedMode, boolean isHomeResizable) {
|
||||||
mMinFlingVelocityPxPerSecond =
|
mMinFlingVelocityPxPerSecond =
|
||||||
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
|
MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density;
|
||||||
mMinDismissVelocityPxPerSecond =
|
mMinDismissVelocityPxPerSecond =
|
||||||
@@ -117,10 +125,11 @@ public class DividerSnapAlgorithm {
|
|||||||
mDisplayWidth = displayWidth;
|
mDisplayWidth = displayWidth;
|
||||||
mDisplayHeight = displayHeight;
|
mDisplayHeight = displayHeight;
|
||||||
mIsLeftRightSplit = isLeftRightSplit;
|
mIsLeftRightSplit = isLeftRightSplit;
|
||||||
|
mDockSide = dockSide;
|
||||||
mInsets.set(insets);
|
mInsets.set(insets);
|
||||||
|
mPinnedTaskbarInsets.set(pinnedTaskbarInsets);
|
||||||
if (Flags.enableFlexibleTwoAppSplit()) {
|
if (Flags.enableFlexibleTwoAppSplit()) {
|
||||||
// In flexible split, we always use a fixed ratio (50%, 66%, or 90%) for splitting
|
mSnapMode = SNAP_FLEXIBLE_SPLIT;
|
||||||
mSnapMode = SNAP_FIXED_RATIO;
|
|
||||||
} else {
|
} else {
|
||||||
// Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config
|
// Set SNAP_MODE_MINIMIZED, SNAP_MODE_16_9, or SNAP_FIXED_RATIO depending on config
|
||||||
mSnapMode = isMinimizedMode
|
mSnapMode = isMinimizedMode
|
||||||
@@ -140,7 +149,7 @@ public class DividerSnapAlgorithm {
|
|||||||
mAllowOffscreenRatios = SplitScreenUtils.allowOffscreenRatios(res);
|
mAllowOffscreenRatios = SplitScreenUtils.allowOffscreenRatios(res);
|
||||||
mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
|
mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize(
|
||||||
com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
|
com.android.internal.R.dimen.task_height_of_minimized_mode) : 0;
|
||||||
calculateTargets(isLeftRightSplit, dockSide);
|
calculateTargets();
|
||||||
mFirstSplitTarget = mTargets.get(1);
|
mFirstSplitTarget = mTargets.get(1);
|
||||||
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
|
mLastSplitTarget = mTargets.get(mTargets.size() - 2);
|
||||||
mDismissStartTarget = mTargets.get(0);
|
mDismissStartTarget = mTargets.get(0);
|
||||||
@@ -276,28 +285,31 @@ public class DividerSnapAlgorithm {
|
|||||||
return mTargets.get(minIndex);
|
return mTargets.get(minIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateTargets(boolean isLeftRightSplit, int dockedSide) {
|
private void calculateTargets() {
|
||||||
mTargets.clear();
|
mTargets.clear();
|
||||||
int dividerMax = isLeftRightSplit
|
int dividerMax = mIsLeftRightSplit
|
||||||
? mDisplayWidth
|
? mDisplayWidth
|
||||||
: mDisplayHeight;
|
: mDisplayHeight;
|
||||||
int startPos = -mDividerSize;
|
int startPos = -mDividerSize;
|
||||||
if (dockedSide == DOCKED_RIGHT) {
|
if (mDockSide == DOCKED_RIGHT) {
|
||||||
startPos += mInsets.left;
|
startPos += mInsets.left;
|
||||||
}
|
}
|
||||||
mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
|
mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f));
|
||||||
switch (mSnapMode) {
|
switch (mSnapMode) {
|
||||||
case SNAP_MODE_16_9:
|
case SNAP_MODE_16_9:
|
||||||
addRatio16_9Targets(isLeftRightSplit, dividerMax);
|
addRatio16_9Targets(mIsLeftRightSplit, dividerMax);
|
||||||
break;
|
break;
|
||||||
case SNAP_FIXED_RATIO:
|
case SNAP_FIXED_RATIO:
|
||||||
addFixedDivisionTargets(isLeftRightSplit, dividerMax);
|
addFixedDivisionTargets(mIsLeftRightSplit, dividerMax);
|
||||||
break;
|
break;
|
||||||
case SNAP_ONLY_1_1:
|
case SNAP_ONLY_1_1:
|
||||||
addMiddleTarget(isLeftRightSplit);
|
addMiddleTarget(mIsLeftRightSplit);
|
||||||
break;
|
break;
|
||||||
case SNAP_MODE_MINIMIZED:
|
case SNAP_MODE_MINIMIZED:
|
||||||
addMinimizedTarget(isLeftRightSplit, dockedSide);
|
addMinimizedTarget(mIsLeftRightSplit, mDockSide);
|
||||||
|
break;
|
||||||
|
case SNAP_FLEXIBLE_SPLIT:
|
||||||
|
addFlexSplitTargets(mIsLeftRightSplit, dividerMax);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
|
mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f));
|
||||||
@@ -321,18 +333,9 @@ public class DividerSnapAlgorithm {
|
|||||||
? mDisplayWidth - mInsets.right
|
? mDisplayWidth - mInsets.right
|
||||||
: mDisplayHeight - mInsets.bottom;
|
: mDisplayHeight - mInsets.bottom;
|
||||||
|
|
||||||
int size;
|
int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2;
|
||||||
if (Flags.enableFlexibleTwoAppSplit()) {
|
if (mCalculateRatiosBasedOnAvailableSpace) {
|
||||||
float ratio = areOffscreenRatiosSupported()
|
size = Math.max(size, mMinimalSizeResizableTask);
|
||||||
? 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 topPosition = start + size;
|
int topPosition = start + size;
|
||||||
@@ -340,6 +343,24 @@ public class DividerSnapAlgorithm {
|
|||||||
addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax);
|
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) {
|
private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) {
|
||||||
int start = isLeftRightSplit ? mInsets.left : mInsets.top;
|
int start = isLeftRightSplit ? mInsets.left : mInsets.top;
|
||||||
int end = isLeftRightSplit
|
int end = isLeftRightSplit
|
||||||
|
|||||||
@@ -49,10 +49,13 @@ import android.app.ActivityManager;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Insets;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
import android.view.InsetsController;
|
||||||
|
import android.view.InsetsSource;
|
||||||
import android.view.InsetsSourceControl;
|
import android.view.InsetsSourceControl;
|
||||||
import android.view.InsetsState;
|
import android.view.InsetsState;
|
||||||
import android.view.RoundedCorner;
|
import android.view.RoundedCorner;
|
||||||
@@ -153,6 +156,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
private final ResizingEffectPolicy mSurfaceEffectPolicy;
|
private final ResizingEffectPolicy mSurfaceEffectPolicy;
|
||||||
private final ShellTaskOrganizer mTaskOrganizer;
|
private final ShellTaskOrganizer mTaskOrganizer;
|
||||||
private final InsetsState mInsetsState = new InsetsState();
|
private final InsetsState mInsetsState = new InsetsState();
|
||||||
|
private Insets mPinnedTaskbarInsets = Insets.NONE;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
@VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
|
@VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
|
||||||
@@ -523,6 +527,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
@Override
|
@Override
|
||||||
public void insetsChanged(InsetsState insetsState) {
|
public void insetsChanged(InsetsState insetsState) {
|
||||||
mInsetsState.set(insetsState);
|
mInsetsState.set(insetsState);
|
||||||
|
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -531,9 +536,51 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
// flicker.
|
// flicker.
|
||||||
return;
|
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);
|
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
|
@Override
|
||||||
public void insetsControlChanged(InsetsState insetsState,
|
public void insetsControlChanged(InsetsState insetsState,
|
||||||
InsetsSourceControl[] activeControls) {
|
InsetsSourceControl[] activeControls) {
|
||||||
@@ -631,8 +678,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #snapToTarget(int, SnapTarget)}, with default animation duration and
|
* Same as {@link #snapToTarget(int, SnapTarget, int, Interpolator)}, with default animation
|
||||||
* interpolator.
|
* duration and interpolator.
|
||||||
*/
|
*/
|
||||||
public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
|
public void snapToTarget(int currentPosition, SnapTarget snapTarget) {
|
||||||
snapToTarget(currentPosition, snapTarget, FLING_RESIZE_DURATION,
|
snapToTarget(currentPosition, snapTarget, FLING_RESIZE_DURATION,
|
||||||
@@ -683,6 +730,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
mDividerSize,
|
mDividerSize,
|
||||||
mIsLeftRightSplit,
|
mIsLeftRightSplit,
|
||||||
insets,
|
insets,
|
||||||
|
mPinnedTaskbarInsets.toRect(),
|
||||||
mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
|
mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user