diff options
| author | 2021-10-27 23:43:43 +0000 | |
|---|---|---|
| committer | 2021-10-27 23:43:43 +0000 | |
| commit | 3c28b0505100dfcdcdd52dd0633d9c27c8abeefc (patch) | |
| tree | 7a1f0fd5fc026194b8d74ed686215eab05ff6ff8 | |
| parent | e55b8be52a8b03740370a5b6e7a3c4c6469c5cd9 (diff) | |
| parent | 1679d92eef99900a831b755533f6026dae20cfb9 (diff) | |
Merge "Update split resizing transition to match design" into sc-v2-dev am: 1679d92eef
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16001109
Change-Id: Ide2ed7f963646c9899c0840069cd6d9c8cfcde8f
22 files changed, 393 insertions, 482 deletions
diff --git a/libs/WindowManager/Shell/res/layout/split_decor.xml b/libs/WindowManager/Shell/res/layout/split_decor.xml new file mode 100644 index 000000000000..9ffa5e8aa179 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/split_decor.xml @@ -0,0 +1,30 @@ +<!-- + ~ 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. + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent"> + + <ImageView android:id="@+id/split_resizing_icon" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center" + android:padding="0dp" + android:visibility="gone" + android:background="@null"/> + +</FrameLayout> diff --git a/libs/WindowManager/Shell/res/layout/split_outline.xml b/libs/WindowManager/Shell/res/layout/split_outline.xml index 13a30f5a0423..6cb9ebb72790 100644 --- a/libs/WindowManager/Shell/res/layout/split_outline.xml +++ b/libs/WindowManager/Shell/res/layout/split_outline.xml @@ -1,18 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> +<!-- + ~ 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. + --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index 9113c79d40f8..e87b150a0709 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -103,8 +103,6 @@ public final class ShellCommandHandlerImpl { return runMoveToSideStage(args, pw); case "removeFromSideStage": return runRemoveFromSideStage(args, pw); - case "setSideStageOutline": - return runSetSideStageOutline(args, pw); case "setSideStagePosition": return runSetSideStagePosition(args, pw); case "setSideStageVisibility": @@ -163,18 +161,6 @@ public final class ShellCommandHandlerImpl { return true; } - private boolean runSetSideStageOutline(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First arguments are "WMShell" and command name. - pw.println("Error: whether to enable or disable side stage outline border should be" - + " provided as arguments"); - return false; - } - final boolean enable = new Boolean(args[2]); - mSplitScreenOptional.ifPresent(split -> split.setSideStageOutline(enable)); - return true; - } - private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index 6a252e0d7dcb..c8449a362988 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -131,7 +131,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou mSplitLayout = new SplitLayout(TAG + "SplitDivider", mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, - mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer()); + mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(), + true /* applyDismissingParallax */); mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout); final WindowContainerToken token1 = task1.token; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java index 55c5125f0a00..4b138e43bc3f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java @@ -23,16 +23,22 @@ import android.view.SurfaceSession; * Helpers for handling surface. */ public class SurfaceUtils { - /** Creates a dim layer above indicated host surface. */ + /** Creates a dim layer above host surface. */ public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host, String name, SurfaceSession surfaceSession) { - SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession) + final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession); + t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f}); + return dimLayer; + } + + /** Creates a color layer for host surface. */ + public static SurfaceControl makeColorLayer(SurfaceControl host, String name, + SurfaceSession surfaceSession) { + return new SurfaceControl.Builder(surfaceSession) .setParent(host) .setColorLayer() .setName(name) - .setCallsite("SurfaceUtils.makeDimLayer") + .setCallsite("SurfaceUtils.makeColorLayer") .build(); - t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f}); - return dimLayer; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java new file mode 100644 index 000000000000..ad9ebb2ef6ae --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -0,0 +1,184 @@ +/* + * 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.common.split; + +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 static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Binder; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.R; +import com.android.wm.shell.common.SurfaceUtils; + +/** + * Handles split decor like showing resizing hint for a specific split. + */ +public class SplitDecorManager extends WindowlessWindowManager { + private static final String TAG = SplitDecorManager.class.getSimpleName(); + private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground"; + + private final IconProvider mIconProvider; + private final SurfaceSession mSurfaceSession; + + private Drawable mIcon; + private ImageView mResizingIconView; + private SurfaceControlViewHost mViewHost; + private SurfaceControl mHostLeash; + private SurfaceControl mIconLeash; + private SurfaceControl mBackgroundLeash; + + public SplitDecorManager(Configuration configuration, IconProvider iconProvider, + SurfaceSession surfaceSession) { + super(configuration, null /* rootSurface */, null /* hostInputToken */); + mIconProvider = iconProvider; + mSurfaceSession = surfaceSession; + } + + @Override + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName(TAG) + .setHidden(true) + .setParent(mHostLeash) + .setCallsite("SplitDecorManager#attachToParentSurface"); + mIconLeash = builder.build(); + b.setParent(mIconLeash); + } + + /** Inflates split decor surface on the root surface. */ + public void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) { + if (mIconLeash != null && mViewHost != null) { + return; + } + + context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, + null /* options */); + mHostLeash = rootLeash; + mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this); + + final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context) + .inflate(R.layout.split_decor, null); + mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon); + + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); + lp.width = rootBounds.width(); + lp.height = rootBounds.height(); + lp.token = new Binder(); + lp.setTitle(TAG); + 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); + } + + /** Releases the surfaces for split decor. */ + public void release(SurfaceControl.Transaction t) { + if (mViewHost != null) { + mViewHost.release(); + mViewHost = null; + } + if (mIconLeash != null) { + t.remove(mIconLeash); + mIconLeash = null; + } + if (mBackgroundLeash != null) { + t.remove(mBackgroundLeash); + mBackgroundLeash = null; + } + mHostLeash = null; + mIcon = null; + mResizingIconView = null; + } + + /** Showing resizing hint. */ + public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds, + SurfaceControl.Transaction t) { + if (mResizingIconView == null) { + return; + } + + if (mIcon == null) { + // TODO: add fade-in animation. + mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, + RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); + t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) + .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1) + .show(mBackgroundLeash); + + mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo); + mResizingIconView.setImageDrawable(mIcon); + mResizingIconView.setVisibility(View.VISIBLE); + + WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); + lp.width = mIcon.getIntrinsicWidth(); + lp.height = mIcon.getIntrinsicHeight(); + mViewHost.relayout(lp); + t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER); + } + + t.setPosition(mIconLeash, + newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2, + newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2); + } + + /** Stops showing resizing hint. */ + public void onResized(Rect newBounds, SurfaceControl.Transaction t) { + if (mResizingIconView == null) { + return; + } + + if (mIcon != null) { + mResizingIconView.setVisibility(View.GONE); + mResizingIconView.setImageDrawable(null); + t.remove(mBackgroundLeash).hide(mIconLeash); + mIcon = null; + mBackgroundLeash = null; + } + } + + private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) { + final int taskBgColor = taskInfo.taskDescription.getBackgroundColor(); + return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index b1b0382b401c..a9ed64c525eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -103,7 +103,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final SplitWindowManager mSplitWindowManager; private final DisplayImeController mDisplayImeController; private final ImePositionProcessor mImePositionProcessor; - private final DismissingParallaxPolicy mDismissingParallaxPolicy; + private final DismissingEffectPolicy mDismissingEffectPolicy; private final ShellTaskOrganizer mTaskOrganizer; private final InsetsState mInsetsState = new InsetsState(); @@ -119,7 +119,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public SplitLayout(String windowName, Context context, Configuration configuration, SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, - DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) { + DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer, + boolean applyDismissingParallax) { mContext = context.createConfigurationContext(configuration); mOrientation = configuration.orientation; mRotation = configuration.windowConfiguration.getRotation(); @@ -129,7 +130,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange parentContainerCallbacks); mTaskOrganizer = taskOrganizer; mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId()); - mDismissingParallaxPolicy = new DismissingParallaxPolicy(); + mDismissingEffectPolicy = new DismissingEffectPolicy(applyDismissingParallax); final Resources resources = context.getResources(); mDividerWindowWidth = resources.getDimensionPixelSize( @@ -247,7 +248,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } DockedDividerUtils.sanitizeStackBounds(mBounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(mBounds2, false /** topLeft */); - mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape); + mDismissingEffectPolicy.applyDividerPosition(position, isLandscape); } /** Inflates {@link DividerView} on the root surface. */ @@ -290,7 +291,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange */ void updateDivideBounds(int position) { updateBounds(position); - mSplitWindowManager.setResizingSplits(true); mSplitLayoutHandler.onLayoutSizeChanging(this); } @@ -298,13 +298,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mDividePosition = position; updateBounds(mDividePosition); mSplitLayoutHandler.onLayoutSizeChanged(this); - mSplitWindowManager.setResizingSplits(false); } /** Resets divider position. */ public void resetDividerPosition() { mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position; - mSplitWindowManager.setResizingSplits(false); updateBounds(mDividePosition); mWinToken1 = null; mWinToken2 = null; @@ -360,8 +358,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange @VisibleForTesting void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) { if (from == to) { - // No animation run, it should stop resizing here. - mSplitWindowManager.setResizingSplits(false); + // No animation run, still callback to stop resizing. + mSplitLayoutHandler.onLayoutSizeChanged(this); return; } ValueAnimator animator = ValueAnimator @@ -425,7 +423,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return; } - mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2); + mDismissingEffectPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2); } /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ @@ -543,7 +541,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange * Calculates and applies proper dismissing parallax offset and dimming value to hint users * dismissing gesture. */ - private class DismissingParallaxPolicy { + private class DismissingEffectPolicy { + /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */ + private final boolean mApplyParallax; + // The current dismissing side. int mDismissingSide = DOCKED_INVALID; @@ -553,6 +554,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // The dimming value to hint the dismissing side and progress. float mDismissingDimValue = 0.0f; + DismissingEffectPolicy(boolean applyDismissingParallax) { + mApplyParallax = applyDismissingParallax; + } + /** * Applies a parallax to the task to hint dismissing progress. * @@ -627,12 +632,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return false; } - t.setPosition(targetLeash, - mTempRect.left + mDismissingParallaxOffset.x, - mTempRect.top + mDismissingParallaxOffset.y); - // Transform the screen-based split bounds to surface-based crop bounds. - mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y); - t.setWindowCrop(targetLeash, mTempRect); + if (mApplyParallax) { + t.setPosition(targetLeash, + mTempRect.left + mDismissingParallaxOffset.x, + mTempRect.top + mDismissingParallaxOffset.y); + // Transform the screen-based split bounds to surface-based crop bounds. + mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y); + t.setWindowCrop(targetLeash, mTempRect); + } t.setAlpha(targetDimLayer, mDismissingDimValue) .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f); return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 47dceb392183..08754d328772 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -25,7 +25,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import android.app.ActivityTaskManager; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -33,8 +32,6 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.Binder; import android.os.IBinder; -import android.os.RemoteException; -import android.util.Slog; import android.view.IWindow; import android.view.InsetsState; import android.view.LayoutInflater; @@ -59,7 +56,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { private Context mContext; private SurfaceControlViewHost mViewHost; private SurfaceControl mLeash; - private boolean mResizingSplits; private DividerView mDividerView; public interface ParentContainerCallbacks { @@ -154,16 +150,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { mDividerView.setInteractive(interactive); } - void setResizingSplits(boolean resizing) { - if (resizing == mResizingSplits) return; - try { - ActivityTaskManager.getService().setSplitScreenResizing(resizing); - mResizingSplits = resizing; - } catch (RemoteException e) { - Slog.w(TAG, "Error calling setSplitScreenResizing", e); - } - } - /** * Gets {@link SurfaceControl} of the surface holding divider view. @return {@code null} if not * feasible. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index a47a15287dda..6440ef0b6b11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -19,11 +19,13 @@ package com.android.wm.shell.splitscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import android.annotation.Nullable; +import android.content.Context; import android.graphics.Rect; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; @@ -37,11 +39,11 @@ class MainStage extends StageTaskListener { private boolean mIsActive = false; - MainStage(ShellTaskOrganizer taskOrganizer, int displayId, + MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, + SurfaceSession surfaceSession, IconProvider iconProvider, @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, + super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider, stageTaskUnfoldController); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java deleted file mode 100644 index a459c8dbfa34..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/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.splitscreen; - -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/splitscreen/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java deleted file mode 100644 index 94dd9b24875a..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/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.splitscreen; - -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/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index dc8fb9fbd7a3..51104e44569e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -16,20 +16,16 @@ package com.android.wm.shell.splitscreen; -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.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.SyncTransactionQueue; /** @@ -38,19 +34,15 @@ import com.android.wm.shell.common.SyncTransactionQueue; * * @see StageCoordinator */ -class SideStage extends StageTaskListener implements - DisplayInsetsController.OnInsetsChangedListener { +class SideStage extends StageTaskListener { 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, + SurfaceSession surfaceSession, IconProvider iconProvider, @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, + super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider, stageTaskUnfoldController); - mContext = context; } void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, @@ -83,62 +75,4 @@ class SideStage extends StageTaskListener implements 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/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 3b75bfb933c9..36f140614deb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -54,6 +54,7 @@ import androidx.annotation.Nullable; import com.android.internal.logging.InstanceId; import com.android.internal.util.FrameworkStatsLog; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayImeController; @@ -78,6 +79,7 @@ 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. @@ -96,6 +98,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private final Transitions mTransitions; private final TransactionPool mTransactionPool; private final SplitscreenEventLogger mLogger; + private final IconProvider mIconProvider; private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider; private StageCoordinator mStageCoordinator; @@ -105,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellExecutor mainExecutor, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, - Transitions transitions, TransactionPool transactionPool, + Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; @@ -118,6 +121,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mTransactionPool = transactionPool; mUnfoldControllerProvider = unfoldControllerProvider; mLogger = new SplitscreenEventLogger(); + mIconProvider = iconProvider; } public SplitScreen asSplitScreen() { @@ -140,7 +144,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, - mUnfoldControllerProvider); + mIconProvider, mUnfoldControllerProvider); } } @@ -165,10 +169,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.removeFromSideStage(taskId); } - public void setSideStageOutline(boolean enable) { - mStageCoordinator.setSideStageOutline(enable); - } - public void setSideStagePosition(@SplitPosition int sideStagePosition) { mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); } @@ -319,9 +319,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } transaction.apply(); transaction.close(); - return new RemoteAnimationTarget[]{ - mStageCoordinator.getDividerBarLegacyTarget(), - mStageCoordinator.getOutlineLegacyTarget()}; + return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()}; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 72d9880d0aa1..3b62afc7320e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -80,6 +80,7 @@ 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.launcher3.icons.IconProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayImeController; @@ -151,10 +152,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Whether the device is supporting legacy split or not. */ private boolean mUseLegacySplit; - @SplitScreen.StageType private int mDismissTop = NO_DISMISS; + @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; + @SplitScreen.StageType + private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; private final Runnable mOnTransitionAnimationComplete = () -> { // If still playing, let it finish. @@ -169,22 +172,23 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks = new SplitWindowManager.ParentContainerCallbacks() { - @Override - public void attachToParentSurface(SurfaceControl.Builder b) { - mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b); - } + @Override + public void attachToParentSurface(SurfaceControl.Builder b) { + mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b); + } - @Override - public void onLeashReady(SurfaceControl leash) { - mSyncQueue.runInSync(t -> applyDividerVisibility(t)); - } - }; + @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, + IconProvider iconProvider, Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { mContext = context; mDisplayId = displayId; @@ -196,11 +200,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideUnfoldController = unfoldControllerProvider.get().orElse(null); mMainStage = new MainStage( + mContext, mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue, mSurfaceSession, + iconProvider, mMainUnfoldController); mSideStage = new SideStage( mContext, @@ -209,10 +215,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStageListener, mSyncQueue, mSurfaceSession, + iconProvider, mSideUnfoldController); mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; - mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage); mRootTDAOrganizer.registerListener(displayId, this); final DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); @@ -284,10 +290,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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, @@ -697,9 +699,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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)) { + // 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); } @@ -719,7 +722,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, t.setVisibility(mSideStage.mRootLeash, bothStageVisible) .setVisibility(mMainStage.mRootLeash, bothStageVisible); applyDividerVisibility(t); - applyOutlineVisibility(t); } }); } @@ -741,19 +743,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void applyOutlineVisibility(SurfaceControl.Transaction t) { - final SurfaceControl outlineLeash = mSideStage.getOutlineLeash(); - if (outlineLeash == null) { - return; - } - - if (mDividerVisible) { - t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER); - } else { - t.hide(outlineLeash); - } - } - private void onStageHasChildrenChanged(StageListenerImpl stageListener) { final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; @@ -817,8 +806,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onLayoutSizeChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); - mSideStage.setOutlineVisibility(false); + mSyncQueue.runInSync(t -> { + updateSurfaceBounds(layout, t); + mMainStage.onResizing(getMainStageBounds(), t); + mSideStage.onResizing(getSideStageBounds(), t); + }); } @Override @@ -827,8 +819,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateWindowBounds(layout, wct); updateUnfoldBounds(); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); - mSideStage.setOutlineVisibility(true); + mSyncQueue.runInSync(t -> { + updateSurfaceBounds(layout, t); + mMainStage.onResized(getMainStageBounds(), t); + mSideStage.onResized(getSideStageBounds(), t); + }); mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } @@ -893,7 +888,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer); + mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); if (mMainUnfoldController != null && mSideUnfoldController != null) { @@ -1216,18 +1211,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 + " "; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 6f1a09dc88e6..5100c5625db6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -26,6 +26,7 @@ import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIO import android.annotation.CallSuper; import android.annotation.Nullable; import android.app.ActivityManager; +import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.util.SparseArray; @@ -35,9 +36,11 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SurfaceUtils; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.split.SplitDecorManager; import java.io.PrintWriter; @@ -72,25 +75,31 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { void onNoLongerSupportMultiWindow(); } + private final Context mContext; private final StageListenerCallbacks mCallbacks; private final SurfaceSession mSurfaceSession; - protected final SyncTransactionQueue mSyncQueue; + private final SyncTransactionQueue mSyncQueue; + private final IconProvider mIconProvider; protected ActivityManager.RunningTaskInfo mRootTaskInfo; protected SurfaceControl mRootLeash; protected SurfaceControl mDimLayer; protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>(); private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>(); + // TODO(b/204308910): Extracts SplitDecorManager related code to common package. + private SplitDecorManager mSplitDecorManager; private final StageTaskUnfoldController mStageTaskUnfoldController; - StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId, + StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, + SurfaceSession surfaceSession, IconProvider iconProvider, @Nullable StageTaskUnfoldController stageTaskUnfoldController) { + mContext = context; mCallbacks = callbacks; mSyncQueue = syncQueue; mSurfaceSession = surfaceSession; + mIconProvider = iconProvider; mStageTaskUnfoldController = stageTaskUnfoldController; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); } @@ -142,13 +151,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { if (mRootTaskInfo == null && !taskInfo.hasParentTask()) { mRootLeash = leash; mRootTaskInfo = taskInfo; + mSplitDecorManager = new SplitDecorManager( + mRootTaskInfo.configuration, + mIconProvider, + mSurfaceSession); mCallbacks.onRootTaskAppeared(); sendStatusChanged(); - mSyncQueue.runInSync(t -> { - t.hide(mRootLeash); - mDimLayer = - SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession); - }); + mSyncQueue.runInSync(t -> mDimLayer = + SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession)); } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); @@ -179,6 +189,17 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } if (mRootTaskInfo.taskId == taskInfo.taskId) { + // Inflates split decor view only when the root task is visible. + if (mRootTaskInfo.isVisible != taskInfo.isVisible) { + mSyncQueue.runInSync(t -> { + if (taskInfo.isVisible) { + mSplitDecorManager.inflate(mContext, mRootLeash, + taskInfo.configuration.windowConfiguration.getBounds()); + } else { + mSplitDecorManager.release(t); + } + }); + } mRootTaskInfo = taskInfo; } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); @@ -205,8 +226,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { final int taskId = taskInfo.taskId; if (mRootTaskInfo.taskId == taskId) { mCallbacks.onRootTaskVanished(); - mSyncQueue.runInSync(t -> t.remove(mDimLayer)); mRootTaskInfo = null; + mSyncQueue.runInSync(t -> { + t.remove(mDimLayer); + mSplitDecorManager.release(t); + }); } else if (mChildrenTaskInfo.contains(taskId)) { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); @@ -237,6 +261,18 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } + void onResizing(Rect newBounds, SurfaceControl.Transaction t) { + if (mSplitDecorManager != null && mRootTaskInfo != null) { + mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, t); + } + } + + void onResized(Rect newBounds, SurfaceControl.Transaction t) { + if (mSplitDecorManager != null) { + mSplitDecorManager.onResized(newBounds, t); + } + } + void setBounds(Rect bounds, WindowContainerTransaction wct) { wct.setBounds(mRootTaskInfo.token, bounds); } 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 index 574e379921b1..60a6cd78c4fc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java @@ -882,7 +882,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer); + mDisplayImeController, mTaskOrganizer, true /* applyDismissingParallax */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); if (mMainUnfoldController != null && mSideUnfoldController != null) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index b4caeb5de4ec..73eebad040d8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -68,7 +68,8 @@ public class SplitLayoutTests extends ShellTestCase { mSplitLayoutHandler, mCallbacks, mDisplayImeController, - mTaskOrganizer)); + mTaskOrganizer, + false /* applyDismissingParallax */)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java index 2bcc45e2587d..c9720671f49c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java @@ -25,10 +25,13 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerTransaction; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.SyncTransactionQueue; @@ -41,22 +44,24 @@ import org.mockito.MockitoAnnotations; /** Tests for {@link MainStage} */ @SmallTest @RunWith(AndroidJUnit4.class) -public class MainStageTests { +public class MainStageTests extends ShellTestCase { @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private StageTaskListener.StageListenerCallbacks mCallbacks; @Mock private SyncTransactionQueue mSyncQueue; @Mock private ActivityManager.RunningTaskInfo mRootTaskInfo; @Mock private SurfaceControl mRootLeash; + @Mock private IconProvider mIconProvider; private WindowContainerTransaction mWct = new WindowContainerTransaction(); private SurfaceSession mSurfaceSession = new SurfaceSession(); private MainStage mMainStage; @Before + @UiThreadTest public void setup() { MockitoAnnotations.initMocks(this); mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); - mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue, - mSurfaceSession, null); + mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, + mSyncQueue, mSurfaceSession, mIconProvider, null); mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java index 838aa811bb87..1857faadb433 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java @@ -33,6 +33,7 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; @@ -54,6 +55,7 @@ public class SideStageTests extends ShellTestCase { @Mock private SyncTransactionQueue mSyncQueue; @Mock private ActivityManager.RunningTaskInfo mRootTask; @Mock private SurfaceControl mRootLeash; + @Mock private IconProvider mIconProvider; @Spy private WindowContainerTransaction mWct; private SurfaceSession mSurfaceSession = new SurfaceSession(); private SideStage mSideStage; @@ -64,7 +66,7 @@ public class SideStageTests extends ShellTestCase { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue, mSurfaceSession, null); + mSyncQueue, mSurfaceSession, mIconProvider, null); mSideStage.onTaskAppeared(mRootTask, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 05496b059030..d5dee824ee9b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -55,6 +55,7 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; @@ -89,6 +90,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private Transitions mTransitions; @Mock private SurfaceSession mSurfaceSession; @Mock private SplitscreenEventLogger mLogger; + @Mock private IconProvider mIconProvider; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -107,11 +109,13 @@ public class SplitTransitionTests extends ShellTestCase { doReturn(mockExecutor).when(mTransitions).getAnimExecutor(); doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire(); mSplitLayout = SplitTestUtils.createMockSplitLayout(); - mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null); + mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, + mIconProvider, null); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, null); + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, + mIconProvider, null); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 3ed72e2c079e..53d5076f5835 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -35,10 +35,13 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerTransaction; +import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.SyncTransactionQueue; @@ -57,7 +60,7 @@ import org.mockito.MockitoAnnotations; */ @SmallTest @RunWith(AndroidJUnit4.class) -public final class StageTaskListenerTests { +public final class StageTaskListenerTests extends ShellTestCase { private static final boolean ENABLE_SHELL_TRANSITIONS = SystemProperties.getBoolean("persist.debug.shell_transit", false); @@ -68,6 +71,8 @@ public final class StageTaskListenerTests { @Mock private SyncTransactionQueue mSyncQueue; @Mock + private IconProvider mIconProvider; + @Mock private StageTaskUnfoldController mStageTaskUnfoldController; @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor; @@ -77,14 +82,17 @@ public final class StageTaskListenerTests { private StageTaskListener mStageTaskListener; @Before + @UiThreadTest public void setup() { MockitoAnnotations.initMocks(this); mStageTaskListener = new StageTaskListener( + mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue, mSurfaceSession, + mIconProvider, mStageTaskUnfoldController); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index fd9783a392a5..0a33930d404c 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -463,13 +463,13 @@ public abstract class WMShellBaseModule { @ShellMainThread ShellExecutor mainExecutor, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, Transitions transitions, - TransactionPool transactionPool, + TransactionPool transactionPool, IconProvider iconProvider, Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) { if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) { return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context, rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController, - displayInsetsController, transitions, - transactionPool, stageTaskUnfoldControllerProvider)); + displayInsetsController, transitions, transactionPool, iconProvider, + stageTaskUnfoldControllerProvider)); } else { return Optional.empty(); } |