diff options
| author | 2021-10-05 19:42:20 +0100 | |
|---|---|---|
| committer | 2021-10-07 14:25:25 +0100 | |
| commit | f96ed6b6a152d3d4a55eb918b1143592dc4dcc01 (patch) | |
| tree | 7a8c996afeb5dbf78e0a61da58127e844f57a856 | |
| parent | 49327062c28a32e7b77711a4f54f2ef794d4d78a (diff) | |
Add split screen unfold animation
Creates controllers in the Shell that manipulate
SurfaceControl leashes of the current active
split screen surfaces based on the current
unfold transition progress.
Bug: 193795566
Test: manual
Change-Id: Ib80807c632444b2939e9b075fb24fd40cc14648a
15 files changed, 573 insertions, 91 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java index 08ab85cab97b..fc1b704e95ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java @@ -16,9 +16,6 @@ package com.android.wm.shell.fullscreen; -import static android.graphics.Color.blue; -import static android.graphics.Color.green; -import static android.graphics.Color.red; import static android.util.MathUtils.lerp; import static android.view.Display.DEFAULT_DISPLAY; @@ -36,12 +33,11 @@ import android.view.InsetsState; import android.view.SurfaceControl; import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.wm.shell.R; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; +import com.android.wm.shell.unfold.UnfoldBackgroundController; import java.util.concurrent.Executor; @@ -59,21 +55,17 @@ public final class FullscreenUnfoldController implements UnfoldListener, private static final float VERTICAL_START_MARGIN = 0.03f; private static final float END_SCALE = 1f; private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2; - private static final int BACKGROUND_LAYER_Z_INDEX = -1; - private final Context mContext; private final Executor mExecutor; private final ShellUnfoldProgressProvider mProgressProvider; - private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; private final DisplayInsetsController mDisplayInsetsController; private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); + private final UnfoldBackgroundController mBackgroundController; - private SurfaceControl mBackgroundLayer; private InsetsSource mTaskbarInsetsSource; private final float mWindowCornerRadiusPx; - private final float[] mBackgroundColor; private final float mExpandedTaskBarHeight; private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); @@ -81,19 +73,17 @@ public final class FullscreenUnfoldController implements UnfoldListener, public FullscreenUnfoldController( @NonNull Context context, @NonNull Executor executor, + @NonNull UnfoldBackgroundController backgroundController, @NonNull ShellUnfoldProgressProvider progressProvider, - @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, @NonNull DisplayInsetsController displayInsetsController ) { - mContext = context; mExecutor = executor; - mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; mProgressProvider = progressProvider; mDisplayInsetsController = displayInsetsController; mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.taskbar_frame_height); - mBackgroundColor = getBackgroundColor(); + mBackgroundController = backgroundController; } /** @@ -108,7 +98,7 @@ public final class FullscreenUnfoldController implements UnfoldListener, public void onStateChangeProgress(float progress) { if (mAnimationContextByTaskId.size() == 0) return; - ensureBackground(); + mBackgroundController.ensureBackground(mTransaction); for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { final AnimationContext context = mAnimationContextByTaskId.valueAt(i); @@ -135,7 +125,7 @@ public final class FullscreenUnfoldController implements UnfoldListener, resetSurface(context); } - removeBackground(); + mBackgroundController.removeBackground(mTransaction); mTransaction.apply(); } @@ -178,7 +168,7 @@ public final class FullscreenUnfoldController implements UnfoldListener, } if (mAnimationContextByTaskId.size() == 0) { - removeBackground(); + mBackgroundController.removeBackground(mTransaction); } mTransaction.apply(); @@ -194,39 +184,6 @@ public final class FullscreenUnfoldController implements UnfoldListener, (float) context.mTaskInfo.positionInParent.y); } - private void ensureBackground() { - if (mBackgroundLayer != null) return; - - SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder() - .setName("app-unfold-background") - .setCallsite("AppUnfoldTransitionController") - .setColorLayer(); - mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder); - mBackgroundLayer = colorLayerBuilder.build(); - - mTransaction - .setColor(mBackgroundLayer, mBackgroundColor) - .show(mBackgroundLayer) - .setLayer(mBackgroundLayer, BACKGROUND_LAYER_Z_INDEX); - } - - private void removeBackground() { - if (mBackgroundLayer == null) return; - if (mBackgroundLayer.isValid()) { - mTransaction.remove(mBackgroundLayer); - } - mBackgroundLayer = null; - } - - private float[] getBackgroundColor() { - int colorInt = mContext.getResources().getColor(R.color.unfold_transition_background); - return new float[]{ - (float) red(colorInt) / 255.0F, - (float) green(colorInt) / 255.0F, - (float) blue(colorInt) / 255.0F - }; - } - private class AnimationContext { final SurfaceControl mLeash; final Rect mStartCropRect = new Rect(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index d0998eb57633..7f82ebde77af 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import android.annotation.Nullable; import android.graphics.Rect; import android.view.SurfaceSession; import android.window.WindowContainerToken; @@ -38,8 +39,10 @@ class MainStage extends StageTaskListener { MainStage(ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); + SurfaceSession surfaceSession, + @Nullable StageTaskUnfoldController stageTaskUnfoldController) { + super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, + stageTaskUnfoldController); } boolean isActive() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index 0e7ccd3515c4..dc8fb9fbd7a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -46,8 +46,10 @@ class SideStage extends StageTaskListener implements SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); + SurfaceSession surfaceSession, + @Nullable StageTaskUnfoldController stageTaskUnfoldController) { + super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, + stageTaskUnfoldController); mContext = context; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index ac68b3b8a6a9..0d5271924375 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -68,8 +68,11 @@ import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Optional; import java.util.concurrent.Executor; +import javax.inject.Provider; + /** * Class manages split-screen multitasking mode and implements the main interface * {@link SplitScreen}. @@ -91,6 +94,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private final Transitions mTransitions; private final TransactionPool mTransactionPool; private final SplitscreenEventLogger mLogger; + private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider; private StageCoordinator mStageCoordinator; @@ -99,7 +103,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellExecutor mainExecutor, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - Transitions transitions, TransactionPool transactionPool) { + Transitions transitions, TransactionPool transactionPool, + Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mContext = context; @@ -109,6 +114,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mDisplayInsetsController = displayInsetsController; mTransitions = transitions; mTransactionPool = transactionPool; + mUnfoldControllerProvider = unfoldControllerProvider; mLogger = new SplitscreenEventLogger(); } @@ -131,7 +137,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, // TODO: Multi-display mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, - mDisplayInsetsController, mTransitions, mTransactionPool, mLogger); + mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, + mUnfoldControllerProvider); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index d7a6cfff6c6f..c3e9d54ce29f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -95,6 +95,9 @@ import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Optional; + +import javax.inject.Provider; /** * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and @@ -121,8 +124,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final MainStage mMainStage; private final StageListenerImpl mMainStageListener = new StageListenerImpl(); + private final StageTaskUnfoldController mMainUnfoldController; private final SideStage mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); + private final StageTaskUnfoldController mSideUnfoldController; @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -179,26 +184,32 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, Transitions transitions, - TransactionPool transactionPool, SplitscreenEventLogger logger) { + TransactionPool transactionPool, SplitscreenEventLogger logger, + Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; mRootTDAOrganizer = rootTDAOrganizer; mTaskOrganizer = taskOrganizer; mLogger = logger; + mMainUnfoldController = unfoldControllerProvider.get().orElse(null); + mSideUnfoldController = unfoldControllerProvider.get().orElse(null); + mMainStage = new MainStage( mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue, - mSurfaceSession); + mSurfaceSession, + mMainUnfoldController); mSideStage = new SideStage( mContext, mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue, - mSurfaceSession); + mSurfaceSession, + mSideUnfoldController); mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage); @@ -218,7 +229,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger) { + SplitscreenEventLogger logger, + Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -232,6 +244,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout = splitLayout; mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, mOnTransitionAnimationComplete); + mMainUnfoldController = unfoldControllerProvider.get().orElse(null); + mSideUnfoldController = unfoldControllerProvider.get().orElse(null); mLogger = logger; transitions.addHandler(this); } @@ -460,6 +474,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, onLayoutChanged(mSplitLayout); } else { updateWindowBounds(mSplitLayout, wct); + updateUnfoldBounds(); } } } @@ -603,6 +618,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final SplitScreen.SplitScreenListener l = mListeners.get(i); l.onSplitVisibilityChanged(mDividerVisible); } + + if (mMainUnfoldController != null && mSideUnfoldController != null) { + mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible); + mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible); + } } private void onStageRootTaskAppeared(StageListenerImpl stageListener) { @@ -641,6 +661,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDividerVisible = visible; if (visible) { mSplitLayout.init(); + updateUnfoldBounds(); } else { mSplitLayout.release(); } @@ -784,12 +805,20 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onLayoutChanged(SplitLayout layout) { final WindowContainerTransaction wct = new WindowContainerTransaction(); updateWindowBounds(layout, wct); + updateUnfoldBounds(); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); mSideStage.setOutlineVisibility(true); mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } + private void updateUnfoldBounds() { + if (mMainUnfoldController != null && mSideUnfoldController != null) { + mMainUnfoldController.onLayoutChanged(getMainStageBounds()); + mSideUnfoldController.onLayoutChanged(getSideStageBounds()); + } + } + /** * Populates `wct` with operations that match the split windows to the current layout. * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied @@ -846,6 +875,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, mDisplayImeController, mTaskOrganizer); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); + + if (mMainUnfoldController != null && mSideUnfoldController != null) { + mMainUnfoldController.init(); + mSideUnfoldController.init(); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 4140332f50a3..84d570f4be44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.annotation.CallSuper; +import android.annotation.Nullable; import android.app.ActivityManager; import android.graphics.Point; import android.graphics.Rect; @@ -80,12 +81,16 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>(); private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>(); + private final StageTaskUnfoldController mStageTaskUnfoldController; + StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession) { + SurfaceSession surfaceSession, + @Nullable StageTaskUnfoldController stageTaskUnfoldController) { mCallbacks = callbacks; mSyncQueue = syncQueue; mSurfaceSession = surfaceSession; + mStageTaskUnfoldController = stageTaskUnfoldController; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); } @@ -158,6 +163,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + "\n mRootTaskInfo: " + mRootTaskInfo); } + + if (mStageTaskUnfoldController != null) { + mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash); + } } @Override @@ -210,6 +219,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + "\n mRootTaskInfo: " + mRootTaskInfo); } + + if (mStageTaskUnfoldController != null) { + mStageTaskUnfoldController.onTaskVanished(taskInfo); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java new file mode 100644 index 000000000000..e904f6a9e22c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskUnfoldController.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2021 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.wm.shell.splitscreen; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.animation.RectEvaluator; +import android.animation.TypeEvaluator; +import android.annotation.NonNull; +import android.app.ActivityManager; +import android.content.Context; +import android.graphics.Rect; +import android.util.SparseArray; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; + +import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; +import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; +import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; +import com.android.wm.shell.unfold.UnfoldBackgroundController; + +import java.util.concurrent.Executor; + +/** + * Controls transformations of the split screen task surfaces in response + * to the unfolding/folding action on foldable devices + */ +public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener { + + private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); + private static final float CROPPING_START_MARGIN_FRACTION = 0.05f; + + private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); + private final ShellUnfoldProgressProvider mUnfoldProgressProvider; + private final DisplayInsetsController mDisplayInsetsController; + private final UnfoldBackgroundController mBackgroundController; + private final Executor mExecutor; + private final int mExpandedTaskBarHeight; + private final float mWindowCornerRadiusPx; + private final Rect mStageBounds = new Rect(); + private final TransactionPool mTransactionPool; + + private InsetsSource mTaskbarInsetsSource; + private boolean mBothStagesVisible; + + public StageTaskUnfoldController(@NonNull Context context, + @NonNull TransactionPool transactionPool, + @NonNull ShellUnfoldProgressProvider unfoldProgressProvider, + @NonNull DisplayInsetsController displayInsetsController, + @NonNull UnfoldBackgroundController backgroundController, + @NonNull Executor executor) { + mUnfoldProgressProvider = unfoldProgressProvider; + mTransactionPool = transactionPool; + mExecutor = executor; + mBackgroundController = backgroundController; + mDisplayInsetsController = displayInsetsController; + mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); + mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.taskbar_frame_height); + } + + /** + * Initializes the controller, starts listening for the external events + */ + public void init() { + mUnfoldProgressProvider.addListener(mExecutor, this); + mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); + } + + @Override + public void insetsChanged(InsetsState insetsState) { + mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + AnimationContext context = mAnimationContextByTaskId.valueAt(i); + context.update(); + } + } + + /** + * Called when split screen task appeared + * @param taskInfo info for the appeared task + * @param leash surface leash for the appeared task + */ + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + AnimationContext context = new AnimationContext(leash); + mAnimationContextByTaskId.put(taskInfo.taskId, context); + } + + /** + * Called when a split screen task vanished + * @param taskInfo info for the vanished task + */ + public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId); + if (context != null) { + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + resetSurface(transaction, context); + transaction.apply(); + mTransactionPool.release(transaction); + } + mAnimationContextByTaskId.remove(taskInfo.taskId); + } + + @Override + public void onStateChangeProgress(float progress) { + if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return; + + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + mBackgroundController.ensureBackground(transaction); + + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + AnimationContext context = mAnimationContextByTaskId.valueAt(i); + + context.mCurrentCropRect.set(RECT_EVALUATOR + .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); + + transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) + .setCornerRadius(context.mLeash, mWindowCornerRadiusPx); + } + + transaction.apply(); + + mTransactionPool.release(transaction); + } + + @Override + public void onStateChangeFinished() { + resetTransformations(); + } + + /** + * Called when split screen visibility changes + * @param bothStagesVisible true if both stages of the split screen are visible + */ + public void onSplitVisibilityChanged(boolean bothStagesVisible) { + mBothStagesVisible = bothStagesVisible; + if (!bothStagesVisible) { + resetTransformations(); + } + } + + /** + * Called when split screen stage bounds changed + * @param bounds new bounds for this stage + */ + public void onLayoutChanged(Rect bounds) { + mStageBounds.set(bounds); + + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + context.update(); + } + } + + private void resetTransformations() { + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + resetSurface(transaction, context); + } + mBackgroundController.removeBackground(transaction); + transaction.apply(); + + mTransactionPool.release(transaction); + } + + private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) { + transaction + .setWindowCrop(context.mLeash, null) + .setCornerRadius(context.mLeash, 0.0F); + } + + private class AnimationContext { + final SurfaceControl mLeash; + final Rect mStartCropRect = new Rect(); + final Rect mEndCropRect = new Rect(); + final Rect mCurrentCropRect = new Rect(); + + private AnimationContext(SurfaceControl leash) { + this.mLeash = leash; + update(); + } + + private void update() { + mStartCropRect.set(mStageBounds); + + if (mTaskbarInsetsSource != null) { + // Only insets the cropping window with taskbar when taskbar is expanded + if (mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { + mStartCropRect.inset(mTaskbarInsetsSource + .calculateVisibleInsets(mStartCropRect)); + } + } + + // Offset to surface coordinates as layout bounds are in screen coordinates + mStartCropRect.offsetTo(0, 0); + + mEndCropRect.set(mStartCropRect); + + int maxSize = Math.max(mEndCropRect.width(), mEndCropRect.height()); + int margin = (int) (maxSize * CROPPING_START_MARGIN_FRACTION); + mStartCropRect.inset(margin, margin, margin, margin); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java new file mode 100644 index 000000000000..9faf454261d3 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 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.wm.shell.unfold; + +import static android.graphics.Color.blue; +import static android.graphics.Color.green; +import static android.graphics.Color.red; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.annotation.NonNull; +import android.content.Context; +import android.view.SurfaceControl; + +import com.android.wm.shell.R; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; + +/** + * Controls background color layer for the unfold animations + */ +public class UnfoldBackgroundController { + + private static final int BACKGROUND_LAYER_Z_INDEX = -1; + + private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; + private final float[] mBackgroundColor; + private SurfaceControl mBackgroundLayer; + + public UnfoldBackgroundController( + @NonNull Context context, + @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mBackgroundColor = getBackgroundColor(context); + } + + /** + * Ensures that unfold animation background color layer is present, + * @param transaction where we should add the background if it is not added + */ + public void ensureBackground(@NonNull SurfaceControl.Transaction transaction) { + if (mBackgroundLayer != null) return; + + SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder() + .setName("app-unfold-background") + .setCallsite("AppUnfoldTransitionController") + .setColorLayer(); + mRootTaskDisplayAreaOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, colorLayerBuilder); + mBackgroundLayer = colorLayerBuilder.build(); + + transaction + .setColor(mBackgroundLayer, mBackgroundColor) + .show(mBackgroundLayer) + .setLayer(mBackgroundLayer, BACKGROUND_LAYER_Z_INDEX); + } + + /** + * Ensures that the background is not visible + * @param transaction as part of which the removal will happen if needed + */ + public void removeBackground(@NonNull SurfaceControl.Transaction transaction) { + if (mBackgroundLayer == null) return; + if (mBackgroundLayer.isValid()) { + transaction.remove(mBackgroundLayer); + } + mBackgroundLayer = null; + } + + private float[] getBackgroundColor(Context context) { + int colorInt = context.getResources().getColor(R.color.unfold_transition_background); + return new float[]{ + (float) red(colorInt) / 255.0F, + (float) green(colorInt) / 255.0F, + (float) blue(colorInt) / 255.0F + }; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java index 1bb5fd1e49e7..12b547a765be 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java @@ -56,7 +56,7 @@ public class MainStageTests { MockitoAnnotations.initMocks(this); mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue, - mSurfaceSession); + mSurfaceSession, null); mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java index 3a2516ec9366..838aa811bb87 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java @@ -64,7 +64,7 @@ public class SideStageTests extends ShellTestCase { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue, mSurfaceSession); + mSyncQueue, mSurfaceSession, null); mSideStage.onTaskAppeared(mRootTask, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index 736566e5b4d3..f90af239db01 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; - import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -39,6 +38,10 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.transition.Transitions; +import java.util.Optional; + +import javax.inject.Provider; + public class SplitTestUtils { static SplitLayout createMockSplitLayout() { @@ -68,10 +71,11 @@ public class SplitTestUtils { MainStage mainStage, SideStage sideStage, DisplayImeController imeController, DisplayInsetsController insetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger) { + SplitscreenEventLogger logger, + Provider<Optional<StageTaskUnfoldController>> unfoldController) { super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage, sideStage, imeController, insetsController, splitLayout, transitions, - transactionPool, logger); + transactionPool, logger, unfoldController); // Prepare default TaskDisplayArea for testing. mDisplayAreaInfo = new DisplayAreaInfo( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 8dce454eb078..be103863a0f2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -74,6 +74,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.util.Optional; + /** Tests for {@link StageCoordinator} */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -106,16 +108,16 @@ public class SplitTransitionTests extends ShellTestCase { doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire(); mSplitLayout = SplitTestUtils.createMockSplitLayout(); mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, - mLogger); + mLogger, Optional::empty); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class)) .when(mTransitions).startTransition(anyInt(), any(), any()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index d930d4ca5dd4..a39d331ca884 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -18,18 +18,20 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.Display.DEFAULT_DISPLAY; - import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; - +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.graphics.Rect; +import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -43,6 +45,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.transition.Transitions; import org.junit.Before; @@ -51,29 +54,55 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -/** Tests for {@link StageCoordinator} */ +import java.util.Optional; + +import javax.inject.Provider; + +/** + * Tests for {@link StageCoordinator} + */ @SmallTest @RunWith(AndroidJUnit4.class) public class StageCoordinatorTests extends ShellTestCase { - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private RootTaskDisplayAreaOrganizer mRootTDAOrganizer; - @Mock private MainStage mMainStage; - @Mock private SideStage mSideStage; - @Mock private DisplayImeController mDisplayImeController; - @Mock private DisplayInsetsController mDisplayInsetsController; - @Mock private Transitions mTransitions; - @Mock private TransactionPool mTransactionPool; - @Mock private SplitscreenEventLogger mLogger; + @Mock + private ShellTaskOrganizer mTaskOrganizer; + @Mock + private SyncTransactionQueue mSyncQueue; + @Mock + private RootTaskDisplayAreaOrganizer mRootTDAOrganizer; + @Mock + private MainStage mMainStage; + @Mock + private SideStage mSideStage; + @Mock + private StageTaskUnfoldController mMainUnfoldController; + @Mock + private StageTaskUnfoldController mSideUnfoldController; + @Mock + private SplitLayout mSplitLayout; + @Mock + private DisplayImeController mDisplayImeController; + @Mock + private DisplayInsetsController mDisplayInsetsController; + @Mock + private Transitions mTransitions; + @Mock + private TransactionPool mTransactionPool; + @Mock + private SplitscreenEventLogger mLogger; + + private final Rect mBounds1 = new Rect(10, 20, 30, 40); + private final Rect mBounds2 = new Rect(5, 10, 15, 20); + private StageCoordinator mStageCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); - mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, - mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, - mDisplayImeController, mDisplayInsetsController, null /* splitLayout */, - mTransitions, mTransactionPool, mLogger); + mStageCoordinator = createStageCoordinator(/* splitLayout */ null); + + when(mSplitLayout.getBounds1()).thenReturn(mBounds1); + when(mSplitLayout.getBounds2()).thenReturn(mBounds2); } @Test @@ -88,6 +117,38 @@ public class StageCoordinatorTests extends ShellTestCase { } @Test + public void testDisplayAreaAppeared_initializesUnfoldControllers() { + mStageCoordinator.onDisplayAreaAppeared(mock(DisplayAreaInfo.class)); + + verify(mMainUnfoldController).init(); + verify(mSideUnfoldController).init(); + } + + @Test + public void testLayoutChanged_topLeftSplitPosition_updatesUnfoldStageBounds() { + mStageCoordinator = createStageCoordinator(mSplitLayout); + mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null); + clearInvocations(mMainUnfoldController, mSideUnfoldController); + + mStageCoordinator.onLayoutChanged(mSplitLayout); + + verify(mMainUnfoldController).onLayoutChanged(mBounds2); + verify(mSideUnfoldController).onLayoutChanged(mBounds1); + } + + @Test + public void testLayoutChanged_bottomRightSplitPosition_updatesUnfoldStageBounds() { + mStageCoordinator = createStageCoordinator(mSplitLayout); + mStageCoordinator.setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, null); + clearInvocations(mMainUnfoldController, mSideUnfoldController); + + mStageCoordinator.onLayoutChanged(mSplitLayout); + + verify(mMainUnfoldController).onLayoutChanged(mBounds1); + verify(mSideUnfoldController).onLayoutChanged(mBounds2); + } + + @Test public void testRemoveFromSideStage() { final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); @@ -131,4 +192,27 @@ public class StageCoordinatorTests extends ShellTestCase { verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(true)); verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false)); } + + private StageCoordinator createStageCoordinator(SplitLayout splitLayout) { + return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, + mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, + mDisplayImeController, mDisplayInsetsController, splitLayout, + mTransitions, mTransactionPool, mLogger, new UnfoldControllerProvider()); + } + + private class UnfoldControllerProvider implements + Provider<Optional<StageTaskUnfoldController>> { + + private boolean isMain = true; + + @Override + public Optional<StageTaskUnfoldController> get() { + if (isMain) { + isMain = false; + return Optional.of(mMainUnfoldController); + } else { + return Optional.of(mSideUnfoldController); + } + } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 0916dd1f71bd..a5746a49da2b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -60,6 +61,7 @@ public final class StageTaskListenerTests { @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private StageTaskListener.StageListenerCallbacks mCallbacks; @Mock private SyncTransactionQueue mSyncQueue; + @Mock private StageTaskUnfoldController mStageTaskUnfoldController; @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor; private SurfaceSession mSurfaceSession = new SurfaceSession(); private SurfaceControl mSurfaceControl; @@ -74,7 +76,8 @@ public final class StageTaskListenerTests { DEFAULT_DISPLAY, mCallbacks, mSyncQueue, - mSurfaceSession); + mSurfaceSession, + mStageTaskUnfoldController); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); @@ -111,6 +114,28 @@ public final class StageTaskListenerTests { verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true)); } + @Test + public void testTaskAppeared_notifiesUnfoldListener() { + final ActivityManager.RunningTaskInfo task = + new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build(); + + mStageTaskListener.onTaskAppeared(task, mSurfaceControl); + + verify(mStageTaskUnfoldController).onTaskAppeared(eq(task), eq(mSurfaceControl)); + } + + @Test + public void testTaskVanished_notifiesUnfoldListener() { + final ActivityManager.RunningTaskInfo task = + new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build(); + mStageTaskListener.onTaskAppeared(task, mSurfaceControl); + clearInvocations(mStageTaskUnfoldController); + + mStageTaskListener.onTaskVanished(task); + + verify(mStageTaskUnfoldController).onTaskVanished(eq(task)); + } + @Test(expected = IllegalArgumentException.class) public void testUnknownTaskVanished() { final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index ff263109d555..514a9035a9ef 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -73,6 +73,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.splitscreen.StageTaskUnfoldController; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.startingsurface.StartingWindowController; import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; @@ -81,10 +82,14 @@ import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController; import com.android.wm.shell.transition.ShellTransitions; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; +import com.android.wm.shell.unfold.UnfoldBackgroundController; import java.util.Optional; +import javax.inject.Provider; + import dagger.BindsOptionalOf; +import dagger.Lazy; import dagger.Module; import dagger.Provides; @@ -234,16 +239,48 @@ public abstract class WMShellBaseModule { static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController( Context context, Optional<ShellUnfoldProgressProvider> progressProvider, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Lazy<UnfoldBackgroundController> unfoldBackgroundController, DisplayInsetsController displayInsetsController, @ShellMainThread ShellExecutor mainExecutor ) { return progressProvider.map(shellUnfoldTransitionProgressProvider -> new FullscreenUnfoldController(context, mainExecutor, - shellUnfoldTransitionProgressProvider, rootTaskDisplayAreaOrganizer, + unfoldBackgroundController.get(), shellUnfoldTransitionProgressProvider, displayInsetsController)); } + @Provides + static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController( + Optional<ShellUnfoldProgressProvider> progressProvider, + Context context, + TransactionPool transactionPool, + Lazy<UnfoldBackgroundController> unfoldBackgroundController, + DisplayInsetsController displayInsetsController, + @ShellMainThread ShellExecutor mainExecutor + ) { + return progressProvider.map(shellUnfoldTransitionProgressProvider -> + new StageTaskUnfoldController( + context, + transactionPool, + shellUnfoldTransitionProgressProvider, + displayInsetsController, + unfoldBackgroundController.get(), + mainExecutor + )); + } + + @WMSingleton + @Provides + static UnfoldBackgroundController provideUnfoldBackgroundController( + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + Context context + ) { + return new UnfoldBackgroundController( + context, + rootTaskDisplayAreaOrganizer + ); + } + // // Freeform (optional feature) // @@ -401,12 +438,13 @@ public abstract class WMShellBaseModule { @ShellMainThread ShellExecutor mainExecutor, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, Transitions transitions, - TransactionPool transactionPool) { + TransactionPool transactionPool, + Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) { if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) { return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context, rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController, displayInsetsController, transitions, - transactionPool)); + transactionPool, stageTaskUnfoldControllerProvider)); } else { return Optional.empty(); } |