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
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 0000000..9ffa5e8
--- /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 13a30f5..6cb9ebb 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 9113c79..e87b150 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 @@
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 @@
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 6a252e0..c8449a3 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 @@
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 55c5125..4b138e4 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 @@
* 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 0000000..ad9ebb2
--- /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 b1b0382..a9ed64c 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 @@
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 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 @@
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 @@
}
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 @@
*/
void updateDivideBounds(int position) {
updateBounds(position);
- mSplitWindowManager.setResizingSplits(true);
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
@@ -298,13 +298,11 @@
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 @@
@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 @@
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 @@
* 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 @@
// 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 @@
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 47dceb3..08754d3 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_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.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 @@
private Context mContext;
private SurfaceControlViewHost mViewHost;
private SurfaceControl mLeash;
- private boolean mResizingSplits;
private DividerView mDividerView;
public interface ParentContainerCallbacks {
@@ -154,16 +150,6 @@
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 a47a152..6440ef0 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 @@
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 @@
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 a459c8d..0000000
--- 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 94dd9b2..0000000
--- 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 dc8fb9f..51104e4 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 @@
*
* @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 @@
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 3b75bfb..36f1406 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 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 @@
/**
* 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 @@
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 @@
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 @@
mTransactionPool = transactionPool;
mUnfoldControllerProvider = unfoldControllerProvider;
mLogger = new SplitscreenEventLogger();
+ mIconProvider = iconProvider;
}
public SplitScreen asSplitScreen() {
@@ -140,7 +144,7 @@
mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mLogger,
- mUnfoldControllerProvider);
+ mIconProvider, mUnfoldControllerProvider);
}
}
@@ -165,10 +169,6 @@
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 @@
}
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 72d9880..3b62afc 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.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 @@
/** 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 @@
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 @@
mSideUnfoldController = unfoldControllerProvider.get().orElse(null);
mMainStage = new MainStage(
+ mContext,
mTaskOrganizer,
mDisplayId,
mMainStageListener,
mSyncQueue,
mSurfaceSession,
+ iconProvider,
mMainUnfoldController);
mSideStage = new SideStage(
mContext,
@@ -209,10 +215,10 @@
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 @@
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 @@
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 @@
t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
.setVisibility(mMainStage.mRootLeash, bothStageVisible);
applyDividerVisibility(t);
- applyOutlineVisibility(t);
}
});
}
@@ -741,19 +743,6 @@
}
}
- 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 @@
@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 @@
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 @@
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 @@
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 6f1a09d..5100c56 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 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 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 574e379..60a6cd7 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 @@
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 b4caeb5..73eebad 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 @@
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 2bcc45e..c972067 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.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 @@
/** 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 838aa81..1857faa 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.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 @@
@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 @@
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 05496b0..d5dee82 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.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 @@
@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 @@
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 3ed72e2..53d5076 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.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 @@
*/
@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 @@
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
+ private IconProvider mIconProvider;
+ @Mock
private StageTaskUnfoldController mStageTaskUnfoldController;
@Captor
private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
@@ -77,14 +82,17 @@
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 fd9783a..0a33930 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 @@
@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();
}