TaskOrganizer: Add WINDOWING_MODE_MULTI_WINDOW support

Also add small test activity.

Bug: 139371701
Test: TaskOrganizerTests. TaskOrganizerMultiWindowTest.
Change-Id: I0bea25a5c8eef25fba88a0d056ef464035749a59
This commit is contained in:
Robert Carr
2020-01-24 15:30:24 -08:00
parent f0a9dd1e94
commit 00c0dbe2cb
6 changed files with 203 additions and 51 deletions

View File

@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.os.IBinder;
import android.os.RemoteException;
@@ -106,9 +107,11 @@ class TaskOrganizerController {
* and receive taskVanished callbacks in the process.
*/
void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
if (windowingMode != WINDOWING_MODE_PINNED) {
if (windowingMode != WINDOWING_MODE_PINNED &&
windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
throw new UnsupportedOperationException(
"As of now only Pinned windowing mode is supported for registerTaskOrganizer");
"As of now only Pinned and Multiwindow windowing modes are"
+ " supported for registerTaskOrganizer");
}
clearIfNeeded(windowingMode);

View File

@@ -16,9 +16,9 @@
package com.android.server.wm;
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_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
@@ -63,20 +63,24 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class TaskOrganizerTests extends WindowTestsBase {
private ITaskOrganizer makeAndRegisterMockOrganizer() {
private ITaskOrganizer registerMockOrganizer(int windowingMode) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
when(organizer.asBinder()).thenReturn(new Binder());
mWm.mAtmService.registerTaskOrganizer(organizer, WINDOWING_MODE_PINNED);
mWm.mAtmService.registerTaskOrganizer(organizer, windowingMode);
return organizer;
}
private ITaskOrganizer registerMockOrganizer() {
return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
}
@Test
public void testAppearVanish() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
final ITaskOrganizer organizer = registerMockOrganizer();
task.setTaskOrganizer(organizer);
verify(organizer).taskAppeared(any(), any());
@@ -89,8 +93,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
public void testSwapOrganizer() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
final ITaskOrganizer organizer2 = makeAndRegisterMockOrganizer();
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
task.setTaskOrganizer(organizer);
verify(organizer).taskAppeared(any(), any());
@@ -99,11 +103,25 @@ public class TaskOrganizerTests extends WindowTestsBase {
verify(organizer2).taskAppeared(any(), any());
}
@Test
public void testSwapWindowingModes() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
verify(organizer).taskAppeared(any(), any());
stack.setWindowingMode(WINDOWING_MODE_PINNED);
verify(organizer).taskVanished(any());
verify(organizer2).taskAppeared(any(), any());
}
@Test
public void testClearOrganizer() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
final ITaskOrganizer organizer = registerMockOrganizer();
stack.setTaskOrganizer(organizer);
verify(organizer).taskAppeared(any(), any());
@@ -116,7 +134,7 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
final ITaskOrganizer organizer = makeAndRegisterMockOrganizer();
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);

View File

@@ -19,5 +19,11 @@
<service android:name=".TaskOrganizerPipTest"
android:exported="true">
</service>
<activity android:name="TaskOrganizerMultiWindowTest" android:label="TaskOrganizer MW Test">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2019 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.test.taskembed;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.ActivityManager;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.ViewGroup;
import android.widget.FrameLayout;
public class TaskOrganizerMultiWindowTest extends Activity {
class TaskLaunchingView extends TaskView {
TaskLaunchingView(Context c, ITaskOrganizer o, int windowingMode) {
super(c, o, windowingMode);
}
@Override
public void surfaceChanged(SurfaceHolder h, int format, int width, int height) {
startCalculatorActivity(width, height);
}
}
TaskView mView;
class Organizer extends ITaskOrganizer.Stub {
@Override
public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
mView.reparentTask(wc);
}
public void taskVanished(IWindowContainer wc) {
}
public void transactionReady(int id, SurfaceControl.Transaction t) {
}
}
Organizer mOrganizer = new Organizer();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = new TaskLaunchingView(this, mOrganizer, WINDOWING_MODE_MULTI_WINDOW);
setContentView(mView);
}
Intent makeCalculatorIntent() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_CALCULATOR);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
Bundle makeLaunchOptions(int width, int height) {
ActivityOptions o = ActivityOptions.makeBasic();
o.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
o.setLaunchBounds(new Rect(0, 0, width, height));
return o.toBundle();
}
void startCalculatorActivity(int width, int height) {
startActivity(makeCalculatorIntent(), makeLaunchOptions(width, height));
}
}

View File

@@ -16,6 +16,8 @@
package com.android.test.taskembed;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.Service;
@@ -38,48 +40,11 @@ public class TaskOrganizerPipTest extends Service {
static final int PIP_WIDTH = 640;
static final int PIP_HEIGHT = 360;
class PipOrgView extends SurfaceView implements SurfaceHolder.Callback {
PipOrgView(Context c) {
super(c);
getHolder().addCallback(this);
setZOrderOnTop(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
ActivityTaskManager.getService().registerTaskOrganizer(mOrganizer,
WindowConfiguration.WINDOWING_MODE_PINNED);
} catch (Exception e) {
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
void reparentTask(IWindowContainer wc) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
SurfaceControl leash = null;
try {
leash = wc.getLeash();
} catch (Exception e) {
// System server died.. oh well
}
t.reparent(leash, getSurfaceControl())
.setPosition(leash, 0, 0)
.apply();
}
}
PipOrgView mPipView;
TaskView mTaskView;
class Organizer extends ITaskOrganizer.Stub {
public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
mPipView.reparentTask(wc);
mTaskView.reparentTask(wc);
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.scheduleFinishEnterPip(wc, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
@@ -113,8 +78,8 @@ public class TaskOrganizerPipTest extends Service {
FrameLayout layout = new FrameLayout(this);
ViewGroup.LayoutParams lp =
new ViewGroup.LayoutParams(PIP_WIDTH, PIP_HEIGHT);
mPipView = new PipOrgView(this);
layout.addView(mPipView, lp);
mTaskView = new TaskView(this, mOrganizer, WINDOWING_MODE_PINNED);
layout.addView(mTaskView, lp);
WindowManager wm = getSystemService(WindowManager.class);
wm.addView(layout, wlp);

View File

@@ -0,0 +1,73 @@
/*
* 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.test.taskembed;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* Simple SurfaceView wrapper which registers a TaskOrganizer
* after it's Surface is ready.
*/
class TaskView extends SurfaceView implements SurfaceHolder.Callback {
final ITaskOrganizer mTaskOrganizer;
final int mWindowingMode;
TaskView(Context c, ITaskOrganizer o, int windowingMode) {
super(c);
getHolder().addCallback(this);
setZOrderOnTop(true);
mTaskOrganizer = o;
mWindowingMode = windowingMode;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
ActivityTaskManager.getService().registerTaskOrganizer(mTaskOrganizer,
mWindowingMode);
} catch (Exception e) {
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
void reparentTask(IWindowContainer wc) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
SurfaceControl leash = null;
try {
leash = wc.getLeash();
} catch (Exception e) {
// System server died.. oh well
}
t.reparent(leash, getSurfaceControl())
.setPosition(leash, 0, 0)
.apply();
}
}