Merge "Use boundsChangeTransaction to improve split-screen enter/exit" into rvc-dev am: 82352f9db8 am: eabcec5bf2
Original change: undetermined Change-Id: If6e10ed11410345824417a2da9820c4e54bfb47a
This commit is contained in:
@@ -209,7 +209,11 @@ public class WindowlessWindowManager implements IWindowSession {
|
|||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
protected SurfaceControl getSurfaceControl(View rootView) {
|
protected SurfaceControl getSurfaceControl(View rootView) {
|
||||||
final State s = mStateForWindow.get(rootView.getViewRootImpl().mWindow.asBinder());
|
final ViewRootImpl root = rootView.getViewRootImpl();
|
||||||
|
if (root == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final State s = mStateForWindow.get(root.mWindow.asBinder());
|
||||||
if (s == null) {
|
if (s == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,6 +264,29 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges another WCT into this one.
|
||||||
|
* @param transfer When true, this will transfer everything from other potentially leaving
|
||||||
|
* other in an unusable state. When false, other is left alone, but
|
||||||
|
* SurfaceFlinger Transactions will not be merged.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public void merge(WindowContainerTransaction other, boolean transfer) {
|
||||||
|
for (int i = 0, n = other.mChanges.size(); i < n; ++i) {
|
||||||
|
final IBinder key = other.mChanges.keyAt(i);
|
||||||
|
Change existing = mChanges.get(key);
|
||||||
|
if (existing == null) {
|
||||||
|
existing = new Change();
|
||||||
|
mChanges.put(key, existing);
|
||||||
|
}
|
||||||
|
existing.merge(other.mChanges.valueAt(i), transfer);
|
||||||
|
}
|
||||||
|
for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) {
|
||||||
|
mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
|
||||||
|
: new HierarchyOp(other.mHierarchyOps.get(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public Map<IBinder, Change> getChanges() {
|
public Map<IBinder, Change> getChanges() {
|
||||||
return mChanges;
|
return mChanges;
|
||||||
@@ -359,6 +382,41 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
mActivityWindowingMode = in.readInt();
|
mActivityWindowingMode = in.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param transfer When true, this will transfer other into this leaving other in an
|
||||||
|
* undefined state. Use this if you don't intend to use other. When false,
|
||||||
|
* SurfaceFlinger Transactions will not merge.
|
||||||
|
*/
|
||||||
|
public void merge(Change other, boolean transfer) {
|
||||||
|
mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask);
|
||||||
|
mConfigSetMask |= other.mConfigSetMask;
|
||||||
|
mWindowSetMask |= other.mWindowSetMask;
|
||||||
|
if ((other.mChangeMask & CHANGE_FOCUSABLE) != 0) {
|
||||||
|
mFocusable = other.mFocusable;
|
||||||
|
}
|
||||||
|
if (transfer && (other.mChangeMask & CHANGE_BOUNDS_TRANSACTION) != 0) {
|
||||||
|
mBoundsChangeTransaction = other.mBoundsChangeTransaction;
|
||||||
|
other.mBoundsChangeTransaction = null;
|
||||||
|
}
|
||||||
|
if ((other.mChangeMask & CHANGE_PIP_CALLBACK) != 0) {
|
||||||
|
mPinnedBounds = transfer ? other.mPinnedBounds : new Rect(other.mPinnedBounds);
|
||||||
|
}
|
||||||
|
if ((other.mChangeMask & CHANGE_HIDDEN) != 0) {
|
||||||
|
mHidden = other.mHidden;
|
||||||
|
}
|
||||||
|
mChangeMask |= other.mChangeMask;
|
||||||
|
if (other.mActivityWindowingMode >= 0) {
|
||||||
|
mActivityWindowingMode = other.mActivityWindowingMode;
|
||||||
|
}
|
||||||
|
if (other.mWindowingMode >= 0) {
|
||||||
|
mWindowingMode = other.mWindowingMode;
|
||||||
|
}
|
||||||
|
if (other.mBoundsChangeSurfaceBounds != null) {
|
||||||
|
mBoundsChangeSurfaceBounds = transfer ? other.mBoundsChangeSurfaceBounds
|
||||||
|
: new Rect(other.mBoundsChangeSurfaceBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getWindowingMode() {
|
public int getWindowingMode() {
|
||||||
return mWindowingMode;
|
return mWindowingMode;
|
||||||
}
|
}
|
||||||
@@ -522,6 +580,12 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
mToTop = toTop;
|
mToTop = toTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HierarchyOp(@NonNull HierarchyOp copy) {
|
||||||
|
mContainer = copy.mContainer;
|
||||||
|
mReparent = copy.mReparent;
|
||||||
|
mToTop = copy.mToTop;
|
||||||
|
}
|
||||||
|
|
||||||
protected HierarchyOp(Parcel in) {
|
protected HierarchyOp(Parcel in) {
|
||||||
mContainer = in.readStrongBinder();
|
mContainer = in.readStrongBinder();
|
||||||
mReparent = in.readStrongBinder();
|
mReparent = in.readStrongBinder();
|
||||||
|
|||||||
@@ -99,16 +99,19 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
private Handler mHandler;
|
private Handler mHandler;
|
||||||
private KeyguardStateController mKeyguardStateController;
|
private KeyguardStateController mKeyguardStateController;
|
||||||
|
|
||||||
|
private WindowManagerProxy mWindowManagerProxy;
|
||||||
|
|
||||||
private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
|
private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
|
||||||
new ArrayList<>();
|
new ArrayList<>();
|
||||||
|
|
||||||
private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
|
private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
|
||||||
|
|
||||||
private DisplayChangeController.OnDisplayChangingListener mRotationController =
|
private DisplayChangeController.OnDisplayChangingListener mRotationController =
|
||||||
(display, fromRotation, toRotation, t) -> {
|
(display, fromRotation, toRotation, wct) -> {
|
||||||
if (!mSplits.isSplitScreenSupported()) {
|
if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
WindowContainerTransaction t = new WindowContainerTransaction();
|
||||||
DisplayLayout displayLayout =
|
DisplayLayout displayLayout =
|
||||||
new DisplayLayout(mDisplayController.getDisplayLayout(display));
|
new DisplayLayout(mDisplayController.getDisplayLayout(display));
|
||||||
SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
|
SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
|
||||||
@@ -127,6 +130,17 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
if (isSplitActive()) {
|
if (isSplitActive()) {
|
||||||
WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
|
WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
|
||||||
}
|
}
|
||||||
|
if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) {
|
||||||
|
// Because sync transactions are serialized, its possible for an "older"
|
||||||
|
// bounds-change to get applied after a screen rotation. In that case, we
|
||||||
|
// want to actually defer on that rather than apply immediately. Of course,
|
||||||
|
// this means that the bounds may not change until after the rotation so
|
||||||
|
// the user might see some artifacts. This should be rare.
|
||||||
|
Slog.w(TAG, "Screen rotated while other operations were pending, this may"
|
||||||
|
+ " result in some graphical artifacts.");
|
||||||
|
} else {
|
||||||
|
wct.merge(t, true /* transfer */);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final DividerImeController mImePositionProcessor;
|
private final DividerImeController mImePositionProcessor;
|
||||||
@@ -159,6 +173,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
mRecentsOptionalLazy = recentsOptionalLazy;
|
mRecentsOptionalLazy = recentsOptionalLazy;
|
||||||
mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
|
mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
|
||||||
mTransactionPool = transactionPool;
|
mTransactionPool = transactionPool;
|
||||||
|
mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
|
||||||
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
|
mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,9 +293,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
|
LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
|
||||||
DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
|
DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
|
||||||
mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
|
mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
|
||||||
mImePositionProcessor);
|
mImePositionProcessor, mWindowManagerProxy);
|
||||||
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
|
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
|
||||||
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
|
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
|
||||||
final int size = dctx.getResources().getDimensionPixelSize(
|
final int size = dctx.getResources().getDimensionPixelSize(
|
||||||
com.android.internal.R.dimen.docked_stack_divider_thickness);
|
com.android.internal.R.dimen.docked_stack_divider_thickness);
|
||||||
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
|
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
|
||||||
@@ -303,7 +318,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
addDivider(configuration);
|
addDivider(configuration);
|
||||||
|
|
||||||
if (mMinimized) {
|
if (mMinimized) {
|
||||||
mView.setMinimizedDockStack(true, mHomeStackResizable);
|
mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */);
|
||||||
updateTouchable();
|
updateTouchable();
|
||||||
}
|
}
|
||||||
mView.setHidden(isDividerHidden);
|
mView.setHidden(isDividerHidden);
|
||||||
@@ -327,11 +342,13 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
if (visible) {
|
if (visible) {
|
||||||
mView.enterSplitMode(mHomeStackResizable);
|
mView.enterSplitMode(mHomeStackResizable);
|
||||||
// Update state because animations won't finish.
|
// Update state because animations won't finish.
|
||||||
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
|
mWindowManagerProxy.runInSync(
|
||||||
|
t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
mView.exitSplitMode();
|
mView.exitSplitMode();
|
||||||
// un-minimize so that next entry triggers minimize anim.
|
mWindowManagerProxy.runInSync(
|
||||||
mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable);
|
t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t));
|
||||||
}
|
}
|
||||||
// Notify existence listeners
|
// Notify existence listeners
|
||||||
synchronized (mDockedStackExistsListeners) {
|
synchronized (mDockedStackExistsListeners) {
|
||||||
@@ -344,12 +361,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSplitDismissed() {
|
|
||||||
updateVisibility(false /* visible */);
|
|
||||||
mMinimized = false;
|
|
||||||
removeDivider();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Switch to minimized state if appropriate */
|
/** Switch to minimized state if appropriate */
|
||||||
public void setMinimized(final boolean minimized) {
|
public void setMinimized(final boolean minimized) {
|
||||||
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
|
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
|
||||||
@@ -405,7 +416,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateTouchable();
|
updateTouchable();
|
||||||
WindowOrganizer.applyTransaction(wct);
|
mWindowManagerProxy.applySyncTransaction(wct);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAdjustedForIme(boolean adjustedForIme) {
|
void setAdjustedForIme(boolean adjustedForIme) {
|
||||||
@@ -501,7 +512,14 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
update(mDisplayController.getDisplayContext(
|
update(mDisplayController.getDisplayContext(
|
||||||
mContext.getDisplayId()).getResources().getConfiguration());
|
mContext.getDisplayId()).getResources().getConfiguration());
|
||||||
// Set resizable directly here because applyEnterSplit already resizes home stack.
|
// Set resizable directly here because applyEnterSplit already resizes home stack.
|
||||||
mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
|
mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startDismissSplit() {
|
||||||
|
mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, true /* dismissOrMaximize */);
|
||||||
|
updateVisibility(false /* visible */);
|
||||||
|
mMinimized = false;
|
||||||
|
removeDivider();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ensureMinimizedSplit() {
|
void ensureMinimizedSplit() {
|
||||||
@@ -530,6 +548,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
|||||||
return mSplitLayout;
|
return mSplitLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowManagerProxy getWmProxy() {
|
||||||
|
return mWindowManagerProxy;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return the container token for the secondary split root task. */
|
/** @return the container token for the secondary split root task. */
|
||||||
public WindowContainerToken getSecondaryRoot() {
|
public WindowContainerToken getSecondaryRoot() {
|
||||||
if (mSplits == null || mSplits.mSecondary == null) {
|
if (mSplits == null || mSplits.mSecondary == null) {
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import android.view.SurfaceControl;
|
|||||||
import android.window.TaskOrganizer;
|
import android.window.TaskOrganizer;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
import android.window.WindowOrganizer;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@@ -213,7 +212,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
|
|||||||
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
|
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowOrganizer.applyTransaction(wct);
|
mSplits.mDivider.getWmProxy().applySyncTransaction(wct);
|
||||||
|
|
||||||
// Update all the adjusted-for-ime states
|
// Update all the adjusted-for-ime states
|
||||||
if (!mPaused) {
|
if (!mPaused) {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import android.view.VelocityTracker;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnTouchListener;
|
import android.view.View.OnTouchListener;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
|
import android.view.ViewRootImpl;
|
||||||
import android.view.ViewTreeObserver.InternalInsetsInfo;
|
import android.view.ViewTreeObserver.InternalInsetsInfo;
|
||||||
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
|
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -138,7 +139,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
private final Rect mOtherInsetRect = new Rect();
|
private final Rect mOtherInsetRect = new Rect();
|
||||||
private final Rect mLastResizeRect = new Rect();
|
private final Rect mLastResizeRect = new Rect();
|
||||||
private final Rect mTmpRect = new Rect();
|
private final Rect mTmpRect = new Rect();
|
||||||
private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
|
private WindowManagerProxy mWindowManagerProxy;
|
||||||
private DividerWindowManager mWindowManager;
|
private DividerWindowManager mWindowManager;
|
||||||
private VelocityTracker mVelocityTracker;
|
private VelocityTracker mVelocityTracker;
|
||||||
private FlingAnimationUtils mFlingAnimationUtils;
|
private FlingAnimationUtils mFlingAnimationUtils;
|
||||||
@@ -360,13 +361,14 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
|
|
||||||
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
|
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
|
||||||
DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
|
DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
|
||||||
DividerImeController imeController) {
|
DividerImeController imeController, WindowManagerProxy wmProxy) {
|
||||||
mWindowManager = windowManager;
|
mWindowManager = windowManager;
|
||||||
mState = dividerState;
|
mState = dividerState;
|
||||||
mCallback = callback;
|
mCallback = callback;
|
||||||
mTiles = tiles;
|
mTiles = tiles;
|
||||||
mSplitLayout = sdl;
|
mSplitLayout = sdl;
|
||||||
mImeController = imeController;
|
mImeController = imeController;
|
||||||
|
mWindowManagerProxy = wmProxy;
|
||||||
|
|
||||||
if (mState.mRatioPositionBeforeMinimized == 0) {
|
if (mState.mRatioPositionBeforeMinimized == 0) {
|
||||||
// Set the middle target as the initial state
|
// Set the middle target as the initial state
|
||||||
@@ -376,10 +378,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public WindowManagerProxy getWindowManagerProxy() {
|
|
||||||
return mWindowManagerProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
|
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
|
||||||
mOtherTaskRect.set(mSplitLayout.mSecondary);
|
mOtherTaskRect.set(mSplitLayout.mSecondary);
|
||||||
return mOtherTaskRect;
|
return mOtherTaskRect;
|
||||||
@@ -519,7 +517,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
|
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
|
||||||
SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
|
SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
|
||||||
mStartPosition, 0 /* velocity */, false /* hardDismiss */);
|
mStartPosition, 0 /* velocity */, false /* hardDismiss */);
|
||||||
resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget);
|
resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget,
|
||||||
|
null /* transaction */);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
@@ -608,7 +607,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
|
taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
|
||||||
? TASK_POSITION_SAME
|
? TASK_POSITION_SAME
|
||||||
: snapTarget.taskPosition,
|
: snapTarget.taskPosition,
|
||||||
snapTarget));
|
snapTarget, null /* transaction */));
|
||||||
Consumer<Boolean> endAction = cancelled -> {
|
Consumer<Boolean> endAction = cancelled -> {
|
||||||
if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
|
if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
|
||||||
final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
|
final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
|
||||||
@@ -716,7 +715,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|
||||||
|| mDockSide == WindowManager.DOCKED_BOTTOM;
|
|| mDockSide == WindowManager.DOCKED_BOTTOM;
|
||||||
}
|
}
|
||||||
mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize);
|
mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, mSplitLayout, dismissOrMaximize);
|
||||||
Transaction t = mTiles.getTransaction();
|
Transaction t = mTiles.getTransaction();
|
||||||
setResizeDimLayer(t, true /* primary */, 0f);
|
setResizeDimLayer(t, true /* primary */, 0f);
|
||||||
setResizeDimLayer(t, false /* primary */, 0f);
|
setResizeDimLayer(t, false /* primary */, 0f);
|
||||||
@@ -806,7 +805,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
mWindowManager.setTouchRegion(touchRegion);
|
mWindowManager.setTouchRegion(touchRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
|
void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable,
|
||||||
|
Transaction t) {
|
||||||
mHomeStackResizable = isHomeStackResizable;
|
mHomeStackResizable = isHomeStackResizable;
|
||||||
updateDockSide();
|
updateDockSide();
|
||||||
if (!minimized) {
|
if (!minimized) {
|
||||||
@@ -840,9 +840,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
// Relayout to recalculate the divider shadow when minimizing
|
// Relayout to recalculate the divider shadow when minimizing
|
||||||
requestLayout();
|
requestLayout();
|
||||||
mIsInMinimizeInteraction = true;
|
mIsInMinimizeInteraction = true;
|
||||||
resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget());
|
resizeStackSurfaces(
|
||||||
|
mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget(), t);
|
||||||
} else {
|
} else {
|
||||||
resizeStackSurfaces(mSnapTargetBeforeMinimized);
|
resizeStackSurfaces(mSnapTargetBeforeMinimized, t);
|
||||||
mIsInMinimizeInteraction = false;
|
mIsInMinimizeInteraction = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -873,10 +874,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
* assigned to it.
|
* assigned to it.
|
||||||
*/
|
*/
|
||||||
private SurfaceControl getWindowSurfaceControl() {
|
private SurfaceControl getWindowSurfaceControl() {
|
||||||
if (getViewRootImpl() == null) {
|
final ViewRootImpl root = getViewRootImpl();
|
||||||
|
if (root == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
SurfaceControl out = getViewRootImpl().getSurfaceControl();
|
SurfaceControl out = root.getSurfaceControl();
|
||||||
if (out != null && out.isValid()) {
|
if (out != null && out.isValid()) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -885,15 +887,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
|
|
||||||
void exitSplitMode() {
|
void exitSplitMode() {
|
||||||
// Reset tile bounds
|
// Reset tile bounds
|
||||||
post(() -> {
|
final SurfaceControl sc = getWindowSurfaceControl();
|
||||||
final SurfaceControl sc = getWindowSurfaceControl();
|
if (sc == null) {
|
||||||
if (sc == null) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
Transaction t = mTiles.getTransaction();
|
||||||
Transaction t = mTiles.getTransaction();
|
t.hide(sc).apply();
|
||||||
t.hide(sc).apply();
|
mTiles.releaseTransaction(t);
|
||||||
mTiles.releaseTransaction(t);
|
|
||||||
});
|
|
||||||
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
|
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
|
||||||
WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
|
WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
|
||||||
}
|
}
|
||||||
@@ -1049,8 +1049,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
mDividerSize);
|
mDividerSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
|
private void resizeStackSurfaces(SnapTarget taskSnapTarget, Transaction t) {
|
||||||
resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
|
resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
|
void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
|
||||||
@@ -1105,7 +1105,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget) {
|
void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget,
|
||||||
|
Transaction transaction) {
|
||||||
if (mRemoved) {
|
if (mRemoved) {
|
||||||
// This divider view has been removed so shouldn't have any additional influence.
|
// This divider view has been removed so shouldn't have any additional influence.
|
||||||
return;
|
return;
|
||||||
@@ -1123,7 +1124,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
mBackground.invalidate();
|
mBackground.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction t = mTiles.getTransaction();
|
final boolean ownTransaction = transaction == null;
|
||||||
|
final Transaction t = ownTransaction ? mTiles.getTransaction() : transaction;
|
||||||
mLastResizeRect.set(mDockedRect);
|
mLastResizeRect.set(mDockedRect);
|
||||||
if (mHomeStackResizable && mIsInMinimizeInteraction) {
|
if (mHomeStackResizable && mIsInMinimizeInteraction) {
|
||||||
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
|
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
|
||||||
@@ -1138,8 +1140,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
}
|
}
|
||||||
resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
|
resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
|
||||||
mOtherTaskRect);
|
mOtherTaskRect);
|
||||||
t.apply();
|
if (ownTransaction) {
|
||||||
mTiles.releaseTransaction(t);
|
t.apply();
|
||||||
|
mTiles.releaseTransaction(t);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,8 +1205,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
|
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
|
||||||
float dimFraction = getDimFraction(position, closestDismissTarget);
|
float dimFraction = getDimFraction(position, closestDismissTarget);
|
||||||
setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
|
setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
|
||||||
t.apply();
|
if (ownTransaction) {
|
||||||
mTiles.releaseTransaction(t);
|
t.apply();
|
||||||
|
mTiles.releaseTransaction(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyExitAnimationParallax(Rect taskRect, int position) {
|
private void applyExitAnimationParallax(Rect taskRect, int position) {
|
||||||
@@ -1383,7 +1389,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
|||||||
|
|
||||||
resizeStackSurfaces(calculatePositionForInsetBounds(),
|
resizeStackSurfaces(calculatePositionForInsetBounds(),
|
||||||
mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
|
mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
|
||||||
mSplitLayout.getSnapAlgorithm().getMiddleTarget());
|
mSplitLayout.getSnapAlgorithm().getMiddleTarget(),
|
||||||
|
null /* transaction */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRecentsDrawn() {
|
void onRecentsDrawn() {
|
||||||
|
|||||||
@@ -200,8 +200,7 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
|
|||||||
Log.d(TAG, " was in split, so this means leave it "
|
Log.d(TAG, " was in split, so this means leave it "
|
||||||
+ mPrimary.topActivityType + " " + mSecondary.topActivityType);
|
+ mPrimary.topActivityType + " " + mSecondary.topActivityType);
|
||||||
}
|
}
|
||||||
WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */);
|
mDivider.startDismissSplit();
|
||||||
mDivider.onSplitDismissed();
|
|
||||||
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
|
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
|
||||||
// Wasn't in split-mode (both were empty), but now that the primary split is
|
// Wasn't in split-mode (both were empty), but now that the primary split is
|
||||||
// populated, we should fully enter split by moving everything else into secondary.
|
// populated, we should fully enter split by moving everything else into secondary.
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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.systemui.stackdivider;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Slog;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
import android.window.WindowContainerTransactionCallback;
|
||||||
|
import android.window.WindowOrganizer;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.systemui.TransactionPool;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for serializing sync-transactions and corresponding callbacks.
|
||||||
|
*/
|
||||||
|
class SyncTransactionQueue {
|
||||||
|
private static final boolean DEBUG = Divider.DEBUG;
|
||||||
|
private static final String TAG = "SyncTransactionQueue";
|
||||||
|
|
||||||
|
// Just a little longer than the sync-engine timeout of 5s
|
||||||
|
private static final int REPLY_TIMEOUT = 5300;
|
||||||
|
|
||||||
|
private final TransactionPool mTransactionPool;
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
|
// Sync Transactions currently don't support nesting or interleaving properly, so
|
||||||
|
// queue up transactions to run them serially.
|
||||||
|
private final ArrayList<SyncCallback> mQueue = new ArrayList<>();
|
||||||
|
|
||||||
|
private SyncCallback mInFlight = null;
|
||||||
|
private final ArrayList<TransactionRunnable> mRunnables = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Runnable mOnReplyTimeout = () -> {
|
||||||
|
synchronized (mQueue) {
|
||||||
|
if (mInFlight != null && mQueue.contains(mInFlight)) {
|
||||||
|
Slog.w(TAG, "Sync Transaction timed-out: " + mInFlight.mWCT);
|
||||||
|
mInFlight.onTransactionReady(mInFlight.mId, new SurfaceControl.Transaction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SyncTransactionQueue(TransactionPool pool, Handler handler) {
|
||||||
|
mTransactionPool = pool;
|
||||||
|
mHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues a sync transaction to be sent serially to WM.
|
||||||
|
*/
|
||||||
|
void queue(WindowContainerTransaction wct) {
|
||||||
|
SyncCallback cb = new SyncCallback(wct);
|
||||||
|
synchronized (mQueue) {
|
||||||
|
if (DEBUG) Slog.d(TAG, "Queueing up " + wct);
|
||||||
|
mQueue.add(cb);
|
||||||
|
if (mQueue.size() == 1) {
|
||||||
|
cb.send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues a sync transaction only if there are already sync transaction(s) queued or in flight.
|
||||||
|
* Otherwise just returns without queueing.
|
||||||
|
* @return {@code true} if queued, {@code false} if not.
|
||||||
|
*/
|
||||||
|
boolean queueIfWaiting(WindowContainerTransaction wct) {
|
||||||
|
synchronized (mQueue) {
|
||||||
|
if (mQueue.isEmpty()) {
|
||||||
|
if (DEBUG) Slog.d(TAG, "Nothing in queue, so skip queueing up " + wct);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (DEBUG) Slog.d(TAG, "Queue is non-empty, so queueing up " + wct);
|
||||||
|
SyncCallback cb = new SyncCallback(wct);
|
||||||
|
mQueue.add(cb);
|
||||||
|
if (mQueue.size() == 1) {
|
||||||
|
cb.send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs a runnable in sync with sync transactions (ie. when the current in-flight transaction
|
||||||
|
* returns. If there are no transactions in-flight, runnable executes immediately.
|
||||||
|
*/
|
||||||
|
void runInSync(TransactionRunnable runnable) {
|
||||||
|
synchronized (mQueue) {
|
||||||
|
if (DEBUG) Slog.d(TAG, "Run in sync. mInFlight=" + mInFlight);
|
||||||
|
if (mInFlight != null) {
|
||||||
|
mRunnables.add(runnable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SurfaceControl.Transaction t = mTransactionPool.acquire();
|
||||||
|
runnable.runWithTransaction(t);
|
||||||
|
t.apply();
|
||||||
|
mTransactionPool.release(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronized on mQueue
|
||||||
|
private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) {
|
||||||
|
if (DEBUG) Slog.d(TAG, " Running " + mRunnables.size() + " sync runnables");
|
||||||
|
for (int i = 0, n = mRunnables.size(); i < n; ++i) {
|
||||||
|
mRunnables.get(i).runWithTransaction(t);
|
||||||
|
}
|
||||||
|
mRunnables.clear();
|
||||||
|
t.apply();
|
||||||
|
t.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionRunnable {
|
||||||
|
void runWithTransaction(SurfaceControl.Transaction t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SyncCallback extends WindowContainerTransactionCallback {
|
||||||
|
int mId = -1;
|
||||||
|
final WindowContainerTransaction mWCT;
|
||||||
|
|
||||||
|
SyncCallback(WindowContainerTransaction wct) {
|
||||||
|
mWCT = wct;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be sychronized on mQueue
|
||||||
|
void send() {
|
||||||
|
if (mInFlight != null) {
|
||||||
|
throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
|
||||||
|
+ mInFlight.mId + " - " + mInFlight.mWCT);
|
||||||
|
}
|
||||||
|
mInFlight = this;
|
||||||
|
if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
|
||||||
|
mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
|
||||||
|
if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
|
||||||
|
mHandler.postDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransactionReady(int id,
|
||||||
|
@androidx.annotation.NonNull SurfaceControl.Transaction t) {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
synchronized (mQueue) {
|
||||||
|
if (mId != id) {
|
||||||
|
Slog.e(TAG, "Got an unexpected onTransactionReady. Expected "
|
||||||
|
+ mId + " but got " + id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mInFlight = null;
|
||||||
|
mHandler.removeCallbacks(mOnReplyTimeout);
|
||||||
|
if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId);
|
||||||
|
mQueue.remove(this);
|
||||||
|
onTransactionReceived(t);
|
||||||
|
if (!mQueue.isEmpty()) {
|
||||||
|
mQueue.get(0).send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,9 +25,11 @@ import android.annotation.NonNull;
|
|||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityTaskManager;
|
import android.app.ActivityTaskManager;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
import android.view.WindowManagerGlobal;
|
import android.view.WindowManagerGlobal;
|
||||||
import android.window.TaskOrganizer;
|
import android.window.TaskOrganizer;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
@@ -35,6 +37,7 @@ import android.window.WindowContainerTransaction;
|
|||||||
import android.window.WindowOrganizer;
|
import android.window.WindowOrganizer;
|
||||||
|
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
|
import com.android.systemui.TransactionPool;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,8 +52,6 @@ public class WindowManagerProxy {
|
|||||||
private static final String TAG = "WindowManagerProxy";
|
private static final String TAG = "WindowManagerProxy";
|
||||||
private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
|
private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
|
||||||
|
|
||||||
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
|
|
||||||
|
|
||||||
@GuardedBy("mDockedRect")
|
@GuardedBy("mDockedRect")
|
||||||
private final Rect mDockedRect = new Rect();
|
private final Rect mDockedRect = new Rect();
|
||||||
|
|
||||||
@@ -61,6 +62,8 @@ public class WindowManagerProxy {
|
|||||||
|
|
||||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
|
private final SyncTransactionQueue mSyncTransactionQueue;
|
||||||
|
|
||||||
private final Runnable mSetTouchableRegionRunnable = new Runnable() {
|
private final Runnable mSetTouchableRegionRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -76,16 +79,13 @@ public class WindowManagerProxy {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private WindowManagerProxy() {
|
WindowManagerProxy(TransactionPool transactionPool, Handler handler) {
|
||||||
|
mSyncTransactionQueue = new SyncTransactionQueue(transactionPool, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WindowManagerProxy getInstance() {
|
void dismissOrMaximizeDocked(final SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
|
||||||
return sInstance;
|
final boolean dismissOrMaximize) {
|
||||||
}
|
mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize));
|
||||||
|
|
||||||
void dismissOrMaximizeDocked(
|
|
||||||
final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
|
|
||||||
mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setResizing(final boolean resizing) {
|
public void setResizing(final boolean resizing) {
|
||||||
@@ -163,7 +163,7 @@ public class WindowManagerProxy {
|
|||||||
*
|
*
|
||||||
* @return whether the home stack is resizable
|
* @return whether the home stack is resizable
|
||||||
*/
|
*/
|
||||||
static boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
|
boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
|
||||||
// Set launchtile first so that any stack created after
|
// Set launchtile first so that any stack created after
|
||||||
// getAllStackInfos and before reparent (even if unlikely) are placed
|
// getAllStackInfos and before reparent (even if unlikely) are placed
|
||||||
// correctly.
|
// correctly.
|
||||||
@@ -174,6 +174,8 @@ public class WindowManagerProxy {
|
|||||||
if (rootTasks.isEmpty()) {
|
if (rootTasks.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ActivityManager.RunningTaskInfo topHomeTask = null;
|
||||||
|
boolean homeIsTop = false;
|
||||||
for (int i = rootTasks.size() - 1; i >= 0; --i) {
|
for (int i = rootTasks.size() - 1; i >= 0; --i) {
|
||||||
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
|
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
|
||||||
// Only move resizeable task to split secondary. WM will just ignore this anyways...
|
// Only move resizeable task to split secondary. WM will just ignore this anyways...
|
||||||
@@ -183,16 +185,25 @@ public class WindowManagerProxy {
|
|||||||
!= WINDOWING_MODE_FULLSCREEN) {
|
!= WINDOWING_MODE_FULLSCREEN) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
// Since this iterates from bottom to top, update topHomeTask for every fullscreen task
|
||||||
|
// so it will be left with the status of the top one.
|
||||||
|
topHomeTask = isHomeOrRecentTask(rootTask) ? rootTask : null;
|
||||||
wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
|
wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
|
||||||
}
|
}
|
||||||
// Move the secondary split-forward.
|
// Move the secondary split-forward.
|
||||||
wct.reorder(tiles.mSecondary.token, true /* onTop */);
|
wct.reorder(tiles.mSecondary.token, true /* onTop */);
|
||||||
boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
|
boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
|
||||||
WindowOrganizer.applyTransaction(wct);
|
if (isHomeResizable && topHomeTask != null) {
|
||||||
|
// Translate/update-crop of secondary out-of-band with sync transaction -- Until BALST
|
||||||
|
// is enabled, this temporarily syncs the home surface position with offset until
|
||||||
|
// sync transaction finishes.
|
||||||
|
wct.setBoundsChangeTransaction(topHomeTask.token, tiles.mHomeBounds);
|
||||||
|
}
|
||||||
|
applySyncTransaction(wct);
|
||||||
return isHomeResizable;
|
return isHomeResizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
|
static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
|
||||||
final int atype = ti.configuration.windowConfiguration.getActivityType();
|
final int atype = ti.configuration.windowConfiguration.getActivityType();
|
||||||
return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
|
return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
|
||||||
}
|
}
|
||||||
@@ -203,7 +214,8 @@ public class WindowManagerProxy {
|
|||||||
* split (thus resulting in the top of the secondary split becoming
|
* split (thus resulting in the top of the secondary split becoming
|
||||||
* fullscreen. {@code false} resolves the other way.
|
* fullscreen. {@code false} resolves the other way.
|
||||||
*/
|
*/
|
||||||
static void applyDismissSplit(SplitScreenTaskOrganizer tiles, boolean dismissOrMaximize) {
|
void applyDismissSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
|
||||||
|
boolean dismissOrMaximize) {
|
||||||
// Set launch root first so that any task created after getChildContainers and
|
// Set launch root first so that any task created after getChildContainers and
|
||||||
// before reparent (pretty unlikely) are put into fullscreen.
|
// before reparent (pretty unlikely) are put into fullscreen.
|
||||||
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
|
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
|
||||||
@@ -229,6 +241,7 @@ public class WindowManagerProxy {
|
|||||||
wct.reparent(primaryChildren.get(i).token, null /* parent */,
|
wct.reparent(primaryChildren.get(i).token, null /* parent */,
|
||||||
true /* onTop */);
|
true /* onTop */);
|
||||||
}
|
}
|
||||||
|
boolean homeOnTop = false;
|
||||||
// Don't need to worry about home tasks because they are already in the "proper"
|
// Don't need to worry about home tasks because they are already in the "proper"
|
||||||
// order within the secondary split.
|
// order within the secondary split.
|
||||||
for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
|
for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
|
||||||
@@ -236,8 +249,31 @@ public class WindowManagerProxy {
|
|||||||
wct.reparent(ti.token, null /* parent */, true /* onTop */);
|
wct.reparent(ti.token, null /* parent */, true /* onTop */);
|
||||||
if (isHomeOrRecentTask(ti)) {
|
if (isHomeOrRecentTask(ti)) {
|
||||||
wct.setBounds(ti.token, null);
|
wct.setBounds(ti.token, null);
|
||||||
|
if (i == 0) {
|
||||||
|
homeOnTop = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (homeOnTop) {
|
||||||
|
// Translate/update-crop of secondary out-of-band with sync transaction -- instead
|
||||||
|
// play this in sync with new home-app frame because until BALST is enabled this
|
||||||
|
// shows up on screen before the syncTransaction returns.
|
||||||
|
// We only have access to the secondary root surface, though, so in order to
|
||||||
|
// position things properly, we have to take into account the existing negative
|
||||||
|
// offset/crop of the minimized-home task.
|
||||||
|
final boolean landscape = layout.mDisplayLayout.isLandscape();
|
||||||
|
final int posX = landscape ? layout.mSecondary.left - tiles.mHomeBounds.left
|
||||||
|
: layout.mSecondary.left;
|
||||||
|
final int posY = landscape ? layout.mSecondary.top
|
||||||
|
: layout.mSecondary.top - tiles.mHomeBounds.top;
|
||||||
|
final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
|
||||||
|
sft.setPosition(tiles.mSecondarySurface, posX, posY);
|
||||||
|
final Rect crop = new Rect(0, 0, layout.mDisplayLayout.width(),
|
||||||
|
layout.mDisplayLayout.height());
|
||||||
|
crop.offset(-posX, -posY);
|
||||||
|
sft.setWindowCrop(tiles.mSecondarySurface, crop);
|
||||||
|
wct.setBoundsChangeTransaction(tiles.mSecondary.token, sft);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Maximize, so move non-home secondary split first
|
// Maximize, so move non-home secondary split first
|
||||||
for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
|
for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
|
||||||
@@ -267,6 +303,29 @@ public class WindowManagerProxy {
|
|||||||
}
|
}
|
||||||
// Reset focusable to true
|
// Reset focusable to true
|
||||||
wct.setFocusable(tiles.mPrimary.token, true /* focusable */);
|
wct.setFocusable(tiles.mPrimary.token, true /* focusable */);
|
||||||
WindowOrganizer.applyTransaction(wct);
|
applySyncTransaction(wct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to apply a sync transaction serially with other sync transactions.
|
||||||
|
*
|
||||||
|
* @see SyncTransactionQueue#queue
|
||||||
|
*/
|
||||||
|
void applySyncTransaction(WindowContainerTransaction wct) {
|
||||||
|
mSyncTransactionQueue.queue(wct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SyncTransactionQueue#queueIfWaiting
|
||||||
|
*/
|
||||||
|
boolean queueSyncTransactionIfWaiting(WindowContainerTransaction wct) {
|
||||||
|
return mSyncTransactionQueue.queueIfWaiting(wct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see SyncTransactionQueue#runInSync
|
||||||
|
*/
|
||||||
|
void runInSync(SyncTransactionQueue.TransactionRunnable runnable) {
|
||||||
|
mSyncTransactionQueue.runInSync(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static android.app.AppOpsManager.MODE_ALLOWED;
|
|||||||
import static android.app.AppOpsManager.MODE_DEFAULT;
|
import static android.app.AppOpsManager.MODE_DEFAULT;
|
||||||
import static android.app.AppOpsManager.OP_NONE;
|
import static android.app.AppOpsManager.OP_NONE;
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
|
||||||
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||||
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
|
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
|
||||||
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||||
import static android.os.PowerManager.DRAW_WAKE_LOCK;
|
import static android.os.PowerManager.DRAW_WAKE_LOCK;
|
||||||
@@ -3757,6 +3758,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
|||||||
if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
|
if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// TODO(157912944): formalize drag-resizing so that exceptions aren't hardcoded like this
|
||||||
|
if (task.getActivityType() == ACTIVITY_TYPE_HOME) {
|
||||||
|
// The current sys-ui implementations never live-resize home, so to prevent WSA from
|
||||||
|
// creating/destroying surfaces (which messes up sync-transactions), skip HOME tasks.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
|
if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) {
|
||||||
// Floating windows never enter drag resize mode.
|
// Floating windows never enter drag resize mode.
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user