Re-parent children when Organized Window is no longer controlled
When an Organized Window is no longer controlled by a specific client, we create a new SurfaceControl and reparent the children so the client no longer has control over the WindowContainer Test: Pip, TaskOrganizerMultiWindowTest Bug: 154558563 Change-Id: I76e0da115fd48f982a82bd6e2351e7171934acfc
This commit is contained in:
@@ -36,7 +36,6 @@ import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.WindowContainerToken;
|
||||
@@ -94,7 +93,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
private boolean mHomeStackResizable = false;
|
||||
private ForcedResizableInfoActivityController mForcedResizableController;
|
||||
private SystemWindows mSystemWindows;
|
||||
final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
private DisplayController mDisplayController;
|
||||
private DisplayImeController mImeController;
|
||||
final TransactionPool mTransactionPool;
|
||||
@@ -493,7 +491,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSplits.init(mSurfaceSession);
|
||||
mSplits.init();
|
||||
// Set starting tile bounds based on middle target
|
||||
final WindowContainerTransaction tct = new WindowContainerTransaction();
|
||||
int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
|
||||
@@ -505,7 +503,6 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
return;
|
||||
}
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(mActivityRestartListener);
|
||||
update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -582,6 +579,15 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
|
||||
}
|
||||
}
|
||||
|
||||
void onTaskVanished() {
|
||||
mHandler.post(this::removeDivider);
|
||||
}
|
||||
|
||||
void onTasksReady() {
|
||||
mHandler.post(() -> update(mDisplayController.getDisplayContext(
|
||||
mContext.getDisplayId()).getResources().getConfiguration()));
|
||||
}
|
||||
|
||||
void updateVisibility(final boolean visible) {
|
||||
if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
|
||||
if (mVisible != visible) {
|
||||
|
||||
@@ -47,39 +47,27 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
|
||||
final Divider mDivider;
|
||||
private boolean mSplitScreenSupported = false;
|
||||
|
||||
final SurfaceSession mSurfaceSession = new SurfaceSession();
|
||||
|
||||
SplitScreenTaskOrganizer(Divider divider) {
|
||||
mDivider = divider;
|
||||
}
|
||||
|
||||
void init(SurfaceSession session) throws RemoteException {
|
||||
void init() throws RemoteException {
|
||||
registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
|
||||
registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
|
||||
try {
|
||||
mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
|
||||
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
|
||||
mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
|
||||
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
|
||||
mPrimarySurface = mPrimary.token.getLeash();
|
||||
mSecondarySurface = mSecondary.token.getLeash();
|
||||
} catch (Exception e) {
|
||||
// teardown to prevent callbacks
|
||||
unregisterOrganizer();
|
||||
throw e;
|
||||
synchronized (this) {
|
||||
try {
|
||||
mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
|
||||
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
|
||||
mSecondary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
|
||||
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
|
||||
} catch (Exception e) {
|
||||
// teardown to prevent callbacks
|
||||
unregisterOrganizer();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
mSplitScreenSupported = true;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
boolean isSplitScreenSupported() {
|
||||
@@ -94,6 +82,67 @@ class SplitScreenTaskOrganizer extends TaskOrganizer {
|
||||
mDivider.mTransactionPool.release(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo taskInfo) {
|
||||
synchronized (this) {
|
||||
if (mPrimary == null || mSecondary == null) {
|
||||
Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (taskInfo.token.equals(mPrimary.token)) {
|
||||
mPrimarySurface = taskInfo.token.getLeash();
|
||||
} else if (taskInfo.token.equals(mSecondary.token)) {
|
||||
mSecondarySurface = taskInfo.token.getLeash();
|
||||
}
|
||||
|
||||
if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) {
|
||||
mSplitScreenSupported = true;
|
||||
|
||||
// Initialize dim surfaces:
|
||||
mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession)
|
||||
.setParent(mPrimarySurface).setColorLayer()
|
||||
.setName("Primary Divider Dim").build();
|
||||
mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession)
|
||||
.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);
|
||||
|
||||
mDivider.onTasksReady();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskVanished(RunningTaskInfo taskInfo) {
|
||||
synchronized (this) {
|
||||
final boolean isPrimaryTask = mPrimary != null
|
||||
&& taskInfo.token.equals(mPrimary.token);
|
||||
final boolean isSecondaryTask = mSecondary != null
|
||||
&& taskInfo.token.equals(mSecondary.token);
|
||||
|
||||
if (mSplitScreenSupported && (isPrimaryTask || isSecondaryTask)) {
|
||||
mSplitScreenSupported = false;
|
||||
|
||||
SurfaceControl.Transaction t = getTransaction();
|
||||
t.remove(mPrimaryDim);
|
||||
t.remove(mSecondaryDim);
|
||||
t.remove(mPrimarySurface);
|
||||
t.remove(mSecondarySurface);
|
||||
t.apply();
|
||||
releaseTransaction(t);
|
||||
|
||||
mDivider.onTaskVanished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||
if (taskInfo.displayId != DEFAULT_DISPLAY) {
|
||||
|
||||
@@ -140,8 +140,12 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
|
||||
|
||||
void setOrganizer(IDisplayAreaOrganizer organizer) {
|
||||
if (mOrganizer == organizer) return;
|
||||
sendDisplayAreaVanished();
|
||||
IDisplayAreaOrganizer lastOrganizer = mOrganizer;
|
||||
// Update the new display area organizer before calling sendDisplayAreaVanished since it
|
||||
// could result in a new SurfaceControl getting created that would notify the old organizer
|
||||
// about it.
|
||||
mOrganizer = organizer;
|
||||
sendDisplayAreaVanished(lastOrganizer);
|
||||
sendDisplayAreaAppeared();
|
||||
}
|
||||
|
||||
@@ -150,9 +154,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
|
||||
mOrganizerController.onDisplayAreaAppeared(mOrganizer, this);
|
||||
}
|
||||
|
||||
void sendDisplayAreaVanished() {
|
||||
if (mOrganizer == null) return;
|
||||
mOrganizerController.onDisplayAreaVanished(mOrganizer, this);
|
||||
void sendDisplayAreaVanished(IDisplayAreaOrganizer organizer) {
|
||||
if (organizer == null) return;
|
||||
migrateToNewSurfaceControl();
|
||||
mOrganizerController.onDisplayAreaVanished(organizer, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2004,6 +2004,14 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
|
||||
}
|
||||
|
||||
@Override
|
||||
void migrateToNewSurfaceControl() {
|
||||
super.migrateToNewSurfaceControl();
|
||||
mLastSurfaceSize.x = 0;
|
||||
mLastSurfaceSize.y = 0;
|
||||
updateSurfaceSize(getPendingTransaction());
|
||||
}
|
||||
|
||||
void updateSurfaceSize(SurfaceControl.Transaction transaction) {
|
||||
if (mSurfaceControl == null || mCreatedByOrganizer) {
|
||||
return;
|
||||
@@ -4400,16 +4408,25 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
return mHasBeenVisible;
|
||||
}
|
||||
|
||||
/** In the case that these three conditions are true, we want to send the Task to
|
||||
* the organizer:
|
||||
* 1. We have a SurfaceControl
|
||||
* 2. An organizer has been set
|
||||
* 3. We have finished drawing
|
||||
/** In the case that these conditions are true, we want to send the Task to the organizer:
|
||||
* 1. An organizer has been set
|
||||
* 2. The Task was created by the organizer
|
||||
* or
|
||||
* 2a. We have a SurfaceControl
|
||||
* 2b. We have finished drawing
|
||||
* Any time any of these conditions are updated, the updating code should call
|
||||
* sendTaskAppeared.
|
||||
*/
|
||||
boolean taskAppearedReady() {
|
||||
return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible();
|
||||
if (mTaskOrganizer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mCreatedByOrganizer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mSurfaceControl != null && getHasBeenVisible();
|
||||
}
|
||||
|
||||
private void sendTaskAppeared() {
|
||||
@@ -4418,9 +4435,9 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendTaskVanished() {
|
||||
if (mTaskOrganizer != null) {
|
||||
mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this);
|
||||
private void sendTaskVanished(ITaskOrganizer organizer) {
|
||||
if (organizer != null) {
|
||||
mAtmService.mTaskOrganizerController.onTaskVanished(organizer, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4429,9 +4446,13 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
if (mTaskOrganizer == organizer) {
|
||||
return false;
|
||||
}
|
||||
// Let the old organizer know it has lost control.
|
||||
sendTaskVanished();
|
||||
|
||||
ITaskOrganizer previousOrganizer = mTaskOrganizer;
|
||||
// Update the new task organizer before calling sendTaskVanished since it could result in
|
||||
// a new SurfaceControl getting created that would notify the old organizer about it.
|
||||
mTaskOrganizer = organizer;
|
||||
// Let the old organizer know it has lost control.
|
||||
sendTaskVanished(previousOrganizer);
|
||||
|
||||
if (mTaskOrganizer != null) {
|
||||
sendTaskAppeared();
|
||||
|
||||
@@ -211,6 +211,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
|
||||
void removeTask(Task t) {
|
||||
if (t.mTaskAppearedSent) {
|
||||
t.migrateToNewSurfaceControl();
|
||||
t.mTaskAppearedSent = false;
|
||||
mOrganizer.onTaskVanished(t);
|
||||
}
|
||||
|
||||
@@ -405,6 +405,40 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
updateSurfacePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SurfaceControl for this WindowContainer and migrate all properties to the new
|
||||
* SurfaceControl. Properties include:
|
||||
* 1. Children
|
||||
* 2. Position
|
||||
* 3. Z order
|
||||
*
|
||||
* Remove the old SurfaceControl since it's no longer needed.
|
||||
*
|
||||
* This is used to revoke control of the SurfaceControl from a client process that was
|
||||
* previously organizing this WindowContainer.
|
||||
*/
|
||||
void migrateToNewSurfaceControl() {
|
||||
SurfaceControl.Transaction t = getPendingTransaction();
|
||||
t.remove(mSurfaceControl);
|
||||
// Clear the last position so the new SurfaceControl will get correct position
|
||||
mLastSurfacePosition.set(0, 0);
|
||||
|
||||
createSurfaceControl(false /* force */);
|
||||
if (mLastRelativeToLayer != null) {
|
||||
t.setRelativeLayer(mSurfaceControl, mLastRelativeToLayer, mLastLayer);
|
||||
} else {
|
||||
t.setLayer(mSurfaceControl, mLastLayer);
|
||||
}
|
||||
|
||||
for (int i = 0; i < mChildren.size(); i++) {
|
||||
SurfaceControl sc = mChildren.get(i).getSurfaceControl();
|
||||
if (sc != null) {
|
||||
t.reparent(sc, mSurfaceControl);
|
||||
}
|
||||
}
|
||||
scheduleAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the surface is shown for the first time.
|
||||
*/
|
||||
|
||||
@@ -18,29 +18,24 @@ package com.android.test.taskembed;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.IWindowContainerTransactionCallback;
|
||||
import android.widget.LinearLayout;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.WindowContainerTransaction;
|
||||
import android.widget.LinearLayout;
|
||||
import android.window.WindowContainerTransactionCallback;
|
||||
import android.window.WindowOrganizer;
|
||||
|
||||
public class TaskOrganizerMultiWindowTest extends Activity {
|
||||
class SplitLayout extends LinearLayout implements View.OnTouchListener {
|
||||
@@ -173,6 +168,12 @@ public class TaskOrganizerMultiWindowTest extends Activity {
|
||||
setContentView(splitView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mOrganizer.unregisterOrganizer();
|
||||
}
|
||||
|
||||
private void addFlags(Intent intent) {
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
import android.view.ViewGroup;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.WindowContainerTransaction;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
public class TaskOrganizerPipTest extends Service {
|
||||
static final int PIP_WIDTH = 640;
|
||||
@@ -76,5 +76,6 @@ public class TaskOrganizerPipTest extends Service {
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mOrganizer.unregisterOrganizer();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user