summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl5
-rw-r--r--core/java/com/android/internal/policy/DockedDividerUtils.java39
-rw-r--r--core/proto/android/server/windowmanagerservice.proto1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl5
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java449
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java428
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java310
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java167
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java322
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java77
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java126
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java141
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java12
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java2
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java9
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java10
-rw-r--r--services/core/java/com/android/server/wm/Task.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskTile.java34
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java63
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java16
37 files changed, 1625 insertions, 873 deletions
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 180507cd7e9c..3c475c1a8083 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -315,11 +315,6 @@ interface IActivityTaskManager {
void positionTaskInStack(int taskId, int stackId, int position);
void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
- /**
- * Dismisses split-screen multi-window mode.
- * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
- */
- void dismissSplitScreenMode(boolean toTop);
/**
* Dismisses PiP
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index c68e50692c2a..b61b9dea2554 100644
--- a/core/java/com/android/internal/policy/DockedDividerUtils.java
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -16,14 +16,15 @@
package com.android.internal.policy;
-import android.graphics.Rect;
-
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+import android.content.res.Resources;
+import android.graphics.Rect;
+
/**
* Utility functions for docked stack divider used by both window manager and System UI.
*
@@ -105,23 +106,6 @@ public class DockedDividerUtils {
return start + (end - start) / 2 - dividerSize / 2;
}
- public static int getDockSideFromCreatedMode(boolean dockOnTopOrLeft,
- boolean isHorizontalDivision) {
- if (dockOnTopOrLeft) {
- if (isHorizontalDivision) {
- return DOCKED_TOP;
- } else {
- return DOCKED_LEFT;
- }
- } else {
- if (isHorizontalDivision) {
- return DOCKED_BOTTOM;
- } else {
- return DOCKED_RIGHT;
- }
- }
- }
-
public static int invertDockSide(int dockSide) {
switch (dockSide) {
case DOCKED_LEFT:
@@ -136,4 +120,21 @@ public class DockedDividerUtils {
return DOCKED_INVALID;
}
}
+
+ /** Returns the inset distance from the divider window edge to the dividerview. */
+ public static int getDividerInsets(Resources res) {
+ return res.getDimensionPixelSize(com.android.internal.R.dimen.docked_stack_divider_insets);
+ }
+
+ /** Returns the size of the divider */
+ public static int getDividerSize(Resources res, int dividerInsets) {
+ final int windowWidth = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ return windowWidth - 2 * dividerInsets;
+ }
+
+ /** Returns the docked-stack side */
+ public static int getDockSide(int displayWidth, int displayHeight) {
+ return displayWidth > displayHeight ? DOCKED_LEFT : DOCKED_TOP;
+ }
}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b0b9ce6f9968..08db4544d5e7 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -275,6 +275,7 @@ message TaskProto {
optional float adjust_divider_amount = 25;
optional bool animating_bounds = 26;
optional float minimize_amount = 27;
+ optional bool created_by_organizer = 28;
}
/* represents ActivityRecordProto */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2d288ff40b2c..b1d39f59f789 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -123,4 +123,9 @@ interface ISystemUiProxy {
*/
void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
in Insets visibleInsets, int taskId) = 21;
+
+ /**
+ * Sets the split-screen divider minimized state
+ */
+ void setSplitScreenMinimized(boolean minimized) = 22;
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 27fe37ef4e69..dc5cb1f9fb6b 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -61,6 +61,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationListener;
@@ -329,6 +330,7 @@ public class Dependency {
@Inject Lazy<DisplayImeController> mDisplayImeController;
@Inject Lazy<RecordingController> mRecordingController;
@Inject Lazy<ProtoTracer> mProtoTracer;
+ @Inject Lazy<Divider> mDivider;
@Inject
public Dependency() {
@@ -530,6 +532,7 @@ public class Dependency {
mProviders.put(AutoHideController.class, mAutoHideController::get);
mProviders.put(RecordingController.class, mRecordingController::get);
+ mProviders.put(Divider.class, mDivider::get);
sDependency = this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
deleted file mode 100644
index 5c0df179dd27..000000000000
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui;
-
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IDockedStackListener;
-import android.view.WindowManagerGlobal;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Utility wrapper to listen for whether or not a docked stack exists, to be
- * used for things like the different overview icon in that mode.
- */
-public class DockedStackExistsListener {
-
- private static final String TAG = "DockedStackExistsListener";
-
- private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
- private static boolean mLastExists;
-
- static {
- try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- new IDockedStackListener.Stub() {
- @Override
- public void onDividerVisibilityChanged(boolean b) throws RemoteException {
-
- }
-
- @Override
- public void onDockedStackExistsChanged(boolean exists)
- throws RemoteException {
- DockedStackExistsListener.onDockedStackExistsChanged(exists);
- }
-
- @Override
- public void onDockedStackMinimizedChanged(boolean b, long l, boolean b1)
- throws RemoteException {
-
- }
-
- @Override
- public void onAdjustedForImeChanged(boolean b, long l)
- throws RemoteException {
-
- }
-
- @Override
- public void onDockSideChanged(int i) throws RemoteException {
-
- }
- });
- } catch (RemoteException e) {
- Log.e(TAG, "Failed registering docked stack exists listener", e);
- }
- }
-
-
- private static void onDockedStackExistsChanged(boolean exists) {
- mLastExists = exists;
- synchronized (sCallbacks) {
- sCallbacks.removeIf(wf -> {
- Consumer<Boolean> l = wf.get();
- if (l != null) l.accept(exists);
- return l == null;
- });
- }
- }
-
- public static void register(Consumer<Boolean> callback) {
- callback.accept(mLastExists);
- synchronized (sCallbacks) {
- sCallbacks.add(new WeakReference<>(callback));
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9f64b397e9d9..fd484ef61c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -380,6 +380,14 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis
taskId, mHandler, null);
}
+ @Override
+ public void setSplitScreenMinimized(boolean minimized) {
+ Divider divider = mDividerOptional.get();
+ if (divider != null) {
+ divider.setMinimized(minimized);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 5ae095421a80..4f20492c60a3 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -22,10 +22,8 @@ import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LE
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
-import android.util.Log;
import android.view.IWindowManager;
import android.view.KeyEvent;
-import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.policy.DividerSnapAlgorithm;
@@ -94,29 +92,24 @@ public class ShortcutKeyDispatcher extends SystemUI
}
private void handleDockKey(long shortcutCode) {
- try {
- int dockSide = mWindowManagerService.getDockedStackSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
- // Split the screen
- mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
- } else {
- // If there is already a docked window, we respond by resizing the docking pane.
- DividerView dividerView = mDivider.getView();
- DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
- int dividerPosition = dividerView.getCurrentPosition();
- DividerSnapAlgorithm.SnapTarget currentTarget =
- snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
- DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
- ? snapAlgorithm.getPreviousTarget(currentTarget)
- : snapAlgorithm.getNextTarget(currentTarget);
- dividerView.startDragging(true /* animate */, false /* touching */);
- dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
- true /* logMetrics */);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "handleDockKey() failed.");
+ if (mDivider == null || !mDivider.inSplitMode()) {
+ // Split the screen
+ mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
+ ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+ : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
+ } else {
+ // If there is already a docked window, we respond by resizing the docking pane.
+ DividerView dividerView = mDivider.getView();
+ DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+ int dividerPosition = dividerView.getCurrentPosition();
+ DividerSnapAlgorithm.SnapTarget currentTarget =
+ snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+ DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
+ ? snapAlgorithm.getPreviousTarget(currentTarget)
+ : snapAlgorithm.getNextTarget(currentTarget);
+ dividerView.startDragging(true /* animate */, false /* touching */);
+ dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
+ true /* logMetrics */);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 90cc0e57f50c..ba9eb4a6ede1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,69 +17,281 @@
package com.android.systemui.stackdivider;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
import android.os.RemoteException;
+import android.provider.Settings;
import android.util.Log;
-import android.view.IDockedStackListener;
+import android.util.Slog;
+import android.view.IWindowContainer;
import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.WindowContainerTransaction;
+import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayChangeController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.DisplayLayout;
+import com.android.systemui.wm.SystemWindows;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Optional;
+import java.util.function.Consumer;
+
+import javax.inject.Singleton;
import dagger.Lazy;
/**
* Controls the docked stack divider.
*/
-public class Divider extends SystemUI implements DividerView.DividerCallbacks {
+@Singleton
+public class Divider extends SystemUI implements DividerView.DividerCallbacks,
+ DisplayController.OnDisplaysChangedListener {
private static final String TAG = "Divider";
+
+ static final boolean DEBUG = true;
+
+ static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
private final Optional<Lazy<Recents>> mRecentsOptionalLazy;
private DividerWindowManager mWindowManager;
private DividerView mView;
private final DividerState mDividerState = new DividerState();
- private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
private boolean mMinimized = false;
private boolean mAdjustedForIme = false;
private boolean mHomeStackResizable = false;
private ForcedResizableInfoActivityController mForcedResizableController;
+ private SystemWindows mSystemWindows;
+ final SurfaceSession mSurfaceSession = new SurfaceSession();
+ private DisplayController mDisplayController;
+ private DisplayImeController mImeController;
+
+ // Keeps track of real-time split geometry including snap positions and ime adjustments
+ private SplitDisplayLayout mSplitLayout;
+
+ // Transient: this contains the layout calculated for a new rotation requested by WM. This is
+ // kept around so that we can wait for a matching configuration change and then use the exact
+ // layout that we sent back to WM.
+ private SplitDisplayLayout mRotateSplitLayout;
+
+ private Handler mHandler;
+ private KeyguardStateController mKeyguardStateController;
+
+ private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
+ new ArrayList<>();
+
+ private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
+
+ private DisplayChangeController.OnDisplayChangingListener mRotationController =
+ (display, fromRotation, toRotation, t) -> {
+ DisplayLayout displayLayout =
+ new DisplayLayout(mDisplayController.getDisplayLayout(display));
+ SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
+ sdl.rotateTo(toRotation);
+ mRotateSplitLayout = sdl;
+ int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position
+ : mView.getCurrentPosition();
+ DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
+ final DividerSnapAlgorithm.SnapTarget target =
+ snap.calculateNonDismissingSnapTarget(position);
+ sdl.resizeSplits(target.position, t);
+
+ if (inSplitMode()) {
+ WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
+ }
+ };
+
+ private IWindowContainer mLastImeTarget = null;
+ private boolean mShouldAdjustForIme = false;
+
+ private DisplayImeController.ImePositionProcessor mImePositionProcessor =
+ new DisplayImeController.ImePositionProcessor() {
+ private int mStartTop = 0;
+ private int mFinalTop = 0;
+ @Override
+ public void onImeStartPositioning(int displayId, int imeTop, int finalImeTop,
+ boolean showing, SurfaceControl.Transaction t) {
+ mStartTop = imeTop;
+ mFinalTop = finalImeTop;
+ if (showing) {
+ try {
+ mLastImeTarget = ActivityTaskManager.getTaskOrganizerController()
+ .getImeTarget(displayId);
+ mShouldAdjustForIme = !mSplitLayout.mDisplayLayout.isLandscape()
+ && (mLastImeTarget.asBinder()
+ == mSplits.mSecondary.token.asBinder());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to get IME target", e);
+ }
+ }
+ if (!mShouldAdjustForIme) {
+ setAdjustedForIme(false);
+ return;
+ }
+ mView.setAdjustedForIme(showing, showing
+ ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
+ : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
+ // Reposition the server's secondary split position so that it evaluates
+ // insets properly.
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (showing) {
+ mSplitLayout.updateAdjustedBounds(finalImeTop, imeTop, finalImeTop);
+ wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary);
+ } else {
+ wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary);
+ }
+ try {
+ ActivityTaskManager.getTaskOrganizerController()
+ .applyContainerTransaction(wct, null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ setAdjustedForIme(showing);
+ }
+
+ @Override
+ public void onImePositionChanged(int displayId, int imeTop,
+ SurfaceControl.Transaction t) {
+ if (!mShouldAdjustForIme) {
+ return;
+ }
+ mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+ mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+ mSplitLayout.mAdjustedSecondary);
+ final boolean showing = mFinalTop < mStartTop;
+ final float progress = ((float) (imeTop - mStartTop)) / (mFinalTop - mStartTop);
+ final float fraction = showing ? progress : 1.f - progress;
+ mView.setResizeDimLayer(t, true /* primary */, fraction * 0.3f);
+ }
+
+ @Override
+ public void onImeEndPositioning(int displayId, int imeTop,
+ boolean showing, SurfaceControl.Transaction t) {
+ if (!mShouldAdjustForIme) {
+ return;
+ }
+ mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+ mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+ mSplitLayout.mAdjustedSecondary);
+ mView.setResizeDimLayer(t, true /* primary */, showing ? 0.3f : 0.f);
+ }
+ };
- public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
+ public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, Handler handler,
+ KeyguardStateController keyguardStateController) {
super(context);
+ mDisplayController = displayController;
+ mSystemWindows = systemWindows;
+ mImeController = imeController;
+ mHandler = handler;
+ mKeyguardStateController = keyguardStateController;
mRecentsOptionalLazy = recentsOptionalLazy;
+ mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
}
@Override
public void start() {
- mWindowManager = new DividerWindowManager(mContext);
- update(mContext.getResources().getConfiguration());
- mDockDividerVisibilityListener = new DockDividerVisibilityListener();
+ mWindowManager = new DividerWindowManager(mSystemWindows);
+ mDisplayController.addDisplayWindowListener(this);
+ // Hide the divider when keyguard is showing. Even though keyguard/statusbar is above
+ // everything, it is actually transparent except for notifications, so we still need to
+ // hide any surfaces that are below it.
+ // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+
+ }
+
+ @Override
+ public void onKeyguardShowingChanged() {
+ if (!inSplitMode() || mView == null || mView.getViewRootImpl() == null
+ || mView.getViewRootImpl().getSurfaceControl() == null) {
+ return;
+ }
+ mView.setHidden(mKeyguardStateController.isShowing());
+ }
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+
+ }
+ });
+ // Don't initialize the divider or anything until we get the default display.
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ mImeController.addPositionProcessor(mImePositionProcessor);
+ mDisplayController.addDisplayChangingController(mRotationController);
try {
- WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
- mDockDividerVisibilityListener);
+ mSplits.init(ActivityTaskManager.getTaskOrganizerController(), mSurfaceSession);
+ // Set starting tile bounds based on middle target
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ mSplitLayout.resizeSplits(midPos, tct);
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+ null /* organizer */);
} catch (Exception e) {
- Log.e(TAG, "Failed to register docked stack listener", e);
+ Slog.e(TAG, "Failed to register docked stack listener", e);
}
- mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
+ update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ if (mRotateSplitLayout == null) {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ mSplitLayout.resizeSplits(midPos, tct);
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ } else if (mRotateSplitLayout != null
+ && mSplitLayout.mDisplayLayout.rotation()
+ == mRotateSplitLayout.mDisplayLayout.rotation()) {
+ mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
+ mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
+ mRotateSplitLayout = null;
+ }
update(newConfig);
}
+ Handler getHandler() {
+ return mHandler;
+ }
+
public DividerView getView() {
return mView;
}
@@ -92,18 +304,25 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
return mHomeStackResizable;
}
+ /** {@code true} if this is visible */
+ public boolean inSplitMode() {
+ return mView != null && mView.getVisibility() == View.VISIBLE;
+ }
+
private void addDivider(Configuration configuration) {
+ Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
mView = (DividerView)
- LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
- mView.injectDependencies(mWindowManager, mDividerState, this);
+ LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
+ DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
+ mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout);
mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- final int size = mContext.getResources().getDimensionPixelSize(
+ final int size = dctx.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? size : MATCH_PARENT;
- final int height = landscape ? MATCH_PARENT : size;
- mWindowManager.add(mView, width, height);
+ final int width = landscape ? size : displayLayout.width();
+ final int height = landscape ? displayLayout.height() : size;
+ mWindowManager.add(mView, width, height, mContext.getDisplayId());
}
private void removeDivider() {
@@ -116,63 +335,84 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
private void update(Configuration configuration) {
removeDivider();
addDivider(configuration);
- if (mMinimized) {
+ if (mMinimized && mView != null) {
mView.setMinimizedDockStack(true, mHomeStackResizable);
updateTouchable();
}
}
- private void updateVisibility(final boolean visible) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- if (mVisible != visible) {
- mVisible = visible;
- mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-
- // Update state because animations won't finish.
- mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
- }
+ void updateVisibility(final boolean visible) {
+ if (mVisible != visible) {
+ mVisible = visible;
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+
+ if (visible) {
+ mView.enterSplitMode(mHomeStackResizable);
+ // Update state because animations won't finish.
+ mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
+ } else {
+ mView.exitSplitMode();
+ // un-minimize so that next entry triggers minimize anim.
+ mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable);
}
- });
+ // Notify existence listeners
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.removeIf(wf -> {
+ Consumer<Boolean> l = wf.get();
+ if (l != null) l.accept(visible);
+ return l == null;
+ });
+ }
+ }
+ }
+
+ private void setHomeStackResizable(boolean resizable) {
+ if (mHomeStackResizable == resizable) {
+ return;
+ }
+ mHomeStackResizable = resizable;
+ if (!inSplitMode()) {
+ return;
+ }
+ WindowManagerProxy.applyHomeTasksMinimized(mSplitLayout, mSplits.mSecondary.token);
}
private void updateMinimizedDockedStack(final boolean minimized, final long animDuration,
final boolean isHomeStackResizable) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mHomeStackResizable = isHomeStackResizable;
- if (mMinimized != minimized) {
- mMinimized = minimized;
- updateTouchable();
- if (animDuration > 0) {
- mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
- } else {
- mView.setMinimizedDockStack(minimized, isHomeStackResizable);
- }
- }
- }
- });
+ setHomeStackResizable(isHomeStackResizable);
+ if (animDuration > 0) {
+ mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
+ } else {
+ mView.setMinimizedDockStack(minimized, isHomeStackResizable);
+ }
+ updateTouchable();
}
- private void notifyDockedStackExistsChanged(final boolean exists) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mForcedResizableController.notifyDockedStackExistsChanged(exists);
+ /** Switch to minimized state if appropriate */
+ public void setMinimized(final boolean minimized) {
+ mHandler.post(() -> {
+ if (!inSplitMode()) {
+ return;
+ }
+ if (mMinimized == minimized) {
+ return;
}
+ mMinimized = minimized;
+ mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
+ updateTouchable();
});
}
- private void updateTouchable() {
- mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
+ void setAdjustedForIme(boolean adjustedForIme) {
+ if (mAdjustedForIme == adjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = adjustedForIme;
+ updateTouchable();
}
- public void onRecentsActivityStarting() {
- if (mView != null) {
- mView.onRecentsActivityStarting();
- }
+ private void updateTouchable() {
+ mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
}
/**
@@ -206,6 +446,9 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
}
public void onAppTransitionFinished() {
+ if (mView == null) {
+ return;
+ }
mForcedResizableController.onAppTransitionFinished();
}
@@ -231,46 +474,66 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks {
pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
}
- class DockDividerVisibilityListener extends IDockedStackListener.Stub {
+ long getAnimDuration() {
+ float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mContext.getResources().getFloat(
+ com.android.internal.R.dimen
+ .config_appTransitionAnimationDurationScaleDefault));
+ final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
+ return (long) (transitionDuration * transitionScale);
+ }
- @Override
- public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
- updateVisibility(visible);
+ /** Register a listener that gets called whenever the existence of the divider changes */
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ listener.accept(inSplitMode());
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.add(new WeakReference<>(listener));
}
+ }
- @Override
- public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
- notifyDockedStackExistsChanged(exists);
- }
+ void startEnterSplit() {
+ // Set resizable directly here because applyEnterSplit already resizes home stack.
+ mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
+ }
- @Override
- public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
- boolean isHomeStackResizable) throws RemoteException {
- mHomeStackResizable = isHomeStackResizable;
- updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable);
+ void ensureMinimizedSplit() {
+ final boolean wasMinimized = mMinimized;
+ mMinimized = true;
+ setHomeStackResizable(mSplits.mSecondary.isResizable());
+ if (!inSplitMode()) {
+ // Wasn't in split-mode yet, so enter now.
+ if (DEBUG) {
+ Log.d(TAG, " entering split mode with minimized=true");
+ }
+ updateVisibility(true /* visible */);
+ } else if (!wasMinimized) {
+ if (DEBUG) {
+ Log.d(TAG, " in split mode, but minimizing ");
+ }
+ // Was already in split-mode, update just minimized state.
+ updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+ mHomeStackResizable);
}
+ }
- @Override
- public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
- throws RemoteException {
- mView.post(() -> {
- if (mAdjustedForIme != adjustedForIme) {
- mAdjustedForIme = adjustedForIme;
- updateTouchable();
- if (!mMinimized) {
- if (animDuration > 0) {
- mView.setAdjustedForIme(adjustedForIme, animDuration);
- } else {
- mView.setAdjustedForIme(adjustedForIme);
- }
- }
- }
- });
+ void ensureNormalSplit() {
+ if (!inSplitMode()) {
+ // Wasn't in split-mode, so enter now.
+ if (DEBUG) {
+ Log.d(TAG, " enter split mode unminimized ");
+ }
+ mMinimized = false;
+ updateVisibility(true /* visible */);
}
-
- @Override
- public void onDockSideChanged(final int newDockSide) throws RemoteException {
- mView.post(() -> mView.notifyDockSideChanged(newDockSide));
+ if (mMinimized) {
+ // Was in minimized state, so leave that.
+ if (DEBUG) {
+ Log.d(TAG, " in split mode already, but unminimizing ");
+ }
+ mMinimized = false;
+ updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+ mHomeStackResizable);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 49f4d5e91659..f3b25535e128 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -17,8 +17,14 @@
package com.android.systemui.stackdivider;
import android.content.Context;
+import android.os.Handler;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.SystemWindows;
import java.util.Optional;
@@ -35,7 +41,11 @@ import dagger.Provides;
public class DividerModule {
@Singleton
@Provides
- static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
- return new Divider(context, recentsOptionalLazy);
+ static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, @Main Handler handler,
+ KeyguardStateController keyguardStateController) {
+ return new Divider(context, recentsOptionalLazy, displayController, systemWindows,
+ imeController, handler, keyguardStateController);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 9fe6e844e0d2..fdd04b9d5858 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,12 +16,8 @@
package com.android.systemui.stackdivider;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -40,10 +36,11 @@ import android.os.Message;
import android.util.AttributeSet;
import android.view.Choreographer;
import android.view.Display;
-import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.PointerIcon;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
@@ -75,6 +72,7 @@ import com.android.systemui.statusbar.FlingAnimationUtils;
*/
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
+ private static final String TAG = "DividerView";
public interface DividerCallbacks {
void onDraggingStart();
@@ -123,14 +121,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private int mTouchSlop;
private boolean mBackgroundLifted;
private boolean mIsInMinimizeInteraction;
- private SnapTarget mSnapTargetBeforeMinimized;
+ SnapTarget mSnapTargetBeforeMinimized;
private int mDividerInsets;
private final Display mDefaultDisplay;
- private int mDisplayWidth;
- private int mDisplayHeight;
- private int mDisplayRotation;
- private int mDividerWindowWidth;
+
private int mDividerSize;
private int mTouchElevation;
private int mLongPressEntraceAnimDuration;
@@ -147,8 +142,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private DividerWindowManager mWindowManager;
private VelocityTracker mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
- private DividerSnapAlgorithm mSnapAlgorithm;
- private DividerSnapAlgorithm mMinimizedSnapAlgorithm;
+ private SplitDisplayLayout mSplitLayout;
private DividerCallbacks mCallback;
private final Rect mStableInsets = new Rect();
@@ -163,6 +157,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private DividerState mState;
private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
+ private SplitScreenTaskOrganizer mTiles;
+ boolean mFirstLayout = true;
+ int mDividerPositionX;
+ int mDividerPositionY;
// The view is removed or in the process of been removed from the system.
private boolean mRemoved;
@@ -172,7 +170,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESIZE_STACK:
- resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
+ resizeStackSurfaces(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
break;
default:
super.handleMessage(msg);
@@ -228,16 +226,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public boolean performAccessibilityAction(View host, int action, Bundle args) {
int currentPosition = getCurrentPosition();
SnapTarget nextTarget = null;
+ DividerSnapAlgorithm snapAlgorithm = mSplitLayout.getSnapAlgorithm();
if (action == R.id.action_move_tl_full) {
- nextTarget = mSnapAlgorithm.getDismissEndTarget();
+ nextTarget = snapAlgorithm.getDismissEndTarget();
} else if (action == R.id.action_move_tl_70) {
- nextTarget = mSnapAlgorithm.getLastSplitTarget();
+ nextTarget = snapAlgorithm.getLastSplitTarget();
} else if (action == R.id.action_move_tl_50) {
- nextTarget = mSnapAlgorithm.getMiddleTarget();
+ nextTarget = snapAlgorithm.getMiddleTarget();
} else if (action == R.id.action_move_tl_30) {
- nextTarget = mSnapAlgorithm.getFirstSplitTarget();
+ nextTarget = snapAlgorithm.getFirstSplitTarget();
} else if (action == R.id.action_move_rb_full) {
- nextTarget = mSnapAlgorithm.getDismissStartTarget();
+ nextTarget = snapAlgorithm.getDismissStartTarget();
}
if (nextTarget != null) {
startDragging(true /* animate */, false /* touching */);
@@ -284,11 +283,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackground = findViewById(R.id.docked_divider_background);
mMinimizedShadow = findViewById(R.id.minimized_dock_shadow);
mHandle.setOnTouchListener(this);
- mDividerWindowWidth = getResources().getDimensionPixelSize(
+ final int dividerWindowWidth = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
mDividerInsets = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
- mDividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+ mDividerSize = dividerWindowWidth - 2 * mDividerInsets;
mTouchElevation = getResources().getDimensionPixelSize(
R.dimen.docked_stack_divider_lift_elevation);
mLongPressEntraceAnimDuration = getResources().getInteger(
@@ -296,7 +295,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow);
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.3f);
- updateDisplayInfo();
boolean landscape = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
@@ -314,6 +312,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
&& !mIsInMinimizeInteraction) {
saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
}
+ mFirstLayout = true;
}
void onDividerRemoved() {
@@ -341,17 +340,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|| mStableInsets.bottom != insets.getStableInsetBottom()) {
mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
insets.getStableInsetRight(), insets.getStableInsetBottom());
- if (mSnapAlgorithm != null || mMinimizedSnapAlgorithm != null) {
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
- }
}
return super.onApplyWindowInsets(insets);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mFirstLayout) {
+ // Wait for first layout so that the ViewRootImpl surface has been created.
+ initializeSurfaceState();
+ mFirstLayout = false;
+ }
super.onLayout(changed, left, top, right, bottom);
int minimizeLeft = 0;
int minimizeTop = 0;
@@ -372,19 +371,16 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
- DividerCallbacks callback) {
+ DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl) {
mWindowManager = windowManager;
mState = dividerState;
mCallback = callback;
-
- // Set the previous position ratio before minimized state after attaching this divider
- if (mStableInsets.isEmpty()) {
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- }
+ mTiles = tiles;
+ mSplitLayout = sdl;
if (mState.mRatioPositionBeforeMinimized == 0) {
// Set the middle target as the initial state
- mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
+ mSnapTargetBeforeMinimized = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
} else {
repositionSnapTargetBeforeMinimized();
}
@@ -411,18 +407,34 @@ public class DividerView extends FrameLayout implements OnTouchListener,
return mOtherTaskRect;
}
+ private boolean inSplitMode() {
+ return getVisibility() == VISIBLE;
+ }
+
+ /** Unlike setVisible, this directly hides the surface without changing view visibility. */
+ void setHidden(boolean hidden) {
+ post(() -> {
+ if (!isViewSurfaceValid()) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ if (hidden) {
+ t.hide(getViewRootImpl().getSurfaceControl());
+ } else {
+ t.show(getViewRootImpl().getSurfaceControl());
+ }
+ t.apply();
+ mTiles.releaseTransaction(t);
+ });
+ }
+
public boolean startDragging(boolean animate, boolean touching) {
cancelFlingAnimation();
if (touching) {
mHandle.setTouching(true, animate);
}
- mDockSide = mWindowManagerProxy.getDockSide();
+ mDockSide = mSplitLayout.getPrimarySplitSide();
- // Update snap algorithm if rotation has occurred
- if (mDisplayRotation != mDefaultDisplay.getRotation()) {
- updateDisplayInfo();
- }
- initializeSnapAlgorithm();
mWindowManagerProxy.setResizing(true);
if (touching) {
mWindowManager.setSlippery(false);
@@ -431,7 +443,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mCallback != null) {
mCallback.onDraggingStart();
}
- return mDockSide != WindowManager.DOCKED_INVALID;
+ return inSplitMode();
}
public void stopDragging(int position, float velocity, boolean avoidDismissStart,
@@ -467,38 +479,22 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private void updateDockSide() {
- mDockSide = mWindowManagerProxy.getDockSide();
+ mDockSide = mSplitLayout.getPrimarySplitSide();
mMinimizedShadow.setDockSide(mDockSide);
}
- private void initializeSnapAlgorithm() {
- if (mSnapAlgorithm == null) {
- mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
- mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets, mDockSide);
- if (mSnapTargetBeforeMinimized != null && mSnapTargetBeforeMinimized.isMiddleTarget) {
- mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
- }
- }
- if (mMinimizedSnapAlgorithm == null) {
- mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
- mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(),
- mStableInsets, mDockSide, mDockedStackMinimized && mHomeStackResizable);
- }
- }
-
public DividerSnapAlgorithm getSnapAlgorithm() {
- initializeSnapAlgorithm();
- return mDockedStackMinimized && mHomeStackResizable ? mMinimizedSnapAlgorithm :
- mSnapAlgorithm;
+ return mDockedStackMinimized
+ && mHomeStackResizable ? mSplitLayout.getMinimizedSnapAlgorithm()
+ : mSplitLayout.getSnapAlgorithm();
}
public int getCurrentPosition() {
- getLocationOnScreen(mTempInt2);
- if (isHorizontalDivision()) {
- return mTempInt2[1] + mDividerInsets;
- } else {
- return mTempInt2[0] + mDividerInsets;
- }
+ return isHorizontalDivision() ? mDividerPositionY : mDividerPositionX;
+ }
+
+ public boolean isMinimized() {
+ return mDockedStackMinimized;
}
@Override
@@ -557,25 +553,25 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private void logResizeEvent(SnapTarget snapTarget) {
- if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
+ if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissStartTarget()) {
MetricsLogger.action(
mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
? LOG_VALUE_UNDOCK_MAX_OTHER
: LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissEndTarget()) {
MetricsLogger.action(
mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
? LOG_VALUE_UNDOCK_MAX_OTHER
: LOG_VALUE_UNDOCK_MAX_DOCKED);
- } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getMiddleTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
LOG_VALUE_RESIZE_50_50);
- } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getFirstSplitTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
dockSideTopLeft(mDockSide)
? LOG_VALUE_RESIZE_DOCKED_SMALLER
: LOG_VALUE_RESIZE_DOCKED_LARGER);
- } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) {
+ } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getLastSplitTarget()) {
MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
dockSideTopLeft(mDockSide)
? LOG_VALUE_RESIZE_DOCKED_LARGER
@@ -625,12 +621,16 @@ public class DividerView extends FrameLayout implements OnTouchListener,
: snapTarget.taskPosition,
snapTarget));
Runnable endAction = () -> {
- commitSnapFlags(snapTarget);
+ boolean dismissed = commitSnapFlags(snapTarget);
mWindowManagerProxy.setResizing(false);
updateDockSide();
mCurrentAnimator = null;
mEntranceAnimationRunning = false;
mExitAnimationRunning = false;
+ if (!dismissed) {
+ WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
+ ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+ }
if (mCallback != null) {
mCallback.onDraggingEnd();
}
@@ -642,12 +642,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
// position isn't negative.
final SnapTarget saveTarget;
if (snapTarget.position < 0) {
- saveTarget = mSnapAlgorithm.getMiddleTarget();
+ saveTarget = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
} else {
saveTarget = snapTarget;
}
- if (saveTarget.position != mSnapAlgorithm.getDismissEndTarget().position
- && saveTarget.position != mSnapAlgorithm.getDismissStartTarget().position) {
+ final DividerSnapAlgorithm snapAlgo = mSplitLayout.getSnapAlgorithm();
+ if (saveTarget.position != snapAlgo.getDismissEndTarget().position
+ && saveTarget.position != snapAlgo.getDismissStartTarget().position) {
saveSnapTargetBeforeMinimized(saveTarget);
}
}
@@ -701,11 +702,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
- private void commitSnapFlags(SnapTarget target) {
+ private boolean commitSnapFlags(SnapTarget target) {
if (target.flag == SnapTarget.FLAG_NONE) {
- return;
+ return false;
}
- boolean dismissOrMaximize;
+ final boolean dismissOrMaximize;
if (target.flag == SnapTarget.FLAG_DISMISS_START) {
dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
|| mDockSide == WindowManager.DOCKED_TOP;
@@ -713,12 +714,13 @@ public class DividerView extends FrameLayout implements OnTouchListener,
dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
|| mDockSide == WindowManager.DOCKED_BOTTOM;
}
- if (dismissOrMaximize) {
- mWindowManagerProxy.dismissDockedStack();
- } else {
- mWindowManagerProxy.maximizeDockedStack();
- }
- mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
+ mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize);
+ Transaction t = mTiles.getTransaction();
+ setResizeDimLayer(t, true /* primary */, 0f);
+ setResizeDimLayer(t, false /* primary */, 0f);
+ t.apply();
+ mTiles.releaseTransaction(t);
+ return true;
}
private void liftBackground() {
@@ -765,6 +767,28 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackgroundLifted = false;
}
+ private void initializeSurfaceState() {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ // Recalculate the split-layout's internal tile bounds
+ mSplitLayout.resizeSplits(midPos);
+ Transaction t = mTiles.getTransaction();
+ if (mDockedStackMinimized) {
+ int position = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget().position;
+ calculateBoundsForPosition(position, mDockSide, mDockedRect);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
+ mDividerPositionX = mDividerPositionY = position;
+ resizeSplitSurfaces(t, mDockedRect, mSplitLayout.mPrimary,
+ mOtherRect, mSplitLayout.mSecondary);
+ } else {
+ resizeSplitSurfaces(t, mSplitLayout.mPrimary, null,
+ mSplitLayout.mSecondary, null);
+ }
+ setResizeDimLayer(t, true /* primary */, 0.f /* alpha */);
+ setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */);
+ t.apply();
+ mTiles.releaseTransaction(t);
+ }
public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
mHomeStackResizable = isHomeStackResizable;
@@ -789,15 +813,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mDockedStackMinimized = minimized;
- if (mDisplayRotation != mDefaultDisplay.getRotation()) {
+ if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) {
// Splitscreen to minimize is about to starts after rotating landscape to seascape,
// update insets, display info and snap algorithm targets
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
repositionSnapTargetBeforeMinimized();
- updateDisplayInfo();
- } else {
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
}
if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) {
cancelFlingAnimation();
@@ -805,15 +825,51 @@ public class DividerView extends FrameLayout implements OnTouchListener,
// Relayout to recalculate the divider shadow when minimizing
requestLayout();
mIsInMinimizeInteraction = true;
- resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+ resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget());
} else {
- resizeStack(mSnapTargetBeforeMinimized);
+ resizeStackSurfaces(mSnapTargetBeforeMinimized);
mIsInMinimizeInteraction = false;
}
}
}
}
+ void enterSplitMode(boolean isHomeStackResizable) {
+ post(() -> {
+ if (!isViewSurfaceValid()) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ t.show(getViewRootImpl().getSurfaceControl()).apply();
+ mTiles.releaseTransaction(t);
+ });
+ if (isHomeStackResizable) {
+ SnapTarget miniMid = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget();
+ if (mDockedStackMinimized) {
+ mDividerPositionY = mDividerPositionX = miniMid.position;
+ }
+ }
+ }
+
+ private boolean isViewSurfaceValid() {
+ return getViewRootImpl() != null && getViewRootImpl().getSurfaceControl() != null
+ && getViewRootImpl().getSurfaceControl().isValid();
+ }
+
+ void exitSplitMode() {
+ // Reset tile bounds
+ post(() -> {
+ if (!isViewSurfaceValid()) {
+ return;
+ }
+ Transaction t = mTiles.getTransaction();
+ t.hide(getViewRootImpl().getSurfaceControl()).apply();
+ mTiles.releaseTransaction(t);
+ });
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
+ }
+
public void setMinimizedDockStack(boolean minimized, long animDuration,
boolean isHomeStackResizable) {
mHomeStackResizable = isHomeStackResizable;
@@ -844,14 +900,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDockedStackMinimized = minimized;
} else if (mDockedStackMinimized != minimized) {
mIsInMinimizeInteraction = true;
- mMinimizedSnapAlgorithm = null;
mDockedStackMinimized = minimized;
- initializeSnapAlgorithm();
stopDragging(minimized
? mSnapTargetBeforeMinimized.position
: getCurrentPosition(),
minimized
- ? mMinimizedSnapAlgorithm.getMiddleTarget()
+ ? mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget()
: mSnapTargetBeforeMinimized,
animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
setAdjustedForIme(false, animDuration);
@@ -865,18 +919,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
.start();
}
- public void setAdjustedForIme(boolean adjustedForIme) {
- updateDockSide();
- mHandle.setAlpha(adjustedForIme ? 0f : 1f);
- if (!adjustedForIme) {
- resetBackground();
- } else if (mDockSide == WindowManager.DOCKED_TOP) {
- mBackground.setPivotY(0);
- mBackground.setScaleY(ADJUSTED_FOR_IME_SCALE);
- }
- mAdjustedForIme = adjustedForIme;
- }
-
public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
updateDockSide();
mHandle.animate()
@@ -902,7 +944,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private void saveSnapTargetBeforeMinimized(SnapTarget target) {
mSnapTargetBeforeMinimized = target;
mState.mRatioPositionBeforeMinimized = (float) target.position /
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth);
+ (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+ : mSplitLayout.mDisplayLayout.width());
}
private void resetBackground() {
@@ -916,51 +959,17 @@ public class DividerView extends FrameLayout implements OnTouchListener,
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- updateDisplayInfo();
- }
-
- public void notifyDockSideChanged(int newDockSide) {
- int oldDockSide = mDockSide;
- mDockSide = newDockSide;
- mMinimizedShadow.setDockSide(mDockSide);
- requestLayout();
-
- // Update the snap position to the new docked side with correct insets
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
-
- if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
- || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
- repositionSnapTargetBeforeMinimized();
- }
-
- // Landscape to seascape rotation requires minimized to resize docked app correctly
- if (mHomeStackResizable && mDockedStackMinimized) {
- resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
- }
}
private void repositionSnapTargetBeforeMinimized() {
int position = (int) (mState.mRatioPositionBeforeMinimized *
- (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
- mSnapAlgorithm = null;
- initializeSnapAlgorithm();
+ (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+ : mSplitLayout.mDisplayLayout.width()));
// Set the snap target before minimized but do not save until divider is attached and not
// minimized because it does not know its minimized state yet.
- mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- }
-
- private void updateDisplayInfo() {
- mDisplayRotation = mDefaultDisplay.getRotation();
- final DisplayInfo info = new DisplayInfo();
- mDefaultDisplay.getDisplayInfo(info);
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
- mSnapAlgorithm = null;
- mMinimizedSnapAlgorithm = null;
- initializeSnapAlgorithm();
+ mSnapTargetBeforeMinimized =
+ mSplitLayout.getSnapAlgorithm().calculateNonDismissingSnapTarget(position);
}
private int calculatePosition(int touchX, int touchY) {
@@ -994,8 +1003,9 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
- DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
- mDisplayHeight, mDividerSize);
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect,
+ mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(),
+ mDividerSize);
}
public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
@@ -1005,16 +1015,61 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mSfChoreographer.scheduleAtSfVsync(mHandler, message);
}
- private void resizeStack(SnapTarget taskSnapTarget) {
- resizeStack(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+ private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
+ resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+ }
+
+ void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
+ post(() -> resizeSplitSurfaces(t, dockedRect, null, otherRect, null));
+ }
+
+ private void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect dockedTaskRect,
+ Rect otherRect, Rect otherTaskRect) {
+ dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect;
+ otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect;
+
+ mDividerPositionX = dockedRect.right;
+ mDividerPositionY = dockedRect.bottom;
+
+ t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
+ Rect crop = new Rect(dockedRect);
+ crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
+ -Math.min(dockedTaskRect.top - dockedRect.top, 0));
+ t.setWindowCrop(mTiles.mPrimarySurface, crop);
+ t.setPosition(mTiles.mSecondarySurface, otherTaskRect.left, otherTaskRect.top);
+ crop.set(otherRect);
+ crop.offsetTo(-(otherTaskRect.left - otherRect.left),
+ -(otherTaskRect.top - otherRect.top));
+ t.setWindowCrop(mTiles.mSecondarySurface, crop);
+ SurfaceControl dividerCtrl = getViewRootImpl() != null
+ ? getViewRootImpl().getSurfaceControl() : null;
+ if (dividerCtrl != null && dividerCtrl.isValid()) {
+ if (isHorizontalDivision()) {
+ t.setPosition(dividerCtrl, 0, mDividerPositionY - mDividerInsets);
+ } else {
+ t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0);
+ }
+ }
+ }
+
+ void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
+ SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
+ if (alpha <= 0.f) {
+ t.hide(dim);
+ } else {
+ t.setAlpha(dim, alpha);
+ t.show(dim);
+ }
}
- public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+ void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget) {
if (mRemoved) {
// This divider view has been removed so shouldn't have any additional influence.
return;
}
calculateBoundsForPosition(position, mDockSide, mDockedRect);
+ calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+ mOtherRect);
if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) {
return;
@@ -1025,6 +1080,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mBackground.invalidate();
}
+ Transaction t = mTiles.getTransaction();
mLastResizeRect.set(mDockedRect);
if (mHomeStackResizable && mIsInMinimizeInteraction) {
calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
@@ -1037,8 +1093,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
- mDockedTaskRect.left + mDividerSize, 0);
}
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
- mOtherTaskRect, null);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
+ mOtherTaskRect);
+ t.apply();
+ mTiles.releaseTransaction(t);
return;
}
@@ -1052,8 +1110,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
mOtherTaskRect);
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
- mOtherTaskRect, null);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
@@ -1066,8 +1123,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
if (mDockSide == DOCKED_RIGHT) {
mDockedTaskRect.offset(position - mStableInsets.left + mDividerSize, 0);
}
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
- mOtherTaskRect, mOtherInsetRect);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else if (taskPosition != TASK_POSITION_SAME) {
calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
mOtherRect);
@@ -1078,7 +1134,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ mTmpRect.set(0, 0, mSplitLayout.mDisplayLayout.width(),
+ mSplitLayout.mDisplayLayout.height());
alignTopLeft(mDockedRect, mDockedTaskRect);
alignTopLeft(mOtherRect, mOtherTaskRect);
mDockedInsetRect.set(mDockedTaskRect);
@@ -1094,15 +1151,15 @@ public class DividerView extends FrameLayout implements OnTouchListener,
taskPositionDocked);
applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
taskPositionOther);
- mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
- mOtherTaskRect, mOtherInsetRect);
+ resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
} else {
- mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
+ resizeSplitSurfaces(t, mDockedRect, null, mOtherRect, null);
}
SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
float dimFraction = getDimFraction(position, closestDismissTarget);
- mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
- getWindowingModeForDismissTarget(closestDismissTarget), dimFraction);
+ setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
+ t.apply();
+ mTiles.releaseTransaction(t);
}
private void applyExitAnimationParallax(Rect taskRect, int position) {
@@ -1156,10 +1213,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
SnapTarget snapTarget) {
if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
- return Math.max(mSnapAlgorithm.getFirstSplitTarget().position, mStartPosition);
+ return Math.max(mSplitLayout.getSnapAlgorithm().getFirstSplitTarget().position,
+ mStartPosition);
} else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
&& dockSideBottomRight(dockSide)) {
- return Math.min(mSnapAlgorithm.getLastSplitTarget().position, mStartPosition);
+ return Math.min(mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position,
+ mStartPosition);
} else {
return taskPosition;
}
@@ -1171,19 +1230,19 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
int position, int taskPosition) {
float fraction = Math.min(1, Math.max(0,
- mSnapAlgorithm.calculateDismissingFraction(position)));
+ mSplitLayout.getSnapAlgorithm().calculateDismissingFraction(position)));
SnapTarget dismissTarget = null;
SnapTarget splitTarget = null;
int start = 0;
- if (position <= mSnapAlgorithm.getLastSplitTarget().position
+ if (position <= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
&& dockSideTopLeft(dockSide)) {
- dismissTarget = mSnapAlgorithm.getDismissStartTarget();
- splitTarget = mSnapAlgorithm.getFirstSplitTarget();
+ dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
+ splitTarget = mSplitLayout.getSnapAlgorithm().getFirstSplitTarget();
start = taskPosition;
- } else if (position >= mSnapAlgorithm.getLastSplitTarget().position
+ } else if (position >= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
&& dockSideBottomRight(dockSide)) {
- dismissTarget = mSnapAlgorithm.getDismissEndTarget();
- splitTarget = mSnapAlgorithm.getLastSplitTarget();
+ dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissEndTarget();
+ splitTarget = mSplitLayout.getSnapAlgorithm().getLastSplitTarget();
start = splitTarget.position;
}
if (dismissTarget != null && fraction > 0f
@@ -1236,14 +1295,10 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
}
- private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) {
- if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
+ private boolean isDismissTargetPrimary(SnapTarget dismissTarget) {
+ return (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
|| (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
- && dockSideBottomRight(mDockSide))) {
- return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- } else {
- return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
+ && dockSideBottomRight(mDockSide));
}
/**
@@ -1297,7 +1352,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
void onDockedFirstAnimationFrame() {
- saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+ saveSnapTargetBeforeMinimized(mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onDockedTopTask() {
@@ -1307,8 +1362,9 @@ public class DividerView extends FrameLayout implements OnTouchListener,
updateDockSide();
mEntranceAnimationRunning = true;
- resizeStack(calculatePositionForInsetBounds(), mSnapAlgorithm.getMiddleTarget().position,
- mSnapAlgorithm.getMiddleTarget());
+ resizeStackSurfaces(calculatePositionForInsetBounds(),
+ mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
+ mSplitLayout.getSnapAlgorithm().getMiddleTarget());
}
void onRecentsDrawn() {
@@ -1337,13 +1393,12 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
void onUndockingTask() {
- int dockSide = mWindowManagerProxy.getDockSide();
- if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable
- || !mDockedStackMinimized)) {
+ int dockSide = mSplitLayout.getPrimarySplitSide();
+ if (inSplitMode() && (mHomeStackResizable || !mDockedStackMinimized)) {
startDragging(false /* animate */, false /* touching */);
SnapTarget target = dockSideTopLeft(dockSide)
- ? mSnapAlgorithm.getDismissEndTarget()
- : mSnapAlgorithm.getDismissStartTarget();
+ ? mSplitLayout.getSnapAlgorithm().getDismissEndTarget()
+ : mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
// Don't start immediately - give a little bit time to settle the drag resize change.
mExitAnimationRunning = true;
@@ -1354,8 +1409,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
}
private int calculatePositionForInsetBounds() {
- mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- mTmpRect.inset(mStableInsets);
+ mSplitLayout.mDisplayLayout.getStableBounds(mTmpRect);
return DockedDividerUtils.calculatePositionForBounds(mTmpRect, mDockSide, mDividerSize);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 2486d6534e8d..bd843b0e0f9e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -26,12 +26,13 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.view.View;
import android.view.WindowManager;
+import com.android.systemui.wm.SystemWindows;
+
/**
* Manages the window parameters of the docked stack divider.
*/
@@ -39,15 +40,16 @@ public class DividerWindowManager {
private static final String WINDOW_TITLE = "DockedStackDivider";
- private final WindowManager mWindowManager;
+ private final SystemWindows mSystemWindows;
private WindowManager.LayoutParams mLp;
private View mView;
- public DividerWindowManager(Context ctx) {
- mWindowManager = ctx.getSystemService(WindowManager.class);
+ public DividerWindowManager(SystemWindows systemWindows) {
+ mSystemWindows = systemWindows;
}
- public void add(View view, int width, int height) {
+ /** Add a divider view */
+ public void add(View view, int width, int height, int displayId) {
mLp = new WindowManager.LayoutParams(
width, height, TYPE_DOCK_DIVIDER,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
@@ -60,13 +62,13 @@ public class DividerWindowManager {
view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- mWindowManager.addView(view, mLp);
+ mSystemWindows.addView(view, mLp, displayId, TYPE_DOCK_DIVIDER);
mView = view;
}
public void remove() {
if (mView != null) {
- mWindowManager.removeView(mView);
+ mSystemWindows.removeView(mView);
}
mView = null;
}
@@ -81,7 +83,7 @@ public class DividerWindowManager {
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
@@ -95,7 +97,7 @@ public class DividerWindowManager {
changed = true;
}
if (changed) {
- mWindowManager.updateViewLayout(mView, mLp);
+ mSystemWindows.updateViewLayout(mView, mLp);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index c6ac309a0f4e..db7996eed7f0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -31,6 +31,8 @@ import com.android.systemui.R;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.function.Consumer;
+
/**
* Controller that decides when to show the {@link ForcedResizableInfoActivity}.
*/
@@ -52,6 +54,12 @@ public class ForcedResizableInfoActivityController {
}
};
+ private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
+ if (!exists) {
+ mPackagesShownInSession.clear();
+ }
+ };
+
/** Record of force resized task that's pending to be handled. */
private class PendingTaskRecord {
int taskId;
@@ -67,7 +75,7 @@ public class ForcedResizableInfoActivityController {
}
}
- public ForcedResizableInfoActivityController(Context context) {
+ public ForcedResizableInfoActivityController(Context context, Divider divider) {
mContext = context;
ActivityManagerWrapper.getInstance().registerTaskStackListener(
new TaskStackChangeListener() {
@@ -87,12 +95,7 @@ public class ForcedResizableInfoActivityController {
activityLaunchOnSecondaryDisplayFailed();
}
});
- }
-
- public void notifyDockedStackExistsChanged(boolean exists) {
- if (!exists) {
- mPackagesShownInSession.clear();
- }
+ divider.registerInSplitScreenListener(mDockedStackExistsListener);
}
public void onAppTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
new file mode 100644
index 000000000000..b19f560f2f50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.TypedValue;
+import android.view.WindowContainerTransaction;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.wm.DisplayLayout;
+
+/**
+ * Handles split-screen related internal display layout. In general, this represents the
+ * WM-facing understanding of the splits.
+ */
+public class SplitDisplayLayout {
+ /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+ * restrict IME adjustment so that a min portion of top stack remains visible.*/
+ private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+ private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
+ SplitScreenTaskOrganizer mTiles;
+ DisplayLayout mDisplayLayout;
+ Context mContext;
+
+ // Lazy stuff
+ boolean mResourcesValid = false;
+ int mDividerSize;
+ int mDividerSizeInactive;
+ private DividerSnapAlgorithm mSnapAlgorithm = null;
+ private DividerSnapAlgorithm mMinimizedSnapAlgorithm = null;
+ Rect mPrimary = null;
+ Rect mSecondary = null;
+ Rect mAdjustedPrimary = null;
+ Rect mAdjustedSecondary = null;
+
+ public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskOrganizer taskTiles) {
+ mTiles = taskTiles;
+ mDisplayLayout = dl;
+ mContext = ctx;
+ }
+
+ void rotateTo(int newRotation) {
+ mDisplayLayout.rotateTo(mContext.getResources(), newRotation);
+ final Configuration config = new Configuration();
+ config.unset();
+ config.orientation = mDisplayLayout.getOrientation();
+ Rect tmpRect = new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ tmpRect.inset(mDisplayLayout.nonDecorInsets());
+ config.windowConfiguration.setAppBounds(tmpRect);
+ tmpRect.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ tmpRect.inset(mDisplayLayout.stableInsets());
+ config.screenWidthDp = (int) (tmpRect.width() / mDisplayLayout.density());
+ config.screenHeightDp = (int) (tmpRect.height() / mDisplayLayout.density());
+ mContext = mContext.createConfigurationContext(config);
+ mSnapAlgorithm = null;
+ mMinimizedSnapAlgorithm = null;
+ mResourcesValid = false;
+ }
+
+ private void updateResources() {
+ if (mResourcesValid) {
+ return;
+ }
+ mResourcesValid = true;
+ Resources res = mContext.getResources();
+ mDividerSize = DockedDividerUtils.getDividerSize(res,
+ DockedDividerUtils.getDividerInsets(res));
+ mDividerSizeInactive = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, DIVIDER_WIDTH_INACTIVE_DP, res.getDisplayMetrics());
+ }
+
+ int getPrimarySplitSide() {
+ return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP;
+ }
+
+ boolean isMinimized() {
+ return mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+ || mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS;
+ }
+
+ DividerSnapAlgorithm getSnapAlgorithm() {
+ if (mSnapAlgorithm == null) {
+ updateResources();
+ boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+ mSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+ isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide());
+ }
+ return mSnapAlgorithm;
+ }
+
+ DividerSnapAlgorithm getMinimizedSnapAlgorithm() {
+ if (mMinimizedSnapAlgorithm == null) {
+ updateResources();
+ boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+ mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+ isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide(),
+ true /* isMinimized */);
+ }
+ return mMinimizedSnapAlgorithm;
+ }
+
+ void resizeSplits(int position) {
+ mPrimary = mPrimary == null ? new Rect() : mPrimary;
+ mSecondary = mSecondary == null ? new Rect() : mSecondary;
+ calcSplitBounds(position, mPrimary, mSecondary);
+ }
+
+ void resizeSplits(int position, WindowContainerTransaction t) {
+ resizeSplits(position);
+ t.setBounds(mTiles.mPrimary.token, mPrimary);
+ t.setBounds(mTiles.mSecondary.token, mSecondary);
+
+ t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
+ t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
+ getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
+ }
+
+ void calcSplitBounds(int position, @NonNull Rect outPrimary, @NonNull Rect outSecondary) {
+ int dockSide = getPrimarySplitSide();
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outPrimary,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+
+ DockedDividerUtils.calculateBoundsForPosition(position,
+ DockedDividerUtils.invertDockSide(dockSide), outSecondary, mDisplayLayout.width(),
+ mDisplayLayout.height(), mDividerSize);
+ }
+
+ Rect calcMinimizedHomeStackBounds() {
+ DividerSnapAlgorithm.SnapTarget miniMid = getMinimizedSnapAlgorithm().getMiddleTarget();
+ Rect homeBounds = new Rect();
+ DockedDividerUtils.calculateBoundsForPosition(miniMid.position,
+ DockedDividerUtils.invertDockSide(getPrimarySplitSide()), homeBounds,
+ mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+ return homeBounds;
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ void updateAdjustedBounds(int currImeTop, int startTop, int finalTop) {
+ updateAdjustedBounds(mDisplayLayout, currImeTop, startTop, finalTop, mDividerSize,
+ mDividerSizeInactive, mPrimary, mSecondary);
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ private void updateAdjustedBounds(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+ int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+ adjustForIME(dl, currImeTop, startTop, finalTop, dividerWidth, dividerWidthInactive,
+ primaryBounds, secondaryBounds);
+ }
+
+ /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */
+ private void adjustForIME(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+ int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+ if (mAdjustedPrimary == null) {
+ mAdjustedPrimary = new Rect();
+ mAdjustedSecondary = new Rect();
+ }
+
+ final Rect displayStableRect = new Rect();
+ dl.getStableBounds(displayStableRect);
+
+ final boolean showing = finalTop < startTop;
+ final float progress = ((float) (currImeTop - startTop)) / (finalTop - startTop);
+ final float dividerSquish = showing ? progress : 1.f - progress;
+ final int currDividerWidth =
+ (int) (dividerWidthInactive * dividerSquish + dividerWidth * (1.f - dividerSquish));
+
+ final int minTopStackBottom = displayStableRect.top
+ + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+ final int minImeTop = minTopStackBottom + currDividerWidth;
+
+ // Calculate an offset which shifts the stacks up by the height of the IME, but still
+ // leaves at least 30% of the top stack visible.
+ final int yOffset = Math.max(0, dl.height() - Math.max(currImeTop, minImeTop));
+
+ // TOP
+ // Reduce the offset by an additional small amount to squish the divider bar.
+ mAdjustedPrimary.set(primaryBounds);
+ mAdjustedPrimary.offset(0, -yOffset + (dividerWidth - currDividerWidth));
+
+ // BOTTOM
+ mAdjustedSecondary.set(secondaryBounds);
+ mAdjustedSecondary.offset(0, -yOffset);
+ }
+
+ static int getSmallestWidthDpForBounds(@NonNull Context context, DisplayLayout dl,
+ Rect bounds) {
+ int dividerSize = DockedDividerUtils.getDividerSize(context.getResources(),
+ DockedDividerUtils.getDividerInsets(context.getResources()));
+
+ int minWidth = Integer.MAX_VALUE;
+
+ // Go through all screen orientations and find the orientation in which the task has the
+ // smallest width.
+ Rect tmpRect = new Rect();
+ Rect rotatedDisplayRect = new Rect();
+ Rect displayRect = new Rect(0, 0, dl.width(), dl.height());
+
+ DisplayLayout tmpDL = new DisplayLayout();
+ for (int rotation = 0; rotation < 4; rotation++) {
+ tmpDL.set(dl);
+ tmpDL.rotateTo(context.getResources(), rotation);
+ DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize);
+
+ tmpRect.set(bounds);
+ DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation());
+ rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height());
+ final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect,
+ tmpDL.getOrientation());
+ final int position = DockedDividerUtils.calculatePositionForBounds(tmpRect, dockSide,
+ dividerSize);
+
+ final int snappedPosition =
+ snap.calculateNonDismissingSnapTarget(position).position;
+ DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, tmpRect,
+ tmpDL.width(), tmpDL.height(), dividerSize);
+ Rect insettedDisplay = new Rect(rotatedDisplayRect);
+ insettedDisplay.inset(tmpDL.stableInsets());
+ tmpRect.intersect(insettedDisplay);
+ minWidth = Math.min(tmpRect.width(), minWidth);
+ }
+ return (int) (minWidth / dl.density());
+ }
+
+ static DividerSnapAlgorithm initSnapAlgorithmForRotation(Context context, DisplayLayout dl,
+ int dividerSize) {
+ final Configuration config = new Configuration();
+ config.unset();
+ config.orientation = dl.getOrientation();
+ Rect tmpRect = new Rect(0, 0, dl.width(), dl.height());
+ tmpRect.inset(dl.nonDecorInsets());
+ config.windowConfiguration.setAppBounds(tmpRect);
+ tmpRect.set(0, 0, dl.width(), dl.height());
+ tmpRect.inset(dl.stableInsets());
+ config.screenWidthDp = (int) (tmpRect.width() / dl.density());
+ config.screenHeightDp = (int) (tmpRect.height() / dl.density());
+ final Context rotationContext = context.createConfigurationContext(config);
+ return new DividerSnapAlgorithm(
+ rotationContext.getResources(), dl.width(), dl.height(), dividerSize,
+ config.orientation == ORIENTATION_PORTRAIT, dl.stableInsets());
+ }
+
+ /**
+ * Get the current primary-split side. Determined by its location of {@param bounds} within
+ * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+ * if allowed in its respected {@param orientation}.
+ *
+ * @param bounds bounds of the primary split task to get which side is docked
+ * @param displayRect bounds of the display that contains the primary split task
+ * @param orientation the origination of device
+ * @return current primary-split side
+ */
+ static int getPrimarySplitSide(Rect bounds, Rect displayRect, int orientation) {
+ if (orientation == ORIENTATION_PORTRAIT) {
+ // Portrait mode, docked either at the top or the bottom.
+ final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+ if (diff < 0) {
+ return DOCKED_BOTTOM;
+ } else {
+ // Top is default
+ return DOCKED_TOP;
+ }
+ } else if (orientation == ORIENTATION_LANDSCAPE) {
+ // Landscape mode, docked either on the left or on the right.
+ final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+ if (diff < 0) {
+ return DOCKED_RIGHT;
+ }
+ return DOCKED_LEFT;
+ }
+ return DOCKED_INVALID;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
new file mode 100644
index 000000000000..0a54d2f7d8c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskOrganizerController;
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pools;
+import android.view.Display;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
+ private static final String TAG = "SplitScreenTaskOrganizer";
+ private static final boolean DEBUG = Divider.DEBUG;
+
+ RunningTaskInfo mPrimary;
+ RunningTaskInfo mSecondary;
+ SurfaceControl mPrimarySurface;
+ SurfaceControl mSecondarySurface;
+ SurfaceControl mPrimaryDim;
+ SurfaceControl mSecondaryDim;
+ final Divider mDivider;
+
+ private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
+ new Pools.SynchronizedPool<>(4);
+
+ SplitScreenTaskOrganizer(Divider divider) {
+ mDivider = divider;
+ }
+
+ void init(ITaskOrganizerController organizerController, SurfaceSession session)
+ throws RemoteException {
+ organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mPrimary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mSecondary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ mPrimarySurface = mPrimary.token.getLeash();
+ mSecondarySurface = mSecondary.token.getLeash();
+
+ // Initialize dim surfaces:
+ mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface)
+ .setColorLayer().setName("Primary Divider Dim").build();
+ mSecondaryDim = new SurfaceControl.Builder(session).setParent(mSecondarySurface)
+ .setColorLayer().setName("Secondary Divider Dim").build();
+ SurfaceControl.Transaction t = getTransaction();
+ t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
+ t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
+ t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
+ t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+ t.apply();
+ releaseTransaction(t);
+ }
+
+ SurfaceControl.Transaction getTransaction() {
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (t == null) {
+ return new SurfaceControl.Transaction();
+ }
+ return t;
+ }
+
+ void releaseTransaction(SurfaceControl.Transaction t) {
+ mTransactionPool.release(t);
+ }
+
+ @Override
+ public void taskAppeared(RunningTaskInfo taskInfo) {
+ }
+
+ @Override
+ public void taskVanished(IWindowContainer container) {
+ }
+
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ if (taskInfo.displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mDivider.getHandler().post(() -> handleTaskInfoChanged(taskInfo));
+ }
+
+ /**
+ * This is effectively a finite state machine which moves between the various split-screen
+ * presentations based on the contents of the split regions.
+ */
+ private void handleTaskInfoChanged(RunningTaskInfo info) {
+ final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ if (info.token.asBinder() == mPrimary.token.asBinder()) {
+ mPrimary = info;
+ } else if (info.token.asBinder() == mSecondary.token.asBinder()) {
+ mSecondary = info;
+ }
+ final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+ if (DEBUG) {
+ Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary);
+ }
+ if (primaryIsEmpty || secondaryIsEmpty) {
+ // At-least one of the splits is empty which means we are currently transitioning
+ // into or out-of split-screen mode.
+ if (DEBUG) {
+ Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
+ + " " + mSecondary.topActivityType);
+ }
+ if (mDivider.inSplitMode()) {
+ // Was in split-mode, which means we are leaving split, so continue that.
+ // This happens when the stack in the primary-split is dismissed.
+ if (DEBUG) {
+ Log.d(TAG, " was in split, so this means leave it "
+ + mPrimary.topActivityType + " " + mSecondary.topActivityType);
+ }
+ WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */);
+ mDivider.updateVisibility(false /* visible */);
+ } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
+ // Wasn't in split-mode (both were empty), but now that the primary split is
+ // populated, we should fully enter split by moving everything else into secondary.
+ // This just tells window-manager to reparent things, the UI will respond
+ // when it gets new task info for the secondary split.
+ if (DEBUG) {
+ Log.d(TAG, " was not in split, but primary is populated, so enter it");
+ }
+ mDivider.startEnterSplit();
+ }
+ } else if (mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+ || mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS) {
+ // Both splits are populated but the secondary split has a home/recents stack on top,
+ // so enter minimized mode.
+ mDivider.ensureMinimizedSplit();
+ } else {
+ // Both splits are populated by normal activities, so make sure we aren't minimized.
+ mDivider.ensureNormalSplit();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 228aab5e3eec..76857337f73e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,16 +16,25 @@
package com.android.systemui.stackdivider;
-import static android.view.WindowManager.DOCKED_INVALID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Display;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -35,88 +44,20 @@ import java.util.concurrent.Executors;
public class WindowManagerProxy {
private static final String TAG = "WindowManagerProxy";
+ private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
private static final WindowManagerProxy sInstance = new WindowManagerProxy();
@GuardedBy("mDockedRect")
private final Rect mDockedRect = new Rect();
- private final Rect mTempDockedTaskRect = new Rect();
- private final Rect mTempDockedInsetRect = new Rect();
- private final Rect mTempOtherTaskRect = new Rect();
- private final Rect mTempOtherInsetRect = new Rect();
private final Rect mTmpRect1 = new Rect();
- private final Rect mTmpRect2 = new Rect();
- private final Rect mTmpRect3 = new Rect();
- private final Rect mTmpRect4 = new Rect();
- private final Rect mTmpRect5 = new Rect();
@GuardedBy("mDockedRect")
private final Rect mTouchableRegion = new Rect();
- private boolean mDimLayerVisible;
- private int mDimLayerTargetWindowingMode;
- private float mDimLayerAlpha;
-
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
- private final Runnable mResizeRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (mDockedRect) {
- mTmpRect1.set(mDockedRect);
- mTmpRect2.set(mTempDockedTaskRect);
- mTmpRect3.set(mTempDockedInsetRect);
- mTmpRect4.set(mTempOtherTaskRect);
- mTmpRect5.set(mTempOtherInsetRect);
- }
- try {
- ActivityTaskManager.getService()
- .resizeDockedStack(mTmpRect1,
- mTmpRect2.isEmpty() ? null : mTmpRect2,
- mTmpRect3.isEmpty() ? null : mTmpRect3,
- mTmpRect4.isEmpty() ? null : mTmpRect4,
- mTmpRect5.isEmpty() ? null : mTmpRect5);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mDismissRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityTaskManager.getService().dismissSplitScreenMode(false /* onTop */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to remove stack: " + e);
- }
- }
- };
-
- private final Runnable mMaximizeRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- ActivityTaskManager.getService().dismissSplitScreenMode(true /* onTop */);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
- private final Runnable mDimLayerRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
- mDimLayerTargetWindowingMode, mDimLayerAlpha);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to resize stack: " + e);
- }
- }
- };
-
private final Runnable mSetTouchableRegionRunnable = new Runnable() {
@Override
public void run() {
@@ -139,40 +80,9 @@ public class WindowManagerProxy {
return sInstance;
}
- public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect,
- Rect tempOtherTaskRect, Rect tempOtherInsetRect) {
- synchronized (mDockedRect) {
- mDockedRect.set(docked);
- if (tempDockedTaskRect != null) {
- mTempDockedTaskRect.set(tempDockedTaskRect);
- } else {
- mTempDockedTaskRect.setEmpty();
- }
- if (tempDockedInsetRect != null) {
- mTempDockedInsetRect.set(tempDockedInsetRect);
- } else {
- mTempDockedInsetRect.setEmpty();
- }
- if (tempOtherTaskRect != null) {
- mTempOtherTaskRect.set(tempOtherTaskRect);
- } else {
- mTempOtherTaskRect.setEmpty();
- }
- if (tempOtherInsetRect != null) {
- mTempOtherInsetRect.set(tempOtherInsetRect);
- } else {
- mTempOtherInsetRect.setEmpty();
- }
- }
- mExecutor.execute(mResizeRunnable);
- }
-
- public void dismissDockedStack() {
- mExecutor.execute(mDismissRunnable);
- }
-
- public void maximizeDockedStack() {
- mExecutor.execute(mMaximizeRunnable);
+ void dismissOrMaximizeDocked(
+ final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
+ mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
}
public void setResizing(final boolean resizing) {
@@ -188,26 +98,204 @@ public class WindowManagerProxy {
});
}
- public int getDockSide() {
+ /** Sets a touch region */
+ public void setTouchRegion(Rect region) {
+ synchronized (mDockedRect) {
+ mTouchableRegion.set(region);
+ }
+ mExecutor.execute(mSetTouchableRegionRunnable);
+ }
+
+ static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ splitLayout.resizeSplits(position, t);
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(t,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private static boolean getHomeAndRecentsTasks(List<IWindowContainer> out,
+ IWindowContainer parent) {
+ boolean resizable = false;
try {
- return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
+ List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
+ ? ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+ Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
+ : ActivityTaskManager.getTaskOrganizerController().getChildTasks(parent,
+ HOME_AND_RECENTS);
+ for (int i = 0, n = rootTasks.size(); i < n; ++i) {
+ final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
+ out.add(ti.token);
+ if (ti.topActivityType == ACTIVITY_TYPE_HOME) {
+ resizable = ti.isResizable();
+ }
+ }
} catch (RemoteException e) {
- Log.w(TAG, "Failed to get dock side: " + e);
}
- return DOCKED_INVALID;
+ return resizable;
}
- public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
- mDimLayerVisible = visible;
- mDimLayerTargetWindowingMode = targetWindowingMode;
- mDimLayerAlpha = alpha;
- mExecutor.execute(mDimLayerRunnable);
+ static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) {
+ applyHomeTasksMinimized(layout, parent, null /* transaction */);
}
- public void setTouchRegion(Rect region) {
- synchronized (mDockedRect) {
- mTouchableRegion.set(region);
+ /**
+ * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary
+ * split is minimized. This actually "sticks out" of the secondary split area, but when in
+ * minimized mode, the secondary split gets a 'negative' crop to expose it.
+ */
+ static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent,
+ WindowContainerTransaction t) {
+ // Resize the home/recents stacks to the larger minimized-state size
+ final Rect homeBounds;
+ final ArrayList<IWindowContainer> homeStacks = new ArrayList<>();
+ boolean isHomeResizable = getHomeAndRecentsTasks(homeStacks, parent);
+ if (isHomeResizable) {
+ homeBounds = layout.calcMinimizedHomeStackBounds();
+ } else {
+ homeBounds = new Rect(0, 0, layout.mDisplayLayout.width(),
+ layout.mDisplayLayout.height());
+ }
+ WindowContainerTransaction wct = t != null ? t : new WindowContainerTransaction();
+ for (int i = homeStacks.size() - 1; i >= 0; --i) {
+ wct.setBounds(homeStacks.get(i), homeBounds);
+ }
+ if (t != null) {
+ return isHomeResizable;
+ }
+ try {
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to resize home stacks ", e);
+ }
+ return isHomeResizable;
+ }
+
+ /**
+ * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split.
+ * This assumes there is already something in the primary split since that is usually what
+ * triggers a call to this. In the same transaction, this overrides the home task bounds via
+ * {@link #applyHomeTasksMinimized}.
+ *
+ * @return whether the home stack is resizable
+ */
+ static boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
+ try {
+ // Set launchtile first so that any stack created after
+ // getAllStackInfos and before reparent (even if unlikely) are placed
+ // correctly.
+ ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(
+ DEFAULT_DISPLAY, tiles.mSecondary.token);
+ List<ActivityManager.RunningTaskInfo> rootTasks =
+ ActivityTaskManager.getTaskOrganizerController().getRootTasks(DEFAULT_DISPLAY,
+ null /* activityTypes */);
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (rootTasks.isEmpty()) {
+ return false;
+ }
+ for (int i = rootTasks.size() - 1; i >= 0; --i) {
+ if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_FULLSCREEN) {
+ continue;
+ }
+ wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
+ true /* onTop */);
+ }
+ boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
+ ActivityTaskManager.getTaskOrganizerController()
+ .applyContainerTransaction(wct, null /* organizer */);
+ return isHomeResizable;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error moving fullscreen tasks to secondary split: " + e);
+ }
+ return false;
+ }
+
+ private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
+ final int atype = ti.configuration.windowConfiguration.getActivityType();
+ return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
+ }
+
+ /**
+ * Reparents all tile members back to their display and resets home task override bounds.
+ * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary
+ * split (thus resulting in the top of the secondary split becoming
+ * fullscreen. {@code false} resolves the other way.
+ */
+ static void applyDismissSplit(SplitScreenTaskOrganizer tiles, boolean dismissOrMaximize) {
+ try {
+ // Set launch root first so that any task created after getChildContainers and
+ // before reparent (pretty unlikely) are put into fullscreen.
+ ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(Display.DEFAULT_DISPLAY,
+ null);
+ // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
+ // plus specific APIs to clean this up.
+ List<ActivityManager.RunningTaskInfo> primaryChildren =
+ ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+ tiles.mPrimary.token, null /* activityTypes */);
+ List<ActivityManager.RunningTaskInfo> secondaryChildren =
+ ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+ tiles.mSecondary.token, null /* activityTypes */);
+ // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
+ // as a result, the above will not capture any tasks; yet, we need to clean-up the
+ // home task bounds.
+ List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
+ ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+ Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
+ if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
+ && freeHomeAndRecents.isEmpty()) {
+ return;
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (dismissOrMaximize) {
+ // Dismissing, so move all primary split tasks first
+ for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+ wct.reparent(primaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ // Don't need to worry about home tasks because they are already in the "proper"
+ // order within the secondary split.
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+ wct.reparent(ti.token, null /* parent */, true /* onTop */);
+ if (isHomeOrRecentTask(ti)) {
+ wct.setBounds(ti.token, null);
+ }
+ }
+ } else {
+ // Maximize, so move non-home secondary split first
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ if (isHomeOrRecentTask(secondaryChildren.get(i))) {
+ continue;
+ }
+ wct.reparent(secondaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ // Find and place home tasks in-between. This simulates the fact that there was
+ // nothing behind the primary split's tasks.
+ for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+ final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+ if (isHomeOrRecentTask(ti)) {
+ wct.reparent(ti.token, null /* parent */, true /* onTop */);
+ // reset bounds too
+ wct.setBounds(ti.token, null);
+ }
+ }
+ for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+ wct.reparent(primaryChildren.get(i).token, null /* parent */,
+ true /* onTop */);
+ }
+ }
+ for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
+ wct.setBounds(freeHomeAndRecents.get(i).token, null);
+ }
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+ null /* organizer */);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to remove stack: " + e);
}
- mExecutor.execute(mSetTouchableRegionRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 93f58053f486..55a20fae4ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,10 +51,10 @@ import android.util.Pair;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
@@ -80,11 +80,13 @@ public class InstantAppNotifier extends SystemUI
private final CommandQueue mCommandQueue;
private boolean mDockedStackExists;
private KeyguardStateController mKeyguardStateController;
+ private final Divider mDivider;
@Inject
public InstantAppNotifier(Context context, CommandQueue commandQueue,
- @UiBackground Executor uiBgExecutor) {
+ @UiBackground Executor uiBgExecutor, Divider divider) {
super(context);
+ mDivider = divider;
mCommandQueue = commandQueue;
mUiBgExecutor = uiBgExecutor;
}
@@ -103,7 +105,7 @@ public class InstantAppNotifier extends SystemUI
mCommandQueue.addCallback(this);
mKeyguardStateController.addCallback(this);
- DockedStackExistsListener.register(
+ mDivider.registerInSplitScreenListener(
exists -> {
mDockedStackExists = exists;
updateForegroundInstantApps();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ba9ba6c9c702..18777bce8679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -63,7 +63,6 @@ import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistHandleViewController;
@@ -75,6 +74,7 @@ import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.policy.DeadZone;
@@ -862,7 +862,8 @@ public class NavigationBarView extends FrameLayout implements
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
- DockedStackExistsListener.register(mDockedListener);
+ Divider divider = Dependency.get(Divider.class);
+ divider.registerInSplitScreenListener(mDockedListener);
updateOrientationViews();
reloadNavIcons();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 823adff6cb91..419d1364f109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -163,7 +163,6 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.stackdivider.WindowManagerProxy;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -1409,8 +1408,11 @@ public class StatusBar extends SystemUI implements DemoMode,
if (!mRecentsOptional.isPresent()) {
return false;
}
- int dockSide = WindowManagerProxy.getInstance().getDockSide();
- if (dockSide == WindowManager.DOCKED_INVALID) {
+ Divider divider = null;
+ if (mDividerOptional.isPresent()) {
+ divider = mDividerOptional.get();
+ }
+ if (divider == null || !divider.inSplitMode()) {
final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
@@ -1420,16 +1422,13 @@ public class StatusBar extends SystemUI implements DemoMode,
: SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
} else {
- if (mDividerOptional.isPresent()) {
- Divider divider = mDividerOptional.get();
- if (divider.isMinimized() && !divider.isHomeStackResizable()) {
- // Undocking from the minimized state is not supported
- return false;
- } else {
- divider.onUndockingTask();
- if (metricsUndockAction != -1) {
- mMetricsLogger.action(metricsUndockAction);
- }
+ if (divider.isMinimized() && !divider.isHomeStackResizable()) {
+ // Undocking from the minimized state is not supported
+ return false;
+ } else {
+ divider.onUndockingTask();
+ if (metricsUndockAction != -1) {
+ mMetricsLogger.action(metricsUndockAction);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 7dad05df8f2c..9227cf0395d3 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -48,8 +48,8 @@ import javax.inject.Singleton;
public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayImeController";
- static final int ANIMATION_DURATION_SHOW_MS = 275;
- static final int ANIMATION_DURATION_HIDE_MS = 340;
+ public static final int ANIMATION_DURATION_SHOW_MS = 275;
+ public static final int ANIMATION_DURATION_HIDE_MS = 340;
static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
private static final int DIRECTION_NONE = 0;
private static final int DIRECTION_SHOW = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index 64b0b66a47a9..4652abfa0721 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -35,6 +35,7 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.util.DisplayMetrics;
import android.util.RotationUtils;
import android.util.Size;
import android.view.Display;
@@ -191,6 +192,11 @@ public class DisplayLayout {
return mDensityDpi;
}
+ /** Get the density scale for the display. */
+ public float density() {
+ return mDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ }
+
/** Get whether this layout is landscape. */
public boolean isLandscape() {
return mWidth > mHeight;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ab8e975098a2..3c39b39c4a9d 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -111,6 +111,7 @@ import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT;
import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
import static com.android.server.wm.TaskProto.BOUNDS;
+import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
@@ -731,53 +732,14 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
newBounds);
hasNewOverrideBounds = true;
}
-
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- // If entering split screen or if something about the available split area changes,
- // recalculate the split windows to match the new configuration.
- if (rotationChanged || windowingModeChanged
- || prevDensity != getConfiguration().densityDpi
- || prevScreenW != getConfiguration().screenWidthDp
- || prevScreenH != getConfiguration().screenHeightDp) {
- calculateDockedBoundsForConfigChange(newParentConfig, newBounds);
- hasNewOverrideBounds = true;
- }
- }
}
if (windowingModeChanged) {
- // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
- if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- // immediately resize so docked bounds are available in onSplitScreenModeActivated
- setTaskDisplayedBounds(null);
- setTaskBounds(newBounds);
- setBounds(newBounds);
- newBounds.set(newBounds);
- } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- Rect dockedBounds = display.getRootSplitScreenPrimaryTask().getBounds();
- final boolean isMinimizedDock =
- display.mDisplayContent.getDockedDividerController().isMinimizedDock();
- if (isMinimizedDock) {
- Task topTask = display.getRootSplitScreenPrimaryTask().getTopMostTask();
- if (topTask != null) {
- dockedBounds = topTask.getBounds();
- }
- }
- getStackDockedModeBounds(dockedBounds, null /* currentTempTaskBounds */,
- newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- hasNewOverrideBounds = true;
- }
+ display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
- if (inSplitScreenPrimaryWindowingMode()) {
- mStackSupervisor.resizeDockedStackLocked(new Rect(newBounds),
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
+ if (inSplitScreenWindowingMode()) {
+ setBounds(newBounds);
} else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
// For pinned stack, resize is now part of the {@link WindowContainerTransaction}
resize(new Rect(newBounds), null /* tempTaskBounds */,
@@ -920,11 +882,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
// warning toast about it.
mAtmService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- final ActivityStack primarySplitStack = display.getRootSplitScreenPrimaryTask();
- primarySplitStack.setWindowingModeInSurfaceTransaction(WINDOWING_MODE_UNDEFINED,
- false /* animate */, false /* showRecents */,
- false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
- primarySplitStack == this ? creating : false);
+ display.onSplitScreenModeDismissed();
}
}
@@ -1218,7 +1176,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullScreenStack != null) {
final ActivityStack primarySplitScreenStack = display.getRootSplitScreenPrimaryTask();
- if (display.getIndexOf(topFullScreenStack)
+ if (primarySplitScreenStack != null && display.getIndexOf(topFullScreenStack)
> display.getIndexOf(primarySplitScreenStack)) {
primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
}
@@ -3994,17 +3952,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
? ((WindowContainer) oldParent).getDisplayContent() : null;
super.onParentChanged(newParent, oldParent);
- if (display != null && inSplitScreenPrimaryWindowingMode()
- // only do this for the base stack
- && !newParent.inSplitScreenPrimaryWindowingMode()) {
- // If we created a docked stack we want to resize it so it resizes all other stacks
- // in the system.
- getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
- mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
- mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
- mTmpRect2, null, null, PRESERVE_WINDOWS);
- }
-
// Resume next focusable stack after reparenting to another display if we aren't removing
// the prevous display.
if (oldDisplay != null && oldDisplay.isRemoving()) {
@@ -4950,6 +4897,12 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
}
@Override
+ public SurfaceControl getParentSurfaceControl() {
+ // Tile is a "virtual" parent, so we need to intercept the parent surface here
+ return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
+ }
+
+ @Override
void removeImmediately() {
// TODO(task-hierarchy): remove this override when tiles are in hierarchy
if (mTile != null) {
@@ -5002,6 +4955,10 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
if (!matchParentBounds()) {
final Rect bounds = getRequestedOverrideBounds();
bounds.dumpDebug(proto, BOUNDS);
+ } else if (getStack().getTile() != null) {
+ // use tile's bounds here for cts.
+ final Rect bounds = getStack().getTile().getRequestedOverrideBounds();
+ bounds.dumpDebug(proto, BOUNDS);
}
getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS);
@@ -5021,6 +4978,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget {
proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
}
+ proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+
proto.end(token);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a582f2159dfc..a2e880171458 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2447,7 +2447,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
+ dockedStack.getDisplay().onSplitScreenModeDismissed();
+ dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
}
return;
}
@@ -2809,7 +2811,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final DisplayContent display = task.getStack().getDisplay();
final ActivityStack topSecondaryStack =
display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (topSecondaryStack.isActivityTypeHome()) {
+ if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) {
// If the home activity is the top split-screen secondary stack, then the
// primary split-screen stack is in the minimized mode which means it can't
// receive input keys, so we should move the focused app to the home app so that
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f2917c54c3fa..f777e909510d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -37,7 +37,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -229,6 +228,7 @@ import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowContainerTransaction;
import android.view.WindowManager;
import com.android.internal.R;
@@ -2269,6 +2269,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
+ if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
+ }
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_TASK_IN_STACKS_ONLY);
if (task == null) {
@@ -2286,10 +2289,16 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final ActivityStack stack = task.getStack();
+ // Convert some windowing-mode changes into root-task reparents for split-screen.
+ if (stack.getTile() != null) {
+ stack.getDisplay().onSplitScreenModeDismissed();
+ }
if (toTop) {
stack.moveToFront("setTaskWindowingMode", task);
}
stack.setWindowingMode(windowingMode);
+ stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+ true /* notifyClients */);
return true;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2719,36 +2728,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- if (isInLockTaskMode()) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: Is in lock task mode="
- + getLockTaskModeState());
- return false;
- }
-
- final Task task = mRootWindowContainer.anyTaskForId(taskId,
- MATCH_TASK_IN_STACKS_ONLY);
- if (task == null) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
- return false;
- }
- if (!task.isActivityTypeStandardOrUndefined()) {
- throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
- + " non-standard task " + taskId + " to split-screen windowing mode");
- }
-
- if (DEBUG_STACK) Slog.d(TAG_STACK,
- "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
- + " to createMode=" + createMode + " toTop=" + toTop);
- mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
- final int windowingMode = task.getWindowingMode();
- final ActivityStack stack = task.getStack();
- if (toTop) {
- stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
- }
- stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
- false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
- false /* creating */);
- return windowingMode != task.getWindowingMode();
+ return setTaskWindowingModeSplitScreen(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ toTop);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2756,6 +2737,49 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
+ * Moves the specified task into a split-screen tile.
+ */
+ private boolean setTaskWindowingModeSplitScreen(int taskId, int windowingMode, boolean toTop) {
+ if (!WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+ throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non"
+ + "split-screen mode: " + windowingMode);
+ }
+ if (isInLockTaskMode()) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode="
+ + getLockTaskModeState());
+ return false;
+ }
+
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_TASK_IN_STACKS_ONLY);
+ if (task == null) {
+ Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+ return false;
+ }
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+ + " non-standard task " + taskId + " to split-screen windowing mode");
+ }
+
+ final int prevMode = task.getWindowingMode();
+ final ActivityStack stack = task.getStack();
+ TaskTile tile = null;
+ for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
+ tile = stack.getDisplay().getStackAt(i).asTile();
+ if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ break;
+ }
+ }
+ if (tile == null) {
+ throw new IllegalStateException("Can't enter split without associated tile");
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+ mTaskOrganizerController.applyContainerTransaction(wct, null);
+ return prevMode != task.getWindowingMode();
+ }
+
+ /**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@@ -3963,46 +3987,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
/**
- * Dismisses split-screen multi-window mode.
- * @param toTop If true the current primary split-screen stack will be placed or left on top.
- */
- @Override
- public void dismissSplitScreenMode(boolean toTop) {
- enforceCallerIsRecentsOrHasPermission(
- MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityStack stack =
- mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
- if (stack == null) {
- Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
- return;
- }
-
- if (toTop) {
- // Caller wants the current split-screen primary stack to be the top stack after
- // it goes fullscreen, so move it to the front.
- stack.moveToFront("dismissSplitScreenMode");
- } else {
- // In this case the current split-screen primary stack shouldn't be the top
- // stack after it goes fullscreen, so we move the focus to the top-most
- // split-screen secondary stack next to it.
- final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- if (otherStack != null) {
- otherStack.moveToFront("dismissSplitScreenMode_other");
- }
- }
-
- stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
* Dismisses Pip
* @param animate True if the dismissal should be animated.
* @param animationDuration The duration of the resize animation in milliseconds or -1 if the
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b0c6c64558e6..55a41ab3d79a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -31,6 +30,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -55,9 +55,6 @@ import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_TOP;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -2626,54 +2623,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void adjustForImeIfNeeded() {
final WindowState imeWin = mInputMethodWindow;
- final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
- && !mDividerControllerLocked.isImeHideRequested();
- final ActivityStack dockedStack = getRootSplitScreenPrimaryTask();
- final boolean dockVisible = dockedStack != null;
- final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
- final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
- final int imeDockSide = (dockVisible && imeTargetStack != null) ?
- imeTargetStack.getDockSide() : DOCKED_INVALID;
- final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
- final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
+ final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
+ && imeWin.isDisplayedLw();
final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
- final boolean imeHeightChanged = imeVisible &&
- imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
-
- // This includes a case where the docked stack is unminimizing and IME is visible for the
- // bottom side stack. The condition prevents adjusting the override task bounds for IME to
- // the minimized docked stack bounds.
- final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
- || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
- && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
-
- // The divider could be adjusted for IME position, or be thinner than usual,
- // or both. There are three possible cases:
- // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
- // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner.
- // - If IME is not visible, divider is not moved and is normal width.
-
- if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
- if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
- && stack.inSplitScreenWindowingMode()) {
- stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
- } else {
- stack.resetAdjustedForIme(false);
- }
- }
- mDividerControllerLocked.setAdjustedForIme(
- imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
- } else {
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- stack.resetAdjustedForIme(!dockVisible);
- }
- mDividerControllerLocked.setAdjustedForIme(
- false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
- }
mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
}
@@ -3453,8 +3405,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mInputMethodTarget = target;
mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
- mInputMethodControlTarget = computeImeControlTarget();
- mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
updateImeParent();
}
@@ -3465,7 +3415,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*
* @see #getImeControlTarget()
*/
- void updateImeControlTarget(WindowState target) {
+ void updateImeControlTarget(InsetsControlTarget target) {
mInputMethodControlTarget = target;
mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
}
@@ -3503,13 +3453,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Computes which control-target the IME should be attached to.
*/
@VisibleForTesting
- InsetsControlTarget computeImeControlTarget() {
+ InsetsControlTarget computeImeControlTarget(InsetsControlTarget appTarget) {
if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
return mRemoteInsetsControlTarget;
}
// Otherwise, we just use the ime target
- return mInputMethodTarget;
+ return appTarget;
}
void setLayoutNeeded() {
@@ -3897,7 +3847,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+ /** @return the orientation of the display when it's rotation is ROTATION_0. */
int getNaturalOrientation() {
return mBaseDisplayWidth < mBaseDisplayHeight
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
@@ -4338,8 +4288,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
} else {
mRootSplitScreenPrimaryTask = stack;
- mDisplayContent.onSplitScreenModeActivated();
- mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
}
@@ -4351,11 +4299,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mRootPinnedTask = null;
} else if (stack == mRootSplitScreenPrimaryTask) {
mRootSplitScreenPrimaryTask = null;
- mDisplayContent.onSplitScreenModeDismissed();
- // Re-set the split-screen create mode whenever the split-screen stack is removed.
- mWmService.setDockedStackCreateStateLocked(
- SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
- mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
}
@@ -5771,6 +5714,33 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
}
+ /** @return the tile to create the next stack in. */
+ private TaskTile updateLaunchTile(int windowingMode) {
+ if (!isSplitScreenWindowingMode(windowingMode)) {
+ // Only split-screen windowing modes interact with tiles.
+ return null;
+ }
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final TaskTile t = getStackAt(i).asTile();
+ if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+ continue;
+ }
+ // If not already set, pick a launch tile which is not the one we are launching
+ // into.
+ if (mLaunchTile == null) {
+ for (int j = 0, n = getStackCount(); j < n; ++j) {
+ TaskTile tt = getStackAt(j).asTile();
+ if (tt != t) {
+ mLaunchTile = tt;
+ break;
+ }
+ }
+ }
+ return t;
+ }
+ return mLaunchTile;
+ }
+
@VisibleForTesting
ActivityStack createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop, ActivityInfo info, Intent intent) {
@@ -5783,13 +5753,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
info.applicationInfo = new ApplicationInfo();
}
+ TaskTile tile = updateLaunchTile(windowingMode);
+ if (tile != null) {
+ // Since this stack will be put into a tile, its windowingMode will be inherited.
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
final ActivityStack stack = new ActivityStack(this, stackId,
mRootWindowContainer.mStackSupervisor, activityType, info, intent);
addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
true /* creating */);
-
+ if (tile != null) {
+ tile.addChild(stack, 0 /* index */);
+ }
return stack;
}
@@ -6009,16 +5986,15 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
void onSplitScreenModeDismissed() {
mAtmService.deferWindowLayout();
try {
- // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+ mLaunchTile = null;
for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getStackAt(i);
- if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
- continue;
+ final TaskTile t = getStackAt(i).asTile();
+ if (t != null) {
+ t.removeAllChildren();
}
- otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
- false /* showRecents */, false /* enteringSplitScreenMode */,
- true /* deferEnsuringVisibility */, false /* creating */);
}
+ mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */,
+ false /* animate */);
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6036,27 +6012,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- void onSplitScreenModeActivated() {
- mAtmService.deferWindowLayout();
- try {
- // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
- final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask();
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getStackAt(i);
- if (otherStack == splitScreenPrimaryStack
- || !otherStack.affectedBySplitScreenResize()) {
- continue;
- }
- otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
- false /* animate */, false /* showRecents */,
- true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
- false /* creating */);
- }
- } finally {
- mAtmService.continueWindowLayout();
- }
- }
-
/**
* Returns true if the {@param windowingMode} is supported based on other parameters passed in.
* @param windowingMode The windowing mode we are checking support for.
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index e71371a477f7..539853bcac77 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -525,9 +525,15 @@ public class DisplayRotation {
}
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mIsWaitingForRemoteRotation = false;
- mDisplayContent.sendNewConfiguration();
- if (t != null) {
- mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+ mService.mAtmService.deferWindowLayout();
+ try {
+ mDisplayContent.sendNewConfiguration();
+ if (t != null) {
+ mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t,
+ null /* organizer */);
+ }
+ } finally {
+ mService.mAtmService.continueWindowLayout();
}
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 872379efa389..6431e117c4e6 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -745,7 +745,7 @@ public class DockedStackDividerController {
* @param minimizedDock Whether the docked stack is currently minimized.
* @param animate Whether to animate the change.
*/
- private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
+ void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
final boolean wasMinimized = mMinimizedDock;
mMinimizedDock = minimizedDock;
if (minimizedDock == wasMinimized) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 00947d766e89..44034edaa4bf 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -31,14 +31,14 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.os.IBinder;
import android.os.RemoteException;
@@ -411,8 +411,7 @@ class KeyguardController {
if (stack == null) {
return;
}
- mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
- stack.isFocusedStackOnDisplay());
+ mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 944e0ae3d73f..be68c5dc3796 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -414,12 +413,7 @@ public class RecentsAnimationController implements DeathRecipient {
}
// Save the minimized home height
- final ActivityStack dockedStack =
- mDisplayContent.getRootSplitScreenPrimaryTask();
- mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
- mDisplayContent.getConfiguration(),
- dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
- mMinimizedHomeBounds);
+ mMinimizedHomeBounds = mDisplayContent.getRootHomeTask().getBounds();
mService.mWindowPlacerLocked.performSurfacePlacement();
@@ -842,8 +836,8 @@ public class RecentsAnimationController implements DeathRecipient {
mTask = task;
mIsRecentTaskInvisible = isRecentTaskInvisible;
final WindowContainer container = mTask.getParent();
- container.getRelativeDisplayedPosition(mPosition);
mBounds.set(container.getDisplayedBounds());
+ mPosition.set(mBounds.left, mBounds.top);
}
RemoteAnimationTarget createRemoteAnimationTarget() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0aed691c36a6..9279271fb976 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2144,12 +2144,6 @@ class Task extends WindowContainer<WindowContainer> {
// For floating tasks, calculate the smallest width from the bounds of the task
inOutConfig.smallestScreenWidthDp = (int) (
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
- } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
- // Iterating across all screen orientations, and return the minimum of the task
- // width taking into account that the bounds might change because the snap
- // algorithm snaps to a different value
- inOutConfig.smallestScreenWidthDp =
- getSmallestScreenWidthDpForDockedBounds(mTmpFullBounds);
}
// otherwise, it will just inherit
}
@@ -3248,6 +3242,10 @@ class Task extends WindowContainer<WindowContainer> {
return this;
}
+ TaskTile asTile() {
+ return null;
+ }
+
// TODO(task-merge): Figure-out how this should work with hierarchy tasks.
boolean shouldBeVisible(ActivityRecord starting) {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4b13a0c1f75d..4d5621cd5b32 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -492,6 +492,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub
if (!(container instanceof Task)) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
+ if (container.getDisplayContent() == null) {
+ Slog.w(TAG, "Container is no longer attached: " + container);
+ return 0;
+ }
if (hop.isReparent()) {
// special case for tiles since they are "virtual" parents
if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 369db05452f7..74d5c338a68d 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -31,7 +31,6 @@ import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
-import android.view.SurfaceControl;
import java.util.ArrayList;
import java.util.Comparator;
@@ -78,30 +77,9 @@ public class TaskTile extends ActivityStack {
// Virtual parent, so don't notify children.
}
- /**
- * If there is a disconnection, this will clean up any vestigial surfaces left on the tile
- * leash by moving known children to a new surfacecontrol and then removing the old one.
- */
- void cleanupSurfaces() {
- if (mSurfaceControl == null) {
- return;
- }
- SurfaceControl oldSurface = mSurfaceControl;
- WindowContainer parentWin = getParent();
- if (parentWin == null) {
- return;
- }
- mSurfaceControl = parentWin.makeChildSurface(null).setName("TaskTile " + mTaskId + " - "
- + getRequestedOverrideWindowingMode()).setContainerLayer().build();
- SurfaceControl.Transaction t = parentWin.getPendingTransaction();
- t.show(mSurfaceControl);
- for (int i = 0; i < mChildren.size(); ++i) {
- if (mChildren.get(i).getSurfaceControl() == null) {
- continue;
- }
- mChildren.get(i).reparentSurfaceControl(t, mSurfaceControl);
- }
- t.remove(oldSurface);
+ @Override
+ TaskTile asTile() {
+ return this;
}
@Override
@@ -215,6 +193,12 @@ public class TaskTile extends ActivityStack {
super.removeImmediately();
}
+ @Override
+ void taskOrganizerDied() {
+ super.taskOrganizerDied();
+ removeImmediately();
+ }
+
static TaskTile forToken(IBinder token) {
try {
return (TaskTile) ((TaskToken) token).getContainer();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2bb67035f44b..1b0d177cab87 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7320,7 +7320,9 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mGlobalLock) {
final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
if (imeTarget != null) {
- imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+ InsetsControlTarget controlTarget =
+ imeTarget.getDisplayContent().computeImeControlTarget(imeTarget);
+ imeTarget.getDisplayContent().updateImeControlTarget(controlTarget);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 46081c5dde92..ed4e6842906d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2662,18 +2662,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mWmService.mTaskSnapshotController.onAppDied(win.mActivityRecord);
}
win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
- if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
- // The owner of the docked divider died :( We reset the docked stack,
- // just in case they have the divider at an unstable position. Better
- // also reset drag resizing state, because the owner can't do it
- // anymore.
- final ActivityStack stack =
- dc.getRootSplitScreenPrimaryTask();
- if (stack != null) {
- stack.resetDockedStackToMiddle();
- }
- resetSplitScreenResizing = true;
- }
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
WindowState.this.removeIfPossible();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index fa182d68d917..d46975cc6552 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -29,6 +29,7 @@ import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_SWITCHES_CANCELED;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -64,8 +65,10 @@ import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -80,6 +83,9 @@ import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.view.Gravity;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -999,7 +1005,8 @@ public class ActivityStarterTests extends ActivityTestsBase {
assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
// Move activity to split-screen-primary stack and make sure it has the focus.
- top.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
+ splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
// Activity must landed on split-screen-secondary when launch adjacent.
@@ -1022,4 +1029,58 @@ public class ActivityStarterTests extends ActivityTestsBase {
verify(recentTasks, times(1)).add(any());
}
+
+ static class TestSplitOrganizer extends ITaskOrganizer.Stub {
+ final ActivityTaskManagerService mService;
+ TaskTile mPrimary;
+ TaskTile mSecondary;
+ boolean mInSplit = false;
+ int mDisplayId;
+ TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
+ mService = service;
+ mDisplayId = displayId;
+ mService.mTaskOrganizerController.registerTaskOrganizer(this,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ mService.mTaskOrganizerController.registerTaskOrganizer(this,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
+ mPrimary = TaskTile.forToken(primary.asBinder());
+ IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
+ displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
+ mSecondary = TaskTile.forToken(secondary.asBinder());
+ }
+ @Override
+ public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+ }
+ @Override
+ public void taskVanished(IWindowContainer wc) {
+ }
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) {
+ }
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ if (mInSplit) {
+ return;
+ }
+ if (info.topActivityType != ACTIVITY_TYPE_UNDEFINED) {
+ if (info.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ mInSplit = true;
+ mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
+ mSecondary.mRemoteToken);
+ // move everything to secondary because test expects this but usually sysui
+ // does it.
+ DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ if (!WindowConfiguration.isSplitScreenWindowingMode(
+ dc.getStackAt(i).getWindowingMode())) {
+ mSecondary.addChild(dc.getStackAt(i), 0);
+ }
+ }
+ }
+ }
+ }
+ };
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index a672a95dcc12..444906977f47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1098,7 +1098,6 @@ public class RecentTasksTest extends ActivityTestsBase {
assertSecurityException(expectCallable,
() -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
() -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 0312df6d34fc..cd53eced948f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -28,19 +28,17 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -49,6 +47,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.PictureInPictureParams;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -56,7 +55,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
-import android.content.pm.ActivityInfo;
import android.util.Rational;
import android.view.Display;
import android.view.ITaskOrganizer;
@@ -458,9 +456,9 @@ public class TaskOrganizerTests extends WindowTestsBase {
private List<TaskTile> getTaskTiles(DisplayContent dc) {
ArrayList<TaskTile> out = new ArrayList<>();
for (int i = dc.getStackCount() - 1; i >= 0; --i) {
- final Task t = dc.getStackAt(i);
- if (t instanceof TaskTile) {
- out.add((TaskTile) t);
+ final TaskTile t = dc.getStackAt(i).asTile();
+ if (t != null) {
+ out.add(t);
}
}
return out;