Merge "Use boundsChangeTransaction to improve split-screen enter/exit" into rvc-dev
This commit is contained in:
@@ -209,7 +209,11 @@ public class WindowlessWindowManager implements IWindowSession {
|
||||
|
||||
/** @hide */
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -264,6 +264,29 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
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 */
|
||||
public Map<IBinder, Change> getChanges() {
|
||||
return mChanges;
|
||||
@@ -359,6 +382,41 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
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() {
|
||||
return mWindowingMode;
|
||||
}
|
||||
@@ -522,6 +580,12 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
mToTop = toTop;
|
||||
}
|
||||
|
||||
public HierarchyOp(@NonNull HierarchyOp copy) {
|
||||
mContainer = copy.mContainer;
|
||||
mReparent = copy.mReparent;
|
||||
mToTop = copy.mToTop;
|
||||
}
|
||||
|
||||
protected HierarchyOp(Parcel in) {
|
||||
mContainer = in.readStrongBinder();
|
||||
mReparent = in.readStrongBinder();
|
||||
|
||||
@@ -99,16 +99,19 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
private Handler mHandler;
|
||||
private KeyguardStateController mKeyguardStateController;
|
||||
|
||||
private WindowManagerProxy mWindowManagerProxy;
|
||||
|
||||
private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
|
||||
new ArrayList<>();
|
||||
|
||||
private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
|
||||
|
||||
private DisplayChangeController.OnDisplayChangingListener mRotationController =
|
||||
(display, fromRotation, toRotation, t) -> {
|
||||
if (!mSplits.isSplitScreenSupported()) {
|
||||
(display, fromRotation, toRotation, wct) -> {
|
||||
if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
|
||||
return;
|
||||
}
|
||||
WindowContainerTransaction t = new WindowContainerTransaction();
|
||||
DisplayLayout displayLayout =
|
||||
new DisplayLayout(mDisplayController.getDisplayLayout(display));
|
||||
SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
|
||||
@@ -127,6 +130,17 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
if (isSplitActive()) {
|
||||
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;
|
||||
@@ -159,6 +173,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
mRecentsOptionalLazy = recentsOptionalLazy;
|
||||
mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
|
||||
mTransactionPool = transactionPool;
|
||||
mWindowManagerProxy = new WindowManagerProxy(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);
|
||||
DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
|
||||
mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
|
||||
mImePositionProcessor);
|
||||
mImePositionProcessor, mWindowManagerProxy);
|
||||
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
|
||||
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
|
||||
final int size = dctx.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.docked_stack_divider_thickness);
|
||||
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
|
||||
@@ -303,7 +318,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
addDivider(configuration);
|
||||
|
||||
if (mMinimized) {
|
||||
mView.setMinimizedDockStack(true, mHomeStackResizable);
|
||||
mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */);
|
||||
updateTouchable();
|
||||
}
|
||||
mView.setHidden(isDividerHidden);
|
||||
@@ -327,11 +342,13 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
if (visible) {
|
||||
mView.enterSplitMode(mHomeStackResizable);
|
||||
// Update state because animations won't finish.
|
||||
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
|
||||
mWindowManagerProxy.runInSync(
|
||||
t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t));
|
||||
|
||||
} else {
|
||||
mView.exitSplitMode();
|
||||
// un-minimize so that next entry triggers minimize anim.
|
||||
mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable);
|
||||
mWindowManagerProxy.runInSync(
|
||||
t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t));
|
||||
}
|
||||
// Notify existence listeners
|
||||
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 */
|
||||
public void setMinimized(final boolean minimized) {
|
||||
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
|
||||
@@ -405,7 +416,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
}
|
||||
}
|
||||
updateTouchable();
|
||||
WindowOrganizer.applyTransaction(wct);
|
||||
mWindowManagerProxy.applySyncTransaction(wct);
|
||||
}
|
||||
|
||||
void setAdjustedForIme(boolean adjustedForIme) {
|
||||
@@ -501,7 +512,14 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
update(mDisplayController.getDisplayContext(
|
||||
mContext.getDisplayId()).getResources().getConfiguration());
|
||||
// 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() {
|
||||
@@ -530,6 +548,10 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
return mSplitLayout;
|
||||
}
|
||||
|
||||
WindowManagerProxy getWmProxy() {
|
||||
return mWindowManagerProxy;
|
||||
}
|
||||
|
||||
/** @return the container token for the secondary split root task. */
|
||||
public WindowContainerToken getSecondaryRoot() {
|
||||
if (mSplits == null || mSplits.mSecondary == null) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.view.SurfaceControl;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
import android.window.WindowOrganizer;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@@ -213,7 +212,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
|
||||
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
|
||||
}
|
||||
|
||||
WindowOrganizer.applyTransaction(wct);
|
||||
mSplits.mDivider.getWmProxy().applySyncTransaction(wct);
|
||||
|
||||
// Update all the adjusted-for-ime states
|
||||
if (!mPaused) {
|
||||
|
||||
@@ -46,6 +46,7 @@ import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewRootImpl;
|
||||
import android.view.ViewTreeObserver.InternalInsetsInfo;
|
||||
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
|
||||
import android.view.WindowManager;
|
||||
@@ -138,7 +139,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
private final Rect mOtherInsetRect = new Rect();
|
||||
private final Rect mLastResizeRect = new Rect();
|
||||
private final Rect mTmpRect = new Rect();
|
||||
private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance();
|
||||
private WindowManagerProxy mWindowManagerProxy;
|
||||
private DividerWindowManager mWindowManager;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private FlingAnimationUtils mFlingAnimationUtils;
|
||||
@@ -360,13 +361,14 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
|
||||
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
|
||||
DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
|
||||
DividerImeController imeController) {
|
||||
DividerImeController imeController, WindowManagerProxy wmProxy) {
|
||||
mWindowManager = windowManager;
|
||||
mState = dividerState;
|
||||
mCallback = callback;
|
||||
mTiles = tiles;
|
||||
mSplitLayout = sdl;
|
||||
mImeController = imeController;
|
||||
mWindowManagerProxy = wmProxy;
|
||||
|
||||
if (mState.mRatioPositionBeforeMinimized == 0) {
|
||||
// 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() {
|
||||
mOtherTaskRect.set(mSplitLayout.mSecondary);
|
||||
return mOtherTaskRect;
|
||||
@@ -519,7 +517,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) {
|
||||
SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget(
|
||||
mStartPosition, 0 /* velocity */, false /* hardDismiss */);
|
||||
resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget);
|
||||
resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget,
|
||||
null /* transaction */);
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
@@ -608,7 +607,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f
|
||||
? TASK_POSITION_SAME
|
||||
: snapTarget.taskPosition,
|
||||
snapTarget));
|
||||
snapTarget, null /* transaction */));
|
||||
Consumer<Boolean> endAction = cancelled -> {
|
||||
if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction);
|
||||
final boolean wasMinimizeInteraction = mIsInMinimizeInteraction;
|
||||
@@ -716,7 +715,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|
||||
|| mDockSide == WindowManager.DOCKED_BOTTOM;
|
||||
}
|
||||
mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize);
|
||||
mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, mSplitLayout, dismissOrMaximize);
|
||||
Transaction t = mTiles.getTransaction();
|
||||
setResizeDimLayer(t, true /* primary */, 0f);
|
||||
setResizeDimLayer(t, false /* primary */, 0f);
|
||||
@@ -806,7 +805,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
mWindowManager.setTouchRegion(touchRegion);
|
||||
}
|
||||
|
||||
public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
|
||||
void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable,
|
||||
Transaction t) {
|
||||
mHomeStackResizable = isHomeStackResizable;
|
||||
updateDockSide();
|
||||
if (!minimized) {
|
||||
@@ -840,9 +840,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
// Relayout to recalculate the divider shadow when minimizing
|
||||
requestLayout();
|
||||
mIsInMinimizeInteraction = true;
|
||||
resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget());
|
||||
resizeStackSurfaces(
|
||||
mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget(), t);
|
||||
} else {
|
||||
resizeStackSurfaces(mSnapTargetBeforeMinimized);
|
||||
resizeStackSurfaces(mSnapTargetBeforeMinimized, t);
|
||||
mIsInMinimizeInteraction = false;
|
||||
}
|
||||
}
|
||||
@@ -873,10 +874,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
* assigned to it.
|
||||
*/
|
||||
private SurfaceControl getWindowSurfaceControl() {
|
||||
if (getViewRootImpl() == null) {
|
||||
final ViewRootImpl root = getViewRootImpl();
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
SurfaceControl out = getViewRootImpl().getSurfaceControl();
|
||||
SurfaceControl out = root.getSurfaceControl();
|
||||
if (out != null && out.isValid()) {
|
||||
return out;
|
||||
}
|
||||
@@ -885,15 +887,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
|
||||
void exitSplitMode() {
|
||||
// Reset tile bounds
|
||||
post(() -> {
|
||||
final SurfaceControl sc = getWindowSurfaceControl();
|
||||
if (sc == null) {
|
||||
return;
|
||||
}
|
||||
Transaction t = mTiles.getTransaction();
|
||||
t.hide(sc).apply();
|
||||
mTiles.releaseTransaction(t);
|
||||
});
|
||||
final SurfaceControl sc = getWindowSurfaceControl();
|
||||
if (sc == null) {
|
||||
return;
|
||||
}
|
||||
Transaction t = mTiles.getTransaction();
|
||||
t.hide(sc).apply();
|
||||
mTiles.releaseTransaction(t);
|
||||
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
|
||||
WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
|
||||
}
|
||||
@@ -1049,8 +1049,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
mDividerSize);
|
||||
}
|
||||
|
||||
private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
|
||||
resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
|
||||
private void resizeStackSurfaces(SnapTarget taskSnapTarget, Transaction t) {
|
||||
resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget, t);
|
||||
}
|
||||
|
||||
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) {
|
||||
// This divider view has been removed so shouldn't have any additional influence.
|
||||
return;
|
||||
@@ -1123,7 +1124,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
mBackground.invalidate();
|
||||
}
|
||||
|
||||
Transaction t = mTiles.getTransaction();
|
||||
final boolean ownTransaction = transaction == null;
|
||||
final Transaction t = ownTransaction ? mTiles.getTransaction() : transaction;
|
||||
mLastResizeRect.set(mDockedRect);
|
||||
if (mHomeStackResizable && mIsInMinimizeInteraction) {
|
||||
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
|
||||
@@ -1138,8 +1140,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
}
|
||||
resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
|
||||
mOtherTaskRect);
|
||||
t.apply();
|
||||
mTiles.releaseTransaction(t);
|
||||
if (ownTransaction) {
|
||||
t.apply();
|
||||
mTiles.releaseTransaction(t);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1201,8 +1205,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
|
||||
float dimFraction = getDimFraction(position, closestDismissTarget);
|
||||
setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
|
||||
t.apply();
|
||||
mTiles.releaseTransaction(t);
|
||||
if (ownTransaction) {
|
||||
t.apply();
|
||||
mTiles.releaseTransaction(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyExitAnimationParallax(Rect taskRect, int position) {
|
||||
@@ -1383,7 +1389,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
|
||||
resizeStackSurfaces(calculatePositionForInsetBounds(),
|
||||
mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
|
||||
mSplitLayout.getSnapAlgorithm().getMiddleTarget());
|
||||
mSplitLayout.getSnapAlgorithm().getMiddleTarget(),
|
||||
null /* transaction */);
|
||||
}
|
||||
|
||||
void onRecentsDrawn() {
|
||||
|
||||
@@ -200,8 +200,7 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
|
||||
Log.d(TAG, " was in split, so this means leave it "
|
||||
+ mPrimary.topActivityType + " " + mSecondary.topActivityType);
|
||||
}
|
||||
WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */);
|
||||
mDivider.onSplitDismissed();
|
||||
mDivider.startDismissSplit();
|
||||
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
|
||||
// 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.
|
||||
|
||||
@@ -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.ActivityTaskManager;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.WindowContainerToken;
|
||||
@@ -35,6 +37,7 @@ import android.window.WindowContainerTransaction;
|
||||
import android.window.WindowOrganizer;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.systemui.TransactionPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -49,8 +52,6 @@ public class WindowManagerProxy {
|
||||
private static final String TAG = "WindowManagerProxy";
|
||||
private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
|
||||
|
||||
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
|
||||
|
||||
@GuardedBy("mDockedRect")
|
||||
private final Rect mDockedRect = new Rect();
|
||||
|
||||
@@ -61,6 +62,8 @@ public class WindowManagerProxy {
|
||||
|
||||
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final SyncTransactionQueue mSyncTransactionQueue;
|
||||
|
||||
private final Runnable mSetTouchableRegionRunnable = new Runnable() {
|
||||
@Override
|
||||
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() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
void dismissOrMaximizeDocked(
|
||||
final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
|
||||
mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
|
||||
void dismissOrMaximizeDocked(final SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout,
|
||||
final boolean dismissOrMaximize) {
|
||||
mExecutor.execute(() -> applyDismissSplit(tiles, layout, dismissOrMaximize));
|
||||
}
|
||||
|
||||
public void setResizing(final boolean resizing) {
|
||||
@@ -163,7 +163,7 @@ public class WindowManagerProxy {
|
||||
*
|
||||
* @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
|
||||
// getAllStackInfos and before reparent (even if unlikely) are placed
|
||||
// correctly.
|
||||
@@ -174,6 +174,8 @@ public class WindowManagerProxy {
|
||||
if (rootTasks.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
ActivityManager.RunningTaskInfo topHomeTask = null;
|
||||
boolean homeIsTop = false;
|
||||
for (int i = rootTasks.size() - 1; i >= 0; --i) {
|
||||
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
|
||||
// Only move resizeable task to split secondary. WM will just ignore this anyways...
|
||||
@@ -183,16 +185,25 @@ public class WindowManagerProxy {
|
||||
!= WINDOWING_MODE_FULLSCREEN) {
|
||||
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 */);
|
||||
}
|
||||
// Move the secondary split-forward.
|
||||
wct.reorder(tiles.mSecondary.token, true /* onTop */);
|
||||
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;
|
||||
}
|
||||
|
||||
private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
|
||||
static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
|
||||
final int atype = ti.configuration.windowConfiguration.getActivityType();
|
||||
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
|
||||
* 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
|
||||
// before reparent (pretty unlikely) are put into fullscreen.
|
||||
TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
|
||||
@@ -229,6 +241,7 @@ public class WindowManagerProxy {
|
||||
wct.reparent(primaryChildren.get(i).token, null /* parent */,
|
||||
true /* onTop */);
|
||||
}
|
||||
boolean homeOnTop = false;
|
||||
// Don't need to worry about home tasks because they are already in the "proper"
|
||||
// order within the secondary split.
|
||||
for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
|
||||
@@ -236,8 +249,31 @@ public class WindowManagerProxy {
|
||||
wct.reparent(ti.token, null /* parent */, true /* onTop */);
|
||||
if (isHomeOrRecentTask(ti)) {
|
||||
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 {
|
||||
// Maximize, so move non-home secondary split first
|
||||
for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
|
||||
@@ -267,6 +303,29 @@ public class WindowManagerProxy {
|
||||
}
|
||||
// Reset focusable to true
|
||||
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.OP_NONE;
|
||||
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.content.res.Configuration.ORIENTATION_LANDSCAPE;
|
||||
import static android.os.PowerManager.DRAW_WAKE_LOCK;
|
||||
@@ -3757,6 +3758,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
if (!inSplitScreenWindowingMode() && !inFreeformWindowingMode()) {
|
||||
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) {
|
||||
// Floating windows never enter drag resize mode.
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user