diff options
14 files changed, 0 insertions, 3814 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl deleted file mode 100644 index 45f6d3c8b154..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.stagesplit; - -import android.app.PendingIntent; -import android.content.Intent; -import android.os.Bundle; -import android.os.UserHandle; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.window.RemoteTransition; - -import com.android.wm.shell.stagesplit.ISplitScreenListener; - -/** - * Interface that is exposed to remote callers to manipulate the splitscreen feature. - */ -interface ISplitScreen { - - /** - * Registers a split screen listener. - */ - oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1; - - /** - * Unregisters a split screen listener. - */ - oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2; - - /** - * Hides the side-stage if it is currently visible. - */ - oneway void setSideStageVisibility(boolean visible) = 3; - - /** - * Removes a task from the side stage. - */ - oneway void removeFromSideStage(int taskId) = 4; - - /** - * Removes the split-screen stages and leaving indicated task to top. Passing INVALID_TASK_ID - * to indicate leaving no top task after leaving split-screen. - */ - oneway void exitSplitScreen(int toTopTaskId) = 5; - - /** - * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. - */ - oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6; - - /** - * Starts a task in a stage. - */ - oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7; - - /** - * Starts a shortcut in a stage. - */ - oneway void startShortcut(String packageName, String shortcutId, int stage, int position, - in Bundle options, in UserHandle user) = 8; - - /** - * Starts an activity in a stage. - */ - oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage, - int position, in Bundle options) = 9; - - /** - * Starts tasks simultaneously in one transition. - */ - oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId, - in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10; - - /** - * Version of startTasks using legacy transition system. - */ - oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, - int sideTaskId, in Bundle sideOptions, int sidePosition, - in RemoteAnimationAdapter adapter) = 11; - - /** - * Blocking call that notifies and gets additional split-screen targets when entering - * recents (for example: the dividerBar). - * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled). - * @param appTargets apps that will be re-parented to display area - */ - RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, - in RemoteAnimationTarget[] appTargets) = 12; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl deleted file mode 100644 index 46e4299f99fa..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.stagesplit; - -/** - * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks. - */ -oneway interface ISplitScreenListener { - - /** - * Called when the stage position changes. - */ - void onStagePositionChanged(int stage, int position); - - /** - * Called when a task changes stages. - */ - void onTaskStageChanged(int taskId, int stage, boolean visible); -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java deleted file mode 100644 index 83855be91e04..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.wm.shell.stagesplit; - -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; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.SyncTransactionQueue; - -/** - * Main stage for split-screen mode. When split-screen is active all standard activity types launch - * on the main stage, except for task that are explicitly pinned to the {@link SideStage}. - * @see StageCoordinator - */ -class MainStage extends StageTaskListener { - private static final String TAG = MainStage.class.getSimpleName(); - - private boolean mIsActive = false; - - MainStage(ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, - @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - stageTaskUnfoldController); - } - - boolean isActive() { - return mIsActive; - } - - void activate(Rect rootBounds, WindowContainerTransaction wct) { - if (mIsActive) return; - - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setBounds(rootToken, rootBounds) - .setWindowingMode(rootToken, WINDOWING_MODE_MULTI_WINDOW) - .setLaunchRoot( - rootToken, - CONTROLLED_WINDOWING_MODES, - CONTROLLED_ACTIVITY_TYPES) - .reparentTasks( - null /* currentParent */, - rootToken, - CONTROLLED_WINDOWING_MODES, - CONTROLLED_ACTIVITY_TYPES, - true /* onTop */) - // Moving the root task to top after the child tasks were re-parented , or the root - // task cannot be visible and focused. - .reorder(rootToken, true /* onTop */); - - mIsActive = true; - } - - void deactivate(WindowContainerTransaction wct) { - deactivate(wct, false /* toTop */); - } - - void deactivate(WindowContainerTransaction wct, boolean toTop) { - if (!mIsActive) return; - mIsActive = false; - - if (mRootTaskInfo == null) return; - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setLaunchRoot( - rootToken, - null, - null) - .reparentTasks( - rootToken, - null /* newParent */, - CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, - CONTROLLED_ACTIVITY_TYPES, - toTop) - // We want this re-order to the bottom regardless since we are re-parenting - // all its tasks. - .reorder(rootToken, false /* onTop */); - } - - void updateConfiguration(int windowingMode, Rect bounds, WindowContainerTransaction wct) { - wct.setBounds(mRootTaskInfo.token, bounds) - .setWindowingMode(mRootTaskInfo.token, windowingMode); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS deleted file mode 100644 index 264e88f32bff..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# WM shell sub-modules stagesplit owner -chenghsiuchang@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java deleted file mode 100644 index 8fbad52c630f..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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.stagesplit; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.Binder; -import android.view.IWindow; -import android.view.InsetsSource; -import android.view.InsetsState; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; -import android.widget.FrameLayout; - -import com.android.wm.shell.R; - -/** - * Handles drawing outline of the bounds of provided root surface. The outline will be drown with - * the consideration of display insets like status bar, navigation bar and display cutout. - */ -class OutlineManager extends WindowlessWindowManager { - private static final String WINDOW_NAME = "SplitOutlineLayer"; - private final Context mContext; - private final Rect mRootBounds = new Rect(); - private final Rect mTempRect = new Rect(); - private final Rect mLastOutlineBounds = new Rect(); - private final InsetsState mInsetsState = new InsetsState(); - private final int mExpandedTaskBarHeight; - private OutlineView mOutlineView; - private SurfaceControlViewHost mViewHost; - private SurfaceControl mHostLeash; - private SurfaceControl mLeash; - - OutlineManager(Context context, Configuration configuration) { - super(configuration, null /* rootSurface */, null /* hostInputToken */); - mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, - null /* options */); - mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.taskbar_frame_height); - } - - @Override - protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - b.setParent(mHostLeash); - } - - void inflate(SurfaceControl rootLeash, Rect rootBounds) { - if (mLeash != null || mViewHost != null) return; - - mHostLeash = rootLeash; - mRootBounds.set(rootBounds); - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - - final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext) - .inflate(R.layout.split_outline, null); - mOutlineView = rootLayout.findViewById(R.id.split_outline); - - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); - lp.width = mRootBounds.width(); - lp.height = mRootBounds.height(); - lp.token = new Binder(); - lp.setTitle(WINDOW_NAME); - lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports - // TRUSTED_OVERLAY for windowless window without input channel. - mViewHost.setView(rootLayout, lp); - mLeash = getSurfaceControl(mViewHost.getWindowToken()); - - drawOutline(); - } - - void release() { - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - mRootBounds.setEmpty(); - mLastOutlineBounds.setEmpty(); - mOutlineView = null; - mHostLeash = null; - mLeash = null; - } - - @Nullable - SurfaceControl getOutlineLeash() { - return mLeash; - } - - void setVisibility(boolean visible) { - if (mOutlineView != null) { - mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - } - } - - void setRootBounds(Rect rootBounds) { - if (mViewHost == null || mViewHost.getView() == null) { - return; - } - - if (!mRootBounds.equals(rootBounds)) { - WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); - lp.width = rootBounds.width(); - lp.height = rootBounds.height(); - mViewHost.relayout(lp); - mRootBounds.set(rootBounds); - drawOutline(); - } - } - - void onInsetsChanged(InsetsState insetsState) { - if (!mInsetsState.equals(insetsState)) { - mInsetsState.set(insetsState); - drawOutline(); - } - } - - private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) { - outBounds.set(rootBounds); - final InsetsSource taskBarInsetsSource = - insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); - // Only insets the divider bar with task bar when it's expanded so that the rounded corners - // will be drawn against task bar. - if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { - outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds)); - } - - // Offset the coordinate from screen based to surface based. - outBounds.offset(-rootBounds.left, -rootBounds.top); - } - - void drawOutline() { - if (mOutlineView == null) { - return; - } - - computeOutlineBounds(mRootBounds, mInsetsState, mTempRect); - if (mTempRect.equals(mLastOutlineBounds)) { - return; - } - - ViewGroup.MarginLayoutParams lp = - (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams(); - lp.leftMargin = mTempRect.left; - lp.topMargin = mTempRect.top; - lp.width = mTempRect.width(); - lp.height = mTempRect.height(); - mOutlineView.setLayoutParams(lp); - mLastOutlineBounds.set(mTempRect); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java deleted file mode 100644 index 92b1381fc808..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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.stagesplit; - -import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT; -import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT; -import static android.view.RoundedCorner.POSITION_TOP_LEFT; -import static android.view.RoundedCorner.POSITION_TOP_RIGHT; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.util.AttributeSet; -import android.view.RoundedCorner; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.R; - -/** View for drawing split outline. */ -public class OutlineView extends View { - private final Paint mPaint = new Paint(); - private final Path mPath = new Path(); - private final float[] mRadii = new float[8]; - - public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setStrokeWidth( - getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); - mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null)); - } - - @Override - protected void onAttachedToWindow() { - // TODO(b/200850654): match the screen corners with the actual display decor. - mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT); - mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT); - mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT); - mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT); - } - - private int getCornerRadius(@RoundedCorner.Position int position) { - final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position); - return roundedCorner == null ? 0 : roundedCorner.getRadius(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (changed) { - mPath.reset(); - mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW); - } - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.drawPath(mPath, mPaint); - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java deleted file mode 100644 index 55c4f3aea19a..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.wm.shell.stagesplit; - -import android.annotation.CallSuper; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.content.Context; -import android.graphics.Rect; -import android.view.InsetsSourceControl; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.SyncTransactionQueue; - -/** - * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up - * here. All other task are launch in the {@link MainStage}. - * - * @see StageCoordinator - */ -class SideStage extends StageTaskListener implements - DisplayInsetsController.OnInsetsChangedListener { - private static final String TAG = SideStage.class.getSimpleName(); - private final Context mContext; - private OutlineManager mOutlineManager; - - SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, - @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - stageTaskUnfoldController); - mContext = context; - } - - void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, - WindowContainerTransaction wct) { - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setBounds(rootToken, rootBounds) - .reparent(task.token, rootToken, true /* onTop*/) - // Moving the root task to top after the child tasks were reparented , or the root - // task cannot be visible and focused. - .reorder(rootToken, true /* onTop */); - } - - boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { - // No matter if the root task is empty or not, moving the root to bottom because it no - // longer preserves visible child task. - wct.reorder(mRootTaskInfo.token, false /* onTop */); - if (mChildrenTaskInfo.size() == 0) return false; - wct.reparentTasks( - mRootTaskInfo.token, - null /* newParent */, - CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, - CONTROLLED_ACTIVITY_TYPES, - toTop); - return true; - } - - boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { - final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); - if (task == null) return false; - wct.reparent(task.token, newParent, false /* onTop */); - return true; - } - - @Nullable - public SurfaceControl getOutlineLeash() { - return mOutlineManager.getOutlineLeash(); - } - - @Override - @CallSuper - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - super.onTaskAppeared(taskInfo, leash); - if (isRootTask(taskInfo)) { - mOutlineManager = new OutlineManager(mContext, taskInfo.configuration); - enableOutline(true); - } - } - - @Override - @CallSuper - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - super.onTaskInfoChanged(taskInfo); - if (isRootTask(taskInfo)) { - mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds()); - } - } - - private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) { - return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId; - } - - void enableOutline(boolean enable) { - if (mOutlineManager == null) { - return; - } - - if (enable) { - if (mRootTaskInfo != null) { - mOutlineManager.inflate(mRootLeash, - mRootTaskInfo.configuration.windowConfiguration.getBounds()); - } - } else { - mOutlineManager.release(); - } - } - - void setOutlineVisibility(boolean visible) { - mOutlineManager.setVisibility(visible); - } - - @Override - public void insetsChanged(InsetsState insetsState) { - mOutlineManager.onInsetsChanged(insetsState); - } - - @Override - public void insetsControlChanged(InsetsState insetsState, - InsetsSourceControl[] activeControls) { - insetsChanged(insetsState); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java deleted file mode 100644 index c5d231262cd2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.wm.shell.stagesplit; - -import android.annotation.IntDef; -import android.annotation.NonNull; - -import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; - -import java.util.concurrent.Executor; - -/** - * Interface to engage split-screen feature. - * TODO: Figure out which of these are actually needed outside of the Shell - */ -@ExternalThread -public interface SplitScreen { - /** - * Stage type isn't specified normally meaning to use what ever the default is. - * E.g. exit split-screen and launch the app in fullscreen. - */ - int STAGE_TYPE_UNDEFINED = -1; - /** - * The main stage type. - * @see MainStage - */ - int STAGE_TYPE_MAIN = 0; - - /** - * The side stage type. - * @see SideStage - */ - int STAGE_TYPE_SIDE = 1; - - @IntDef(prefix = { "STAGE_TYPE_" }, value = { - STAGE_TYPE_UNDEFINED, - STAGE_TYPE_MAIN, - STAGE_TYPE_SIDE - }) - @interface StageType {} - - /** Callback interface for listening to changes in a split-screen stage. */ - interface SplitScreenListener { - default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {} - default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {} - default void onSplitVisibilityChanged(boolean visible) {} - } - - /** Registers listener that gets split screen callback. */ - void registerSplitScreenListener(@NonNull SplitScreenListener listener, - @NonNull Executor executor); - - /** Unregisters listener that gets split screen callback. */ - void unregisterSplitScreenListener(@NonNull SplitScreenListener listener); - - /** - * Returns a binder that can be passed to an external process to manipulate SplitScreen. - */ - default ISplitScreen createExternalInterface() { - return null; - } - - /** - * Called when the keyguard occluded state changes. - * @param occluded Indicates if the keyguard is now occluded. - */ - void onKeyguardOccludedChanged(boolean occluded); - - /** - * Called when the visibility of the keyguard changes. - * @param showing Indicates if the keyguard is now visible. - */ - void onKeyguardVisibilityChanged(boolean showing); - - /** Get a string representation of a stage type */ - static String stageTypeToString(@StageType int stage) { - switch (stage) { - case STAGE_TYPE_UNDEFINED: return "UNDEFINED"; - case STAGE_TYPE_MAIN: return "MAIN"; - case STAGE_TYPE_SIDE: return "SIDE"; - default: return "UNKNOWN(" + stage + ")"; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java deleted file mode 100644 index 07174051a344..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * 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.wm.shell.stagesplit; - -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.RemoteAnimationTarget.MODE_OPENING; - -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; - -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.LauncherApps; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.Slog; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.view.WindowManager; -import android.window.RemoteTransition; -import android.window.WindowContainerTransaction; - -import androidx.annotation.BinderThread; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.logging.InstanceId; -import com.android.internal.util.FrameworkStatsLog; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.RemoteCallable; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.draganddrop.DragAndDropPolicy; -import com.android.wm.shell.transition.LegacyTransitions; -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}. - * @see StageCoordinator - */ -// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen. -public class SplitScreenController implements DragAndDropPolicy.Starter, - RemoteCallable<SplitScreenController> { - private static final String TAG = SplitScreenController.class.getSimpleName(); - - private final ShellTaskOrganizer mTaskOrganizer; - private final SyncTransactionQueue mSyncQueue; - private final Context mContext; - private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; - private final ShellExecutor mMainExecutor; - private final SplitScreenImpl mImpl = new SplitScreenImpl(); - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - private final Transitions mTransitions; - private final TransactionPool mTransactionPool; - private final SplitscreenEventLogger mLogger; - private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider; - - private StageCoordinator mStageCoordinator; - - public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue, Context context, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, - ShellExecutor mainExecutor, DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, - Transitions transitions, TransactionPool transactionPool, - Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { - mTaskOrganizer = shellTaskOrganizer; - mSyncQueue = syncQueue; - mContext = context; - mRootTDAOrganizer = rootTDAOrganizer; - mMainExecutor = mainExecutor; - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mTransitions = transitions; - mTransactionPool = transactionPool; - mUnfoldControllerProvider = unfoldControllerProvider; - mLogger = new SplitscreenEventLogger(); - } - - public SplitScreen asSplitScreen() { - return mImpl; - } - - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - - public void onOrganizerRegistered() { - if (mStageCoordinator == null) { - // TODO: Multi-display - mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, - mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, - mUnfoldControllerProvider); - } - } - - public boolean isSplitScreenVisible() { - return mStageCoordinator.isSplitScreenVisible(); - } - - public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) { - final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId); - if (task == null) { - throw new IllegalArgumentException("Unknown taskId" + taskId); - } - return moveToSideStage(task, sideStagePosition); - } - - public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @SplitPosition int sideStagePosition) { - return mStageCoordinator.moveToSideStage(task, sideStagePosition); - } - - public boolean removeFromSideStage(int taskId) { - return mStageCoordinator.removeFromSideStage(taskId); - } - - public void setSideStageOutline(boolean enable) { - mStageCoordinator.setSideStageOutline(enable); - } - - public void setSideStagePosition(@SplitPosition int sideStagePosition) { - mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); - } - - public void setSideStageVisibility(boolean visible) { - mStageCoordinator.setSideStageVisibility(visible); - } - - public void enterSplitScreen(int taskId, boolean leftOrTop) { - moveToSideStage(taskId, - leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); - } - - public void exitSplitScreen(int toTopTaskId, int exitReason) { - mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason); - } - - public void onKeyguardOccludedChanged(boolean occluded) { - mStageCoordinator.onKeyguardOccludedChanged(occluded); - } - - public void onKeyguardVisibilityChanged(boolean showing) { - mStageCoordinator.onKeyguardVisibilityChanged(showing); - } - - public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide); - } - - public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { - mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds); - } - - public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) { - mStageCoordinator.registerSplitScreenListener(listener); - } - - public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) { - mStageCoordinator.unregisterSplitScreenListener(listener); - } - - public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) { - options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, - null /* wct */); - - try { - ActivityTaskManager.getService().startActivityFromRecents(taskId, options); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to launch task", e); - } - } - - public void startShortcut(String packageName, String shortcutId, @SplitPosition int position, - @Nullable Bundle options, UserHandle user) { - options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, - null /* wct */); - - try { - LauncherApps launcherApps = - mContext.getSystemService(LauncherApps.class); - launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */, - options, user); - } catch (ActivityNotFoundException e) { - Slog.e(TAG, "Failed to launch shortcut", e); - } - } - - public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, - @Nullable Bundle options) { - if (!Transitions.ENABLE_SHELL_TRANSITIONS) { - startIntentLegacy(intent, fillInIntent, position, options); - return; - } - mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options, - null /* remote */); - } - - private void startIntentLegacy(PendingIntent intent, Intent fillInIntent, - @SplitPosition int position, @Nullable Bundle options) { - LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() { - @Override - public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback, - SurfaceControl.Transaction t) { - mStageCoordinator.updateSurfaceBounds(null /* layout */, t, - false /* applyResizingOffset */); - - if (apps != null) { - for (int i = 0; i < apps.length; ++i) { - if (apps[i].mode == MODE_OPENING) { - t.show(apps[i].leash); - } - } - } - - t.apply(); - if (finishedCallback != null) { - try { - finishedCallback.onAnimationFinished(); - } catch (RemoteException e) { - Slog.e(TAG, "Error finishing legacy transition: ", e); - } - } - } - }; - WindowContainerTransaction wct = new WindowContainerTransaction(); - options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct); - wct.sendPendingIntent(intent, fillInIntent, options); - mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); - } - - RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) { - if (!isSplitScreenVisible()) return null; - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("RecentsAnimationSplitTasks") - .setHidden(false) - .setCallsite("SplitScreenController#onGoingtoRecentsLegacy"); - mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder); - SurfaceControl sc = builder.build(); - SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - - // Ensure that we order these in the parent in the right z-order as their previous order - Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex); - int layer = 1; - for (RemoteAnimationTarget appTarget : apps) { - transaction.reparent(appTarget.leash, sc); - transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left, - appTarget.screenSpaceBounds.top); - transaction.setLayer(appTarget.leash, layer++); - } - transaction.apply(); - transaction.close(); - return new RemoteAnimationTarget[]{ - mStageCoordinator.getDividerBarLegacyTarget(), - mStageCoordinator.getOutlineLegacyTarget()}; - } - - /** - * Sets drag info to be logged when splitscreen is entered. - */ - public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { - mStageCoordinator.logOnDroppedToSplit(position, dragSessionId); - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - pw.println(prefix + TAG); - if (mStageCoordinator != null) { - mStageCoordinator.dump(pw, prefix); - } - } - - /** - * The interface for calls from outside the Shell, within the host process. - */ - @ExternalThread - private class SplitScreenImpl implements SplitScreen { - private ISplitScreenImpl mISplitScreen; - private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>(); - private final SplitScreenListener mListener = new SplitScreenListener() { - @Override - public void onStagePositionChanged(int stage, int position) { - for (int i = 0; i < mExecutors.size(); i++) { - final int index = i; - mExecutors.valueAt(index).execute(() -> { - mExecutors.keyAt(index).onStagePositionChanged(stage, position); - }); - } - } - - @Override - public void onTaskStageChanged(int taskId, int stage, boolean visible) { - for (int i = 0; i < mExecutors.size(); i++) { - final int index = i; - mExecutors.valueAt(index).execute(() -> { - mExecutors.keyAt(index).onTaskStageChanged(taskId, stage, visible); - }); - } - } - - @Override - public void onSplitVisibilityChanged(boolean visible) { - for (int i = 0; i < mExecutors.size(); i++) { - final int index = i; - mExecutors.valueAt(index).execute(() -> { - mExecutors.keyAt(index).onSplitVisibilityChanged(visible); - }); - } - } - }; - - @Override - public ISplitScreen createExternalInterface() { - if (mISplitScreen != null) { - mISplitScreen.invalidate(); - } - mISplitScreen = new ISplitScreenImpl(SplitScreenController.this); - return mISplitScreen; - } - - @Override - public void onKeyguardOccludedChanged(boolean occluded) { - mMainExecutor.execute(() -> { - SplitScreenController.this.onKeyguardOccludedChanged(occluded); - }); - } - - @Override - public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) { - if (mExecutors.containsKey(listener)) return; - - mMainExecutor.execute(() -> { - if (mExecutors.size() == 0) { - SplitScreenController.this.registerSplitScreenListener(mListener); - } - - mExecutors.put(listener, executor); - }); - - executor.execute(() -> { - mStageCoordinator.sendStatusToListener(listener); - }); - } - - @Override - public void unregisterSplitScreenListener(SplitScreenListener listener) { - mMainExecutor.execute(() -> { - mExecutors.remove(listener); - - if (mExecutors.size() == 0) { - SplitScreenController.this.unregisterSplitScreenListener(mListener); - } - }); - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - mMainExecutor.execute(() -> { - SplitScreenController.this.onKeyguardVisibilityChanged(showing); - }); - } - } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class ISplitScreenImpl extends ISplitScreen.Stub { - private SplitScreenController mController; - private ISplitScreenListener mListener; - private final SplitScreen.SplitScreenListener mSplitScreenListener = - new SplitScreen.SplitScreenListener() { - @Override - public void onStagePositionChanged(int stage, int position) { - try { - if (mListener != null) { - mListener.onStagePositionChanged(stage, position); - } - } catch (RemoteException e) { - Slog.e(TAG, "onStagePositionChanged", e); - } - } - - @Override - public void onTaskStageChanged(int taskId, int stage, boolean visible) { - try { - if (mListener != null) { - mListener.onTaskStageChanged(taskId, stage, visible); - } - } catch (RemoteException e) { - Slog.e(TAG, "onTaskStageChanged", e); - } - } - }; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final SplitScreenController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); - } - }; - - public ISplitScreenImpl(SplitScreenController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; - } - - @Override - public void registerSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.registerSplitScreenListener(mSplitScreenListener); - }); - } - - @Override - public void unregisterSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); - } - - @Override - public void exitSplitScreen(int toTopTaskId) { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreen", - (controller) -> { - controller.exitSplitScreen(toTopTaskId, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT); - }); - } - - @Override - public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide", - (controller) -> { - controller.exitSplitScreenOnHide(exitSplitScreenOnHide); - }); - } - - @Override - public void setSideStageVisibility(boolean visible) { - executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility", - (controller) -> { - controller.setSideStageVisibility(visible); - }); - } - - @Override - public void removeFromSideStage(int taskId) { - executeRemoteCallWithTaskPermission(mController, "removeFromSideStage", - (controller) -> { - controller.removeFromSideStage(taskId); - }); - } - - @Override - public void startTask(int taskId, int stage, int position, @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startTask", - (controller) -> { - controller.startTask(taskId, position, options); - }); - } - - @Override - public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, - RemoteAnimationAdapter adapter) { - executeRemoteCallWithTaskPermission(mController, "startTasks", - (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition( - mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition, - adapter)); - } - - @Override - public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, - @SplitPosition int sidePosition, - @Nullable RemoteTransition remoteTransition) { - executeRemoteCallWithTaskPermission(mController, "startTasks", - (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions, - sideTaskId, sideOptions, sidePosition, remoteTransition)); - } - - @Override - public void startShortcut(String packageName, String shortcutId, int stage, int position, - @Nullable Bundle options, UserHandle user) { - executeRemoteCallWithTaskPermission(mController, "startShortcut", - (controller) -> { - controller.startShortcut(packageName, shortcutId, position, - options, user); - }); - } - - @Override - public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position, - @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startIntent", - (controller) -> { - controller.startIntent(intent, fillInIntent, position, options); - }); - } - - @Override - public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, - RemoteAnimationTarget[] apps) { - final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null}; - executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy", - (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel, apps), - true /* blocking */); - return out[0]; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java deleted file mode 100644 index 018365420177..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * 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.stagesplit; - -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; - -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; -import static com.android.wm.shell.transition.Transitions.isOpeningType; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.graphics.Rect; -import android.os.IBinder; -import android.view.SurfaceControl; -import android.view.WindowManager; -import android.window.RemoteTransition; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.transition.OneShotRemoteHandler; -import com.android.wm.shell.transition.Transitions; - -import java.util.ArrayList; - -/** Manages transition animations for split-screen. */ -class SplitScreenTransitions { - private static final String TAG = "SplitScreenTransitions"; - - /** Flag applied to a transition change to identify it as a divider bar for animation. */ - public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; - - private final TransactionPool mTransactionPool; - private final Transitions mTransitions; - private final Runnable mOnFinish; - - IBinder mPendingDismiss = null; - IBinder mPendingEnter = null; - - private IBinder mAnimatingTransition = null; - private OneShotRemoteHandler mRemoteHandler = null; - - private Transitions.TransitionFinishCallback mRemoteFinishCB = (wct, wctCB) -> { - if (wct != null || wctCB != null) { - throw new UnsupportedOperationException("finish transactions not supported yet."); - } - onFinish(); - }; - - /** Keeps track of currently running animations */ - private final ArrayList<Animator> mAnimations = new ArrayList<>(); - - private Transitions.TransitionFinishCallback mFinishCallback = null; - private SurfaceControl.Transaction mFinishTransaction; - - SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, - @NonNull Runnable onFinishCallback) { - mTransactionPool = pool; - mTransitions = transitions; - mOnFinish = onFinishCallback; - } - - void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback, - @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) { - mFinishCallback = finishCallback; - mAnimatingTransition = transition; - if (mRemoteHandler != null) { - mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction, - mRemoteFinishCB); - mRemoteHandler = null; - return; - } - playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot); - } - - private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, - @NonNull WindowContainerToken sideRoot) { - mFinishTransaction = mTransactionPool.acquire(); - - // Play some place-holder fade animations - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final SurfaceControl leash = change.getLeash(); - final int mode = info.getChanges().get(i).getMode(); - - if (mode == TRANSIT_CHANGE) { - if (change.getParent() != null) { - // This is probably reparented, so we want the parent to be immediately visible - final TransitionInfo.Change parentChange = info.getChange(change.getParent()); - t.show(parentChange.getLeash()); - t.setAlpha(parentChange.getLeash(), 1.f); - // and then animate this layer outside the parent (since, for example, this is - // the home task animating from fullscreen to part-screen). - t.reparent(leash, info.getRootLeash()); - t.setLayer(leash, info.getChanges().size() - i); - // build the finish reparent/reposition - mFinishTransaction.reparent(leash, parentChange.getLeash()); - mFinishTransaction.setPosition(leash, - change.getEndRelOffset().x, change.getEndRelOffset().y); - } - // TODO(shell-transitions): screenshot here - final Rect startBounds = new Rect(change.getStartAbsBounds()); - final Rect endBounds = new Rect(change.getEndAbsBounds()); - startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); - endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); - startExampleResizeAnimation(leash, startBounds, endBounds); - } - if (change.getParent() != null) { - continue; - } - - if (transition == mPendingEnter && (mainRoot.equals(change.getContainer()) - || sideRoot.equals(change.getContainer()))) { - t.setWindowCrop(leash, change.getStartAbsBounds().width(), - change.getStartAbsBounds().height()); - } - boolean isOpening = isOpeningType(info.getType()); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - // fade in - startExampleAnimation(leash, true /* show */); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { - // fade out - if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { - // Dismissing via snap-to-top/bottom means that the dismissed task is already - // not-visible (usually cropped to oblivion) so immediately set its alpha to 0 - // and don't animate it so it doesn't pop-in when reparented. - t.setAlpha(leash, 0.f); - } else { - startExampleAnimation(leash, false /* show */); - } - } - } - t.apply(); - onFinish(); - } - - /** Starts a transition to enter split with a remote transition animator. */ - IBinder startEnterTransition(@WindowManager.TransitionType int transitType, - @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition, - @NonNull Transitions.TransitionHandler handler) { - if (remoteTransition != null) { - // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff) - mRemoteHandler = new OneShotRemoteHandler( - mTransitions.getMainExecutor(), remoteTransition); - } - final IBinder transition = mTransitions.startTransition(transitType, wct, handler); - mPendingEnter = transition; - if (mRemoteHandler != null) { - mRemoteHandler.setTransition(transition); - } - return transition; - } - - /** Starts a transition for dismissing split after dragging the divider to a screen edge */ - IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct, - @NonNull Transitions.TransitionHandler handler) { - final IBinder transition = mTransitions.startTransition( - TRANSIT_SPLIT_DISMISS_SNAP, wct, handler); - mPendingDismiss = transition; - return transition; - } - - void onFinish() { - if (!mAnimations.isEmpty()) return; - mOnFinish.run(); - if (mFinishTransaction != null) { - mFinishTransaction.apply(); - mTransactionPool.release(mFinishTransaction); - mFinishTransaction = null; - } - mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); - mFinishCallback = null; - if (mAnimatingTransition == mPendingEnter) { - mPendingEnter = null; - } - if (mAnimatingTransition == mPendingDismiss) { - mPendingDismiss = null; - } - mAnimatingTransition = null; - } - - // TODO(shell-transitions): real animations - private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) { - final float end = show ? 1.f : 0.f; - final float start = 1.f - end; - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(start, end); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setAlpha(leash, end); - transaction.apply(); - mTransactionPool.release(transaction); - mTransitions.getMainExecutor().execute(() -> { - mAnimations.remove(va); - onFinish(); - }); - }; - va.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { } - - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationRepeat(Animator animation) { } - }); - mAnimations.add(va); - mTransitions.getAnimExecutor().execute(va::start); - } - - // TODO(shell-transitions): real animations - private void startExampleResizeAnimation(@NonNull SurfaceControl leash, - @NonNull Rect startBounds, @NonNull Rect endBounds) { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setWindowCrop(leash, - (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction), - (int) (startBounds.height() * (1.f - fraction) - + endBounds.height() * fraction)); - transaction.setPosition(leash, - startBounds.left * (1.f - fraction) + endBounds.left * fraction, - startBounds.top * (1.f - fraction) + endBounds.top * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setWindowCrop(leash, 0, 0); - transaction.setPosition(leash, endBounds.left, endBounds.top); - transaction.apply(); - mTransactionPool.release(transaction); - mTransitions.getMainExecutor().execute(() -> { - mAnimations.remove(va); - onFinish(); - }); - }; - va.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - }); - mAnimations.add(va); - mTransitions.getAnimExecutor().execute(va::start); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java deleted file mode 100644 index e1850396a5c0..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * 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.stagesplit; - -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; - -import com.android.internal.logging.InstanceId; -import com.android.internal.logging.InstanceIdSequence; -import com.android.internal.util.FrameworkStatsLog; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; - -/** - * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent - */ -public class SplitscreenEventLogger { - - // Used to generate instance ids for this drag if one is not provided - private final InstanceIdSequence mIdSequence; - - // The instance id for the current splitscreen session (from start to end) - private InstanceId mLoggerSessionId; - - // Drag info - private @SplitPosition int mDragEnterPosition; - private InstanceId mDragEnterSessionId; - - // For deduping async events - private int mLastMainStagePosition = -1; - private int mLastMainStageUid = -1; - private int mLastSideStagePosition = -1; - private int mLastSideStageUid = -1; - private float mLastSplitRatio = -1f; - - public SplitscreenEventLogger() { - mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE); - } - - /** - * Return whether a splitscreen session has started. - */ - public boolean hasStartedSession() { - return mLoggerSessionId != null; - } - - /** - * May be called before logEnter() to indicate that the session was started from a drag. - */ - public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) { - mDragEnterPosition = position; - mDragEnterSessionId = dragSessionId; - } - - /** - * Logs when the user enters splitscreen. - */ - public void logEnter(float splitRatio, - @SplitPosition int mainStagePosition, int mainStageUid, - @SplitPosition int sideStagePosition, int sideStageUid, - boolean isLandscape) { - mLoggerSessionId = mIdSequence.newInstanceId(); - int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED - ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape) - : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW; - updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), - mainStageUid); - updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), - sideStageUid); - updateSplitRatioState(splitRatio); - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER, - enterReason, - 0 /* exitReason */, - splitRatio, - mLastMainStagePosition, - mLastMainStageUid, - mLastSideStagePosition, - mLastSideStageUid, - mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0, - mLoggerSessionId.getId()); - } - - /** - * Logs when the user exits splitscreen. Only one of the main or side stages should be - * specified to indicate which position was focused as a part of exiting (both can be unset). - */ - public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid, - @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if ((mainStagePosition != SPLIT_POSITION_UNDEFINED - && sideStagePosition != SPLIT_POSITION_UNDEFINED) - || (mainStageUid != 0 && sideStageUid != 0)) { - throw new IllegalArgumentException("Only main or side stage should be set"); - } - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT, - 0 /* enterReason */, - exitReason, - 0f /* splitRatio */, - getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), - mainStageUid, - getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), - sideStageUid, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - - // Reset states - mLoggerSessionId = null; - mDragEnterPosition = SPLIT_POSITION_UNDEFINED; - mDragEnterSessionId = null; - mLastMainStagePosition = -1; - mLastMainStageUid = -1; - mLastSideStagePosition = -1; - mLastSideStageUid = -1; - } - - /** - * Logs when an app in the main stage changes. - */ - public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid, - boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, - isLandscape), mainStageUid)) { - // Ignore if there are no user perceived changes - return; - } - - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE, - 0 /* enterReason */, - 0 /* exitReason */, - 0f /* splitRatio */, - mLastMainStagePosition, - mLastMainStageUid, - 0 /* sideStagePosition */, - 0 /* sideStageUid */, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - /** - * Logs when an app in the side stage changes. - */ - public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid, - boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, - isLandscape), sideStageUid)) { - // Ignore if there are no user perceived changes - return; - } - - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE, - 0 /* enterReason */, - 0 /* exitReason */, - 0f /* splitRatio */, - 0 /* mainStagePosition */, - 0 /* mainStageUid */, - mLastSideStagePosition, - mLastSideStageUid, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - /** - * Logs when the splitscreen ratio changes. - */ - public void logResize(float splitRatio) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if (splitRatio <= 0f || splitRatio >= 1f) { - // Don't bother reporting resizes that end up dismissing the split, that will be logged - // via the exit event - return; - } - if (!updateSplitRatioState(splitRatio)) { - // Ignore if there are no user perceived changes - return; - } - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE, - 0 /* enterReason */, - 0 /* exitReason */, - mLastSplitRatio, - 0 /* mainStagePosition */, 0 /* mainStageUid */, - 0 /* sideStagePosition */, 0 /* sideStageUid */, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - /** - * Logs when the apps in splitscreen are swapped. - */ - public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid, - @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - - updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), - mainStageUid); - updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), - sideStageUid); - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP, - 0 /* enterReason */, - 0 /* exitReason */, - 0f /* splitRatio */, - mLastMainStagePosition, - mLastMainStageUid, - mLastSideStagePosition, - mLastSideStageUid, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - private boolean updateMainStageState(int mainStagePosition, int mainStageUid) { - boolean changed = (mLastMainStagePosition != mainStagePosition) - || (mLastMainStageUid != mainStageUid); - if (!changed) { - return false; - } - - mLastMainStagePosition = mainStagePosition; - mLastMainStageUid = mainStageUid; - return true; - } - - private boolean updateSideStageState(int sideStagePosition, int sideStageUid) { - boolean changed = (mLastSideStagePosition != sideStagePosition) - || (mLastSideStageUid != sideStageUid); - if (!changed) { - return false; - } - - mLastSideStagePosition = sideStagePosition; - mLastSideStageUid = sideStageUid; - return true; - } - - private boolean updateSplitRatioState(float splitRatio) { - boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0; - if (!changed) { - return false; - } - - mLastSplitRatio = splitRatio; - return true; - } - - public int getDragEnterReasonFromSplitPosition(@SplitPosition int position, - boolean isLandscape) { - if (isLandscape) { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT; - } else { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM; - } - } - - private int getMainStagePositionFromSplitPosition(@SplitPosition int position, - boolean isLandscape) { - if (position == SPLIT_POSITION_UNDEFINED) { - return 0; - } - if (isLandscape) { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT; - } else { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM; - } - } - - private int getSideStagePositionFromSplitPosition(@SplitPosition int position, - boolean isLandscape) { - if (position == SPLIT_POSITION_UNDEFINED) { - return 0; - } - if (isLandscape) { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT; - } else { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java deleted file mode 100644 index ac25c7510931..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * 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.wm.shell.stagesplit; - -import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.view.WindowManager.transitTypeToString; - -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; -import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_MAIN; -import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_SIDE; -import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_UNDEFINED; -import static com.android.wm.shell.stagesplit.SplitScreen.stageTypeToString; -import static com.android.wm.shell.stagesplit.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; -import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; -import static com.android.wm.shell.transition.Transitions.isClosingType; -import static com.android.wm.shell.transition.Transitions.isOpeningType; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.app.ActivityTaskManager; -import android.app.PendingIntent; -import android.app.WindowConfiguration; -import android.content.Context; -import android.content.Intent; -import android.graphics.Rect; -import android.hardware.devicestate.DeviceStateManager; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.util.Slog; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.IRemoteAnimationRunner; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.view.WindowManager; -import android.window.DisplayAreaInfo; -import android.window.RemoteTransition; -import android.window.TransitionInfo; -import android.window.TransitionRequestInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellTaskOrganizer; -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.common.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.common.split.SplitWindowManager; -import com.android.wm.shell.protolog.ShellProtoLogGroup; -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 - * {@link SideStage} stages. - * Some high-level rules: - * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at - * least one child task. - * - The {@link MainStage} should only have children if the coordinator is active. - * - The {@link SplitLayout} divider is only visible if both the {@link MainStage} - * and {@link SideStage} are visible. - * - The {@link MainStage} configuration is fullscreen when the {@link SideStage} isn't visible. - * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and - * {@link #onStageHasChildrenChanged(StageListenerImpl).} - */ -class StageCoordinator implements SplitLayout.SplitLayoutHandler, - RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler { - - private static final String TAG = StageCoordinator.class.getSimpleName(); - - /** internal value for mDismissTop that represents no dismiss */ - private static final int NO_DISMISS = -2; - - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - - 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; - - private final int mDisplayId; - private SplitLayout mSplitLayout; - private boolean mDividerVisible; - private final SyncTransactionQueue mSyncQueue; - private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; - private final ShellTaskOrganizer mTaskOrganizer; - private DisplayAreaInfo mDisplayAreaInfo; - private final Context mContext; - private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - private final SplitScreenTransitions mSplitTransitions; - private final SplitscreenEventLogger mLogger; - private boolean mExitSplitScreenOnHide; - private boolean mKeyguardOccluded; - - // TODO(b/187041611): remove this flag after totally deprecated legacy split - /** Whether the device is supporting legacy split or not. */ - private boolean mUseLegacySplit; - - @SplitScreen.StageType private int mDismissTop = NO_DISMISS; - - /** The target stage to dismiss to when unlock after folded. */ - @SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; - - private final Runnable mOnTransitionAnimationComplete = () -> { - // If still playing, let it finish. - if (!isSplitScreenVisible()) { - // Update divider state after animation so that it is still around and positioned - // properly for the animation itself. - setDividerVisibility(false); - mSplitLayout.resetDividerPosition(); - } - mDismissTop = NO_DISMISS; - }; - - private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks = - new SplitWindowManager.ParentContainerCallbacks() { - @Override - public void attachToParentSurface(SurfaceControl.Builder b) { - mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b); - } - - @Override - public void onLeashReady(SurfaceControl leash) { - mSyncQueue.runInSync(t -> applyDividerVisibility(t)); - } - }; - - StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, Transitions transitions, - 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, - mMainUnfoldController); - mSideStage = new SideStage( - mContext, - mTaskOrganizer, - mDisplayId, - mSideStageListener, - mSyncQueue, - mSurfaceSession, - mSideUnfoldController); - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage); - mRootTDAOrganizer.registerListener(displayId, this); - final DeviceStateManager deviceStateManager = - mContext.getSystemService(DeviceStateManager.class); - deviceStateManager.registerCallback(taskOrganizer.getExecutor(), - new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged)); - mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, - mOnTransitionAnimationComplete); - transitions.addHandler(this); - } - - @VisibleForTesting - StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, SplitLayout splitLayout, - Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger, - Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { - mContext = context; - mDisplayId = displayId; - mSyncQueue = syncQueue; - mRootTDAOrganizer = rootTDAOrganizer; - mTaskOrganizer = taskOrganizer; - mMainStage = mainStage; - mSideStage = sideStage; - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mRootTDAOrganizer.registerListener(displayId, this); - mSplitLayout = splitLayout; - mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, - mOnTransitionAnimationComplete); - mMainUnfoldController = unfoldControllerProvider.get().orElse(null); - mSideUnfoldController = unfoldControllerProvider.get().orElse(null); - mLogger = logger; - transitions.addHandler(this); - } - - @VisibleForTesting - SplitScreenTransitions getSplitTransitions() { - return mSplitTransitions; - } - - boolean isSplitScreenVisible() { - return mSideStageListener.mVisible && mMainStageListener.mVisible; - } - - boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @SplitPosition int sideStagePosition) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - setSideStagePosition(sideStagePosition, wct); - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.addTask(task, getSideStageBounds(), wct); - mSyncQueue.queue(wct); - mSyncQueue.runInSync( - t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */)); - return true; - } - - boolean removeFromSideStage(int taskId) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - /** - * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the - * {@link SideStage} no longer has children. - */ - final boolean result = mSideStage.removeTask(taskId, - mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null, - wct); - mTaskOrganizer.applyTransaction(wct); - return result; - } - - void setSideStageOutline(boolean enable) { - mSideStage.enableOutline(enable); - } - - /** Starts 2 tasks in one transition. */ - void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, - @Nullable Bundle sideOptions, @SplitPosition int sidePosition, - @Nullable RemoteTransition remoteTransition) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mainOptions = mainOptions != null ? mainOptions : new Bundle(); - sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition, wct); - - // Build a request WCT that will launch both apps such that task 0 is on the main stage - // while task 1 is on the side stage. - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.setBounds(getSideStageBounds(), wct); - - // Make sure the launch options will put tasks in the corresponding split roots - addActivityOptions(mainOptions, mMainStage); - addActivityOptions(sideOptions, mSideStage); - - // Add task launch requests - wct.startTask(mainTaskId, mainOptions); - wct.startTask(sideTaskId, sideOptions); - - mSplitTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this); - } - - /** Starts 2 tasks in one legacy transition. */ - void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, - RemoteAnimationAdapter adapter) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Need to add another wrapper here in shell so that we can inject the divider bar - // and also manage the process elevation via setRunningRemote - IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { - @Override - public void onAnimationStart(@WindowManager.TransitionOldType int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - final IRemoteAnimationFinishedCallback finishedCallback) { - RemoteAnimationTarget[] augmentedNonApps = - new RemoteAnimationTarget[nonApps.length + 1]; - for (int i = 0; i < nonApps.length; ++i) { - augmentedNonApps[i] = nonApps[i]; - } - augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget(); - try { - ActivityTaskManager.getService().setRunningRemoteTransitionDelegate( - adapter.getCallingApplication()); - adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps, - finishedCallback); - } catch (RemoteException e) { - Slog.e(TAG, "Error starting remote animation", e); - } - } - - @Override - public void onAnimationCancelled() { - try { - adapter.getRunner().onAnimationCancelled(); - } catch (RemoteException e) { - Slog.e(TAG, "Error starting remote animation", e); - } - } - }; - RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter( - wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay()); - - if (mainOptions == null) { - mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle(); - } else { - ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions); - mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter)); - } - - sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition, wct); - - // Build a request WCT that will launch both apps such that task 0 is on the main stage - // while task 1 is on the side stage. - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.setBounds(getSideStageBounds(), wct); - - // Make sure the launch options will put tasks in the corresponding split roots - addActivityOptions(mainOptions, mMainStage); - addActivityOptions(sideOptions, mSideStage); - - // Add task launch requests - wct.startTask(mainTaskId, mainOptions); - wct.startTask(sideTaskId, sideOptions); - - // Using legacy transitions, so we can't use blast sync since it conflicts. - mTaskOrganizer.applyTransaction(wct); - } - - public void startIntent(PendingIntent intent, Intent fillInIntent, - @SplitScreen.StageType int stage, @SplitPosition int position, - @androidx.annotation.Nullable Bundle options, - @Nullable RemoteTransition remoteTransition) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - options = resolveStartStage(stage, position, options, wct); - wct.sendPendingIntent(intent, fillInIntent, options); - mSplitTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this); - } - - Bundle resolveStartStage(@SplitScreen.StageType int stage, - @SplitPosition int position, @androidx.annotation.Nullable Bundle options, - @androidx.annotation.Nullable WindowContainerTransaction wct) { - switch (stage) { - case STAGE_TYPE_UNDEFINED: { - // Use the stage of the specified position is valid. - if (position != SPLIT_POSITION_UNDEFINED) { - if (position == getSideStagePosition()) { - options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct); - } else { - options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct); - } - } else { - // Exit split-screen and launch fullscreen since stage wasn't specified. - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - } - break; - } - case STAGE_TYPE_SIDE: { - if (position != SPLIT_POSITION_UNDEFINED) { - setSideStagePosition(position, wct); - } else { - position = getSideStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - updateActivityOptions(options, position); - break; - } - case STAGE_TYPE_MAIN: { - if (position != SPLIT_POSITION_UNDEFINED) { - // Set the side stage opposite of what we want to the main stage. - final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - setSideStagePosition(sideStagePosition, wct); - } else { - position = getMainStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - updateActivityOptions(options, position); - break; - } - default: - throw new IllegalArgumentException("Unknown stage=" + stage); - } - - return options; - } - - @SplitPosition - int getSideStagePosition() { - return mSideStagePosition; - } - - @SplitPosition - int getMainStagePosition() { - return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - } - - void setSideStagePosition(@SplitPosition int sideStagePosition, - @Nullable WindowContainerTransaction wct) { - setSideStagePosition(sideStagePosition, true /* updateBounds */, wct); - } - - private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateBounds, - @Nullable WindowContainerTransaction wct) { - if (mSideStagePosition == sideStagePosition) return; - mSideStagePosition = sideStagePosition; - sendOnStagePositionChanged(); - - if (mSideStageListener.mVisible && updateBounds) { - if (wct == null) { - // onLayoutSizeChanged builds/applies a wct with the contents of updateWindowBounds. - onLayoutSizeChanged(mSplitLayout); - } else { - updateWindowBounds(mSplitLayout, wct); - updateUnfoldBounds(); - } - } - } - - void setSideStageVisibility(boolean visible) { - if (mSideStageListener.mVisible == visible) return; - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.setVisibility(visible, wct); - mTaskOrganizer.applyTransaction(wct); - } - - void onKeyguardOccludedChanged(boolean occluded) { - // Do not exit split directly, because it needs to wait for task info update to determine - // which task should remain on top after split dismissed. - mKeyguardOccluded = occluded; - } - - void onKeyguardVisibilityChanged(boolean showing) { - if (!showing && mMainStage.isActive() - && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { - exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, - SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED); - } - } - - void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - mExitSplitScreenOnHide = exitSplitScreenOnHide; - } - - void exitSplitScreen(int toTopTaskId, int exitReason) { - StageTaskListener childrenToTop = null; - if (mMainStage.containsTask(toTopTaskId)) { - childrenToTop = mMainStage; - } else if (mSideStage.containsTask(toTopTaskId)) { - childrenToTop = mSideStage; - } - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (childrenToTop != null) { - childrenToTop.reorderChild(toTopTaskId, true /* onTop */, wct); - } - applyExitSplitScreen(childrenToTop, wct, exitReason); - } - - private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - applyExitSplitScreen(childrenToTop, wct, exitReason); - } - - private void applyExitSplitScreen( - StageTaskListener childrenToTop, - WindowContainerTransaction wct, int exitReason) { - mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); - mMainStage.deactivate(wct, childrenToTop == mMainStage); - mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> t - .setWindowCrop(mMainStage.mRootLeash, null) - .setWindowCrop(mSideStage.mRootLeash, null)); - // Hide divider and reset its position. - setDividerVisibility(false); - mSplitLayout.resetDividerPosition(); - mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; - if (childrenToTop != null) { - logExitToStage(exitReason, childrenToTop == mMainStage); - } else { - logExit(exitReason); - } - } - - /** - * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates - * an existing WindowContainerTransaction (rather than applying immediately). This is intended - * to be used when exiting split might be bundled with other window operations. - */ - void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop, - @NonNull WindowContainerTransaction wct) { - mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); - mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); - } - - void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { - outTopOrLeftBounds.set(mSplitLayout.getBounds1()); - outBottomOrRightBounds.set(mSplitLayout.getBounds2()); - } - - private void addActivityOptions(Bundle opts, StageTaskListener stage) { - opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token); - } - - void updateActivityOptions(Bundle opts, @SplitPosition int position) { - addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage); - } - - void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) { - if (mListeners.contains(listener)) return; - mListeners.add(listener); - sendStatusToListener(listener); - } - - void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) { - mListeners.remove(listener); - } - - void sendStatusToListener(SplitScreen.SplitScreenListener listener) { - listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition()); - listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition()); - listener.onSplitVisibilityChanged(isSplitScreenVisible()); - mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE); - mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN); - } - - private void sendOnStagePositionChanged() { - for (int i = mListeners.size() - 1; i >= 0; --i) { - final SplitScreen.SplitScreenListener l = mListeners.get(i); - l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition()); - l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition()); - } - } - - private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId, - boolean present, boolean visible) { - int stage; - if (present) { - stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; - } else { - // No longer on any stage - stage = STAGE_TYPE_UNDEFINED; - } - if (stage == STAGE_TYPE_MAIN) { - mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } else { - mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onTaskStageChanged(taskId, stage, visible); - } - } - - private void sendSplitVisibilityChanged() { - for (int i = mListeners.size() - 1; i >= 0; --i) { - 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) { - if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) { - mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, - true /* moveTogether */); - - // Only sets side stage as launch-adjacent-flag-root when the device is not using legacy - // split to prevent new split behavior confusing users. - if (!mUseLegacySplit) { - wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); - } - - mTaskOrganizer.applyTransaction(wct); - } - } - - private void onStageRootTaskVanished(StageListenerImpl stageListener) { - if (stageListener == mMainStageListener || stageListener == mSideStageListener) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Deactivate the main stage if it no longer has a root task. - mMainStage.deactivate(wct); - - if (!mUseLegacySplit) { - wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); - } - - mTaskOrganizer.applyTransaction(wct); - } - } - - private void setDividerVisibility(boolean visible) { - if (mDividerVisible == visible) return; - mDividerVisible = visible; - if (visible) { - mSplitLayout.init(); - updateUnfoldBounds(); - } else { - mSplitLayout.release(); - } - sendSplitVisibilityChanged(); - } - - private void onStageVisibilityChanged(StageListenerImpl stageListener) { - final boolean sideStageVisible = mSideStageListener.mVisible; - final boolean mainStageVisible = mMainStageListener.mVisible; - final boolean bothStageVisible = sideStageVisible && mainStageVisible; - final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible; - final boolean sameVisibility = sideStageVisible == mainStageVisible; - // Only add or remove divider when both visible or both invisible to avoid sometimes we only - // got one stage visibility changed for a moment and it will cause flicker. - if (sameVisibility) { - setDividerVisibility(bothStageVisible); - } - - if (bothStageInvisible) { - if (mExitSplitScreenOnHide - // Don't dismiss staged split when both stages are not visible due to sleeping display, - // like the cases keyguard showing or screen off. - || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) { - exitSplitScreen(null /* childrenToTop */, - SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME); - } - } else if (mKeyguardOccluded) { - // At least one of the stages is visible while keyguard occluded. Dismiss split because - // there's show-when-locked activity showing on top of keyguard. Also make sure the - // task contains show-when-locked activity remains on top after split dismissed. - final StageTaskListener toTop = - mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null); - exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP); - } - - mSyncQueue.runInSync(t -> { - // Same above, we only set root tasks and divider leash visibility when both stage - // change to visible or invisible to avoid flicker. - if (sameVisibility) { - t.setVisibility(mSideStage.mRootLeash, bothStageVisible) - .setVisibility(mMainStage.mRootLeash, bothStageVisible); - applyDividerVisibility(t); - applyOutlineVisibility(t); - } - }); - } - - private void applyDividerVisibility(SurfaceControl.Transaction t) { - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (dividerLeash == null) { - return; - } - - if (mDividerVisible) { - t.show(dividerLeash) - .setLayer(dividerLeash, Integer.MAX_VALUE) - .setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, - mSplitLayout.getDividerBounds().top); - } else { - t.hide(dividerLeash); - } - } - - private void applyOutlineVisibility(SurfaceControl.Transaction t) { - final SurfaceControl outlineLeash = mSideStage.getOutlineLeash(); - if (outlineLeash == null) { - return; - } - - if (mDividerVisible) { - t.show(outlineLeash).setLayer(outlineLeash, Integer.MAX_VALUE); - } else { - t.hide(outlineLeash); - } - } - - private void onStageHasChildrenChanged(StageListenerImpl stageListener) { - final boolean hasChildren = stageListener.mHasChildren; - final boolean isSideStage = stageListener == mSideStageListener; - if (!hasChildren) { - if (isSideStage && mMainStageListener.mVisible) { - // Exit to main stage if side stage no longer has children. - exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED); - } else if (!isSideStage && mSideStageListener.mVisible) { - // Exit to side stage if main stage no longer has children. - exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED); - } - } else if (isSideStage) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Make sure the main stage is active. - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.setBounds(getSideStageBounds(), wct); - mTaskOrganizer.applyTransaction(wct); - } - if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren - && mSideStageListener.mHasChildren) { - mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), - getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - } - - @VisibleForTesting - IBinder onSnappedToDismissTransition(boolean mainStageToTop) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct); - return mSplitTransitions.startSnapToDismiss(wct, this); - } - - @Override - public void onSnappedToDismiss(boolean bottomOrRight) { - final boolean mainStageToTop = - bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT - : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; - if (ENABLE_SHELL_TRANSITIONS) { - onSnappedToDismissTransition(mainStageToTop); - return; - } - exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, - SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER); - } - - @Override - public void onDoubleTappedDivider() { - setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */); - mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - - @Override - public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); - } - - @Override - public void onLayoutSizeChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); - mSideStage.setOutlineVisibility(false); - } - - @Override - public void onLayoutSizeChanged(SplitLayout layout) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - updateWindowBounds(layout, wct); - updateUnfoldBounds(); - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */)); - 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 - */ - private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); - } - - void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, - boolean applyResizingOffset) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, - applyResizingOffset); - } - - @Override - public int getSplitItemPosition(WindowContainerToken token) { - if (token == null) { - return SPLIT_POSITION_UNDEFINED; - } - - if (token.equals(mMainStage.mRootTaskInfo.getToken())) { - return getMainStagePosition(); - } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) { - return getSideStagePosition(); - } - - return SPLIT_POSITION_UNDEFINED; - } - - @Override - public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - final WindowContainerTransaction wct = new WindowContainerTransaction(); - layout.applyLayoutOffsetTarget(wct, offsetX, offsetY, topLeftStage.mRootTaskInfo, - bottomRightStage.mRootTaskInfo); - mTaskOrganizer.applyTransaction(wct); - } - - @Override - public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) { - mDisplayAreaInfo = displayAreaInfo; - if (mSplitLayout == null) { - mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, - mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING); - mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); - - if (mMainUnfoldController != null && mSideUnfoldController != null) { - mMainUnfoldController.init(); - mSideUnfoldController.init(); - } - } - } - - @Override - public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) { - throw new IllegalStateException("Well that was unexpected..."); - } - - @Override - public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) { - mDisplayAreaInfo = displayAreaInfo; - if (mSplitLayout != null - && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration) - && mMainStage.isActive()) { - onLayoutSizeChanged(mSplitLayout); - } - } - - private void onFoldedStateChanged(boolean folded) { - mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; - if (!folded) return; - - if (mMainStage.isFocused()) { - mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN; - } else if (mSideStage.isFocused()) { - mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE; - } - } - - private Rect getSideStageBounds() { - return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2(); - } - - private Rect getMainStageBounds() { - return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1(); - } - - /** - * Get the stage that should contain this `taskInfo`. The stage doesn't necessarily contain - * this task (yet) so this can also be used to identify which stage to put a task into. - */ - private StageTaskListener getStageOfTask(ActivityManager.RunningTaskInfo taskInfo) { - // TODO(b/184679596): Find a way to either include task-org information in the transition, - // or synchronize task-org callbacks so we can use stage.containsTask - if (mMainStage.mRootTaskInfo != null - && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) { - return mMainStage; - } else if (mSideStage.mRootTaskInfo != null - && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) { - return mSideStage; - } - return null; - } - - @SplitScreen.StageType - private int getStageType(StageTaskListener stage) { - return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; - } - - @Override - public WindowContainerTransaction handleRequest(@NonNull IBinder transition, - @Nullable TransitionRequestInfo request) { - final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); - if (triggerTask == null) { - // still want to monitor everything while in split-screen, so return non-null. - return isSplitScreenVisible() ? new WindowContainerTransaction() : null; - } - - WindowContainerTransaction out = null; - final @WindowManager.TransitionType int type = request.getType(); - if (isSplitScreenVisible()) { - // try to handle everything while in split-screen, so return a WCT even if it's empty. - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split" - + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d" - + " sideChildren=%d", triggerTask.taskId, transitTypeToString(type), - mMainStage.getChildCount(), mSideStage.getChildCount()); - out = new WindowContainerTransaction(); - final StageTaskListener stage = getStageOfTask(triggerTask); - if (stage != null) { - // dismiss split if the last task in one of the stages is going away - if (isClosingType(type) && stage.getChildCount() == 1) { - // The top should be the opposite side that is closing: - mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN - ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; - } - } else { - if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) { - // Going home so dismiss both. - mDismissTop = STAGE_TYPE_UNDEFINED; - } - } - if (mDismissTop != NO_DISMISS) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " - + " deduced Dismiss from request. toTop=%s", - stageTypeToString(mDismissTop)); - prepareExitSplitScreen(mDismissTop, out); - mSplitTransitions.mPendingDismiss = transition; - } - } else { - // Not in split mode, so look for an open into a split stage just so we can whine and - // complain about how this isn't a supported operation. - if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) { - if (getStageOfTask(triggerTask) != null) { - throw new IllegalStateException("Entering split implicitly with only one task" - + " isn't supported."); - } - } - } - return out; - } - - @Override - public boolean startAnimation(@NonNull IBinder transition, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (transition != mSplitTransitions.mPendingDismiss - && transition != mSplitTransitions.mPendingEnter) { - // Not entering or exiting, so just do some house-keeping and validation. - - // If we're not in split-mode, just abort so something else can handle it. - if (!isSplitScreenVisible()) return false; - - for (int iC = 0; iC < info.getChanges().size(); ++iC) { - final TransitionInfo.Change change = info.getChanges().get(iC); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null || !taskInfo.hasParentTask()) continue; - final StageTaskListener stage = getStageOfTask(taskInfo); - if (stage == null) continue; - if (isOpeningType(change.getMode())) { - if (!stage.containsTask(taskInfo.taskId)) { - Log.w(TAG, "Expected onTaskAppeared on " + stage + " to have been called" - + " with " + taskInfo.taskId + " before startAnimation()."); - } - } else if (isClosingType(change.getMode())) { - if (stage.containsTask(taskInfo.taskId)) { - Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called" - + " with " + taskInfo.taskId + " before startAnimation()."); - } - } - } - if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { - // TODO(shell-transitions): Implement a fallback behavior for now. - throw new IllegalStateException("Somehow removed the last task in a stage" - + " outside of a proper transition"); - // This can happen in some pathological cases. For example: - // 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C] - // 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time - // In this case, the result *should* be that we leave split. - // TODO(b/184679596): Find a way to either include task-org information in - // the transition, or synchronize task-org callbacks. - } - - // Use normal animations. - return false; - } - - boolean shouldAnimate = true; - if (mSplitTransitions.mPendingEnter == transition) { - shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction); - } else if (mSplitTransitions.mPendingDismiss == transition) { - shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction); - } - if (!shouldAnimate) return false; - - mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction, - finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); - return true; - } - - private boolean startPendingEnterAnimation(@NonNull IBinder transition, - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { - if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) { - // First, verify that we actually have opened 2 apps in split. - TransitionInfo.Change mainChild = null; - TransitionInfo.Change sideChild = null; - for (int iC = 0; iC < info.getChanges().size(); ++iC) { - final TransitionInfo.Change change = info.getChanges().get(iC); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null || !taskInfo.hasParentTask()) continue; - final @SplitScreen.StageType int stageType = getStageType(getStageOfTask(taskInfo)); - if (stageType == STAGE_TYPE_MAIN) { - mainChild = change; - } else if (stageType == STAGE_TYPE_SIDE) { - sideChild = change; - } - } - if (mainChild == null || sideChild == null) { - throw new IllegalStateException("Launched 2 tasks in split, but didn't receive" - + " 2 tasks in transition. Possibly one of them failed to launch"); - // TODO: fallback logic. Probably start a new transition to exit split before - // applying anything here. Ideally consolidate with transition-merging. - } - - // Update local states (before animating). - setDividerVisibility(true); - setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */, - null /* wct */); - setSplitsVisible(true); - - addDividerBarToTransition(info, t, true /* show */); - - // Make some noise if things aren't totally expected. These states shouldn't effect - // transitions locally, but remotes (like Launcher) may get confused if they were - // depending on listener callbacks. This can happen because task-organizer callbacks - // aren't serialized with transition callbacks. - // TODO(b/184679596): Find a way to either include task-org information in - // the transition, or synchronize task-org callbacks. - if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) { - Log.w(TAG, "Expected onTaskAppeared on " + mMainStage - + " to have been called with " + mainChild.getTaskInfo().taskId - + " before startAnimation()."); - } - if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) { - Log.w(TAG, "Expected onTaskAppeared on " + mSideStage - + " to have been called with " + sideChild.getTaskInfo().taskId - + " before startAnimation()."); - } - return true; - } else { - // TODO: other entry method animations - throw new RuntimeException("Unsupported split-entry"); - } - } - - private boolean startPendingDismissAnimation(@NonNull IBinder transition, - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { - // Make some noise if things aren't totally expected. These states shouldn't effect - // transitions locally, but remotes (like Launcher) may get confused if they were - // depending on listener callbacks. This can happen because task-organizer callbacks - // aren't serialized with transition callbacks. - // TODO(b/184679596): Find a way to either include task-org information in - // the transition, or synchronize task-org callbacks. - if (mMainStage.getChildCount() != 0) { - final StringBuilder tasksLeft = new StringBuilder(); - for (int i = 0; i < mMainStage.getChildCount(); ++i) { - tasksLeft.append(i != 0 ? ", " : ""); - tasksLeft.append(mMainStage.mChildrenTaskInfo.keyAt(i)); - } - Log.w(TAG, "Expected onTaskVanished on " + mMainStage - + " to have been called with [" + tasksLeft.toString() - + "] before startAnimation()."); - } - if (mSideStage.getChildCount() != 0) { - final StringBuilder tasksLeft = new StringBuilder(); - for (int i = 0; i < mSideStage.getChildCount(); ++i) { - tasksLeft.append(i != 0 ? ", " : ""); - tasksLeft.append(mSideStage.mChildrenTaskInfo.keyAt(i)); - } - Log.w(TAG, "Expected onTaskVanished on " + mSideStage - + " to have been called with [" + tasksLeft.toString() - + "] before startAnimation()."); - } - - // Update local states. - setSplitsVisible(false); - // Wait until after animation to update divider - - if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { - // Reset crops so they don't interfere with subsequent launches - t.setWindowCrop(mMainStage.mRootLeash, null); - t.setWindowCrop(mSideStage.mRootLeash, null); - } - - if (mDismissTop == STAGE_TYPE_UNDEFINED) { - // Going home (dismissing both splits) - - // TODO: Have a proper remote for this. Until then, though, reset state and use the - // normal animation stuff (which falls back to the normal launcher remote). - t.hide(mSplitLayout.getDividerLeash()); - setDividerVisibility(false); - mSplitTransitions.mPendingDismiss = null; - return false; - } - - addDividerBarToTransition(info, t, false /* show */); - // We're dismissing split by moving the other one to fullscreen. - // Since we don't have any animations for this yet, just use the internal example - // animations. - return true; - } - - private void addDividerBarToTransition(@NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, boolean show) { - final SurfaceControl leash = mSplitLayout.getDividerLeash(); - final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash); - final Rect bounds = mSplitLayout.getDividerBounds(); - barChange.setStartAbsBounds(bounds); - barChange.setEndAbsBounds(bounds); - barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK); - barChange.setFlags(FLAG_IS_DIVIDER_BAR); - // Technically this should be order-0, but this is running after layer assignment - // and it's a special case, so just add to end. - info.addChange(barChange); - // Be default, make it visible. The remote animator can adjust alpha if it plans to animate. - if (show) { - t.setAlpha(leash, 1.f); - t.setLayer(leash, Integer.MAX_VALUE); - t.setPosition(leash, bounds.left, bounds.top); - t.show(leash); - } - } - - RemoteAnimationTarget getDividerBarLegacyTarget() { - final Rect bounds = mSplitLayout.getDividerBounds(); - return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */, - mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */, - null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */, - new android.graphics.Point(0, 0) /* position */, bounds, bounds, - new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */, - null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); - } - - RemoteAnimationTarget getOutlineLegacyTarget() { - final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds(); - // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to - // distinguish as a split auxiliary target in Launcher. - return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */, - mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */, - null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */, - new android.graphics.Point(0, 0) /* position */, bounds, bounds, - new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */, - null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); - } - - @Override - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + TAG + " mDisplayId=" + mDisplayId); - pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible); - pw.println(innerPrefix + "MainStage"); - pw.println(childPrefix + "isActive=" + mMainStage.isActive()); - mMainStageListener.dump(pw, childPrefix); - pw.println(innerPrefix + "SideStage"); - mSideStageListener.dump(pw, childPrefix); - pw.println(innerPrefix + "mSplitLayout=" + mSplitLayout); - } - - /** - * Directly set the visibility of both splits. This assumes hasChildren matches visibility. - * This is intended for batch use, so it assumes other state management logic is already - * handled. - */ - private void setSplitsVisible(boolean visible) { - mMainStageListener.mVisible = mSideStageListener.mVisible = visible; - mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible; - } - - /** - * Sets drag info to be logged when splitscreen is next entered. - */ - public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { - mLogger.enterRequestedByDrag(position, dragSessionId); - } - - /** - * Logs the exit of splitscreen. - */ - private void logExit(int exitReason) { - mLogger.logExit(exitReason, - SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */, - SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */, - mSplitLayout.isLandscape()); - } - - /** - * Logs the exit of splitscreen to a specific stage. This must be called before the exit is - * executed. - */ - private void logExitToStage(int exitReason, boolean toMainStage) { - mLogger.logExit(exitReason, - toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED, - toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */, - !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED, - !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */, - mSplitLayout.isLandscape()); - } - - class StageListenerImpl implements StageTaskListener.StageListenerCallbacks { - boolean mHasRootTask = false; - boolean mVisible = false; - boolean mHasChildren = false; - - @Override - public void onRootTaskAppeared() { - mHasRootTask = true; - StageCoordinator.this.onStageRootTaskAppeared(this); - } - - @Override - public void onStatusChanged(boolean visible, boolean hasChildren) { - if (!mHasRootTask) return; - - if (mHasChildren != hasChildren) { - mHasChildren = hasChildren; - StageCoordinator.this.onStageHasChildrenChanged(this); - } - if (mVisible != visible) { - mVisible = visible; - StageCoordinator.this.onStageVisibilityChanged(this); - } - } - - @Override - public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) { - StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible); - } - - @Override - public void onRootTaskVanished() { - reset(); - StageCoordinator.this.onStageRootTaskVanished(this); - } - - @Override - public void onNoLongerSupportMultiWindow() { - if (mMainStage.isActive()) { - StageCoordinator.this.exitSplitScreen(null /* childrenToTop */, - SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW); - } - } - - private void reset() { - mHasRootTask = false; - mVisible = false; - mHasChildren = false; - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - pw.println(prefix + "mHasRootTask=" + mHasRootTask); - pw.println(prefix + "mVisible=" + mVisible); - pw.println(prefix + "mHasChildren=" + mHasChildren); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java deleted file mode 100644 index 7b679580fa87..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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.wm.shell.stagesplit; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; -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; -import android.util.SparseArray; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.WindowContainerTransaction; - -import androidx.annotation.NonNull; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.SurfaceUtils; -import com.android.wm.shell.common.SyncTransactionQueue; - -import java.io.PrintWriter; - -/** - * Base class that handle common task org. related for split-screen stages. - * Note that this class and its sub-class do not directly perform hierarchy operations. - * They only serve to hold a collection of tasks and provide APIs like - * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator} - * to perform operations in-sync with other containers. - * - * @see StageCoordinator - */ -class StageTaskListener implements ShellTaskOrganizer.TaskListener { - private static final String TAG = StageTaskListener.class.getSimpleName(); - - protected static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD}; - protected static final int[] CONTROLLED_WINDOWING_MODES = - {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; - protected static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE = - {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW}; - - /** Callback interface for listening to changes in a split-screen stage. */ - public interface StageListenerCallbacks { - void onRootTaskAppeared(); - - void onStatusChanged(boolean visible, boolean hasChildren); - - void onChildTaskStatusChanged(int taskId, boolean present, boolean visible); - - void onRootTaskVanished(); - void onNoLongerSupportMultiWindow(); - } - - private final StageListenerCallbacks mCallbacks; - private final SurfaceSession mSurfaceSession; - protected final SyncTransactionQueue mSyncQueue; - - protected ActivityManager.RunningTaskInfo mRootTaskInfo; - protected SurfaceControl mRootLeash; - protected SurfaceControl mDimLayer; - 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, - @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - mCallbacks = callbacks; - mSyncQueue = syncQueue; - mSurfaceSession = surfaceSession; - mStageTaskUnfoldController = stageTaskUnfoldController; - taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); - } - - int getChildCount() { - return mChildrenTaskInfo.size(); - } - - boolean containsTask(int taskId) { - return mChildrenTaskInfo.contains(taskId); - } - - /** - * Returns the top activity uid for the top child task. - */ - int getTopChildTaskUid() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); - if (info.topActivityInfo == null) { - continue; - } - return info.topActivityInfo.applicationInfo.uid; - } - return 0; - } - - /** @return {@code true} if this listener contains the currently focused task. */ - boolean isFocused() { - if (mRootTaskInfo == null) { - return false; - } - - if (mRootTaskInfo.isFocused) { - return true; - } - - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - if (mChildrenTaskInfo.valueAt(i).isFocused) { - return true; - } - } - - return false; - } - - @Override - @CallSuper - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mRootTaskInfo == null && !taskInfo.hasParentTask()) { - mRootLeash = leash; - mRootTaskInfo = taskInfo; - mCallbacks.onRootTaskAppeared(); - sendStatusChanged(); - mSyncQueue.runInSync(t -> { - t.hide(mRootLeash); - mDimLayer = - SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession); - }); - } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { - final int taskId = taskInfo.taskId; - mChildrenLeashes.put(taskId, leash); - mChildrenTaskInfo.put(taskId, taskInfo); - updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); - mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible); - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); - } else { - throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo - + "\n mRootTaskInfo: " + mRootTaskInfo); - } - - if (mStageTaskUnfoldController != null) { - mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash); - } - } - - @Override - @CallSuper - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - if (!taskInfo.supportsMultiWindow) { - // Leave split screen if the task no longer supports multi window. - mCallbacks.onNoLongerSupportMultiWindow(); - return; - } - if (mRootTaskInfo.taskId == taskInfo.taskId) { - mRootTaskInfo = taskInfo; - } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { - mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, - taskInfo.isVisible); - if (!ENABLE_SHELL_TRANSITIONS) { - updateChildTaskSurface( - taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); - } - } else { - throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo - + "\n mRootTaskInfo: " + mRootTaskInfo); - } - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); - } - - @Override - @CallSuper - public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - final int taskId = taskInfo.taskId; - if (mRootTaskInfo.taskId == taskId) { - mCallbacks.onRootTaskVanished(); - mSyncQueue.runInSync(t -> t.remove(mDimLayer)); - mRootTaskInfo = null; - } else if (mChildrenTaskInfo.contains(taskId)) { - mChildrenTaskInfo.remove(taskId); - mChildrenLeashes.remove(taskId); - mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); - } else { - throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo - + "\n mRootTaskInfo: " + mRootTaskInfo); - } - - if (mStageTaskUnfoldController != null) { - mStageTaskUnfoldController.onTaskVanished(taskInfo); - } - } - - @Override - public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { - b.setParent(findTaskSurface(taskId)); - } - - @Override - public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, - SurfaceControl.Transaction t) { - t.reparent(sc, findTaskSurface(taskId)); - } - - private SurfaceControl findTaskSurface(int taskId) { - if (mRootTaskInfo.taskId == taskId) { - return mRootLeash; - } else if (mChildrenLeashes.contains(taskId)) { - return mChildrenLeashes.get(taskId); - } else { - throw new IllegalArgumentException("There is no surface for taskId=" + taskId); - } - } - - void setBounds(Rect bounds, WindowContainerTransaction wct) { - wct.setBounds(mRootTaskInfo.token, bounds); - } - - void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) { - if (!containsTask(taskId)) { - return; - } - wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */); - } - - void setVisibility(boolean visible, WindowContainerTransaction wct) { - wct.reorder(mRootTaskInfo.token, visible /* onTop */); - } - - void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener, - @SplitScreen.StageType int stage) { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - int taskId = mChildrenTaskInfo.keyAt(i); - listener.onTaskStageChanged(taskId, stage, - mChildrenTaskInfo.get(taskId).isVisible); - } - } - - private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo, - SurfaceControl leash, boolean firstAppeared) { - final Point taskPositionInParent = taskInfo.positionInParent; - mSyncQueue.runInSync(t -> { - t.setWindowCrop(leash, null); - t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); - if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } - }); - } - - private void sendStatusChanged() { - mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0); - } - - @Override - @CallSuper - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java deleted file mode 100644 index 62b9da6d4715..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * 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.stagesplit; - -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); - } - } -} |