Merge "Move letterbox task positioning to LetterboxTaskListener."
This commit is contained in:
committed by
Android (Google) Code Review
commit
771dfa640b
@@ -24,6 +24,8 @@ import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
@@ -180,6 +182,20 @@ public class TaskInfo {
|
||||
*/
|
||||
public boolean isResizeable;
|
||||
|
||||
/**
|
||||
* Activity bounds if this task or its top activity is presented in letterbox mode and
|
||||
* {@code null} otherwise.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public Rect letterboxActivityBounds;
|
||||
|
||||
/**
|
||||
* Relative position of the task's top left corner in the parent container.
|
||||
* @hide
|
||||
*/
|
||||
public Point positionInParent;
|
||||
|
||||
/**
|
||||
* The launch cookies associated with activities in this task if any.
|
||||
* @see ActivityOptions#setLaunchCookie(IBinder)
|
||||
@@ -256,6 +272,8 @@ public class TaskInfo {
|
||||
topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR);
|
||||
isResizeable = source.readBoolean();
|
||||
source.readBinderList(launchCookies);
|
||||
letterboxActivityBounds = source.readTypedObject(Rect.CREATOR);
|
||||
positionInParent = source.readTypedObject(Point.CREATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,6 +305,8 @@ public class TaskInfo {
|
||||
dest.writeTypedObject(topActivityInfo, flags);
|
||||
dest.writeBoolean(isResizeable);
|
||||
dest.writeBinderList(launchCookies);
|
||||
dest.writeTypedObject(letterboxActivityBounds, flags);
|
||||
dest.writeTypedObject(positionInParent, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -306,6 +326,9 @@ public class TaskInfo {
|
||||
+ " topActivityType=" + topActivityType
|
||||
+ " pictureInPictureParams=" + pictureInPictureParams
|
||||
+ " topActivityInfo=" + topActivityInfo
|
||||
+ " launchCookies" + launchCookies;
|
||||
+ " launchCookies" + launchCookies
|
||||
+ " letterboxActivityBounds=" + letterboxActivityBounds
|
||||
+ " positionInParent=" + positionInParent
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,12 @@
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"-848099324": {
|
||||
"message": "Letterbox Task Appeared: #%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
|
||||
},
|
||||
"-712674749": {
|
||||
"message": "Clip description: %s",
|
||||
"level": "VERBOSE",
|
||||
@@ -115,12 +121,24 @@
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
|
||||
},
|
||||
"1104702476": {
|
||||
"message": "Letterbox Task Changed: #%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
|
||||
},
|
||||
"1184615936": {
|
||||
"message": "Set drop target window visibility: displayId=%d visibility=%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_DRAG_AND_DROP",
|
||||
"at": "com\/android\/wm\/shell\/draganddrop\/DragAndDropController.java"
|
||||
},
|
||||
"1218010718": {
|
||||
"message": "Letterbox Task Vanished: #%d",
|
||||
"level": "VERBOSE",
|
||||
"group": "WM_SHELL_TASK_ORG",
|
||||
"at": "com\/android\/wm\/shell\/LetterboxTaskListener.java"
|
||||
},
|
||||
"1481772149": {
|
||||
"message": "Current target: %s",
|
||||
"level": "VERBOSE",
|
||||
|
||||
@@ -20,9 +20,7 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCR
|
||||
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
@@ -39,7 +37,7 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
|
||||
private final ArrayMap<Integer, SurfaceControl> mTasks = new ArrayMap<>();
|
||||
private final ArraySet<Integer> mTasks = new ArraySet<>();
|
||||
|
||||
FullscreenTaskListener(SyncTransactionQueue syncQueue) {
|
||||
mSyncQueue = syncQueue;
|
||||
@@ -48,17 +46,17 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@Override
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
synchronized (mTasks) {
|
||||
if (mTasks.containsKey(taskInfo.taskId)) {
|
||||
if (mTasks.contains(taskInfo.taskId)) {
|
||||
throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
|
||||
}
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
|
||||
taskInfo.taskId);
|
||||
mTasks.put(taskInfo.taskId, leash);
|
||||
mTasks.add(taskInfo.taskId);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
// Reset several properties back to fullscreen (PiP, for example, leaves all these
|
||||
// properties in a bad state).
|
||||
updateSurfacePosition(t, taskInfo, leash);
|
||||
t.setWindowCrop(leash, null);
|
||||
t.setPosition(leash, 0, 0);
|
||||
// TODO(shell-transitions): Eventually set everything in transition so there's no
|
||||
// SF Transaction here.
|
||||
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||
@@ -73,7 +71,7 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
@Override
|
||||
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
synchronized (mTasks) {
|
||||
if (mTasks.remove(taskInfo.taskId) == null) {
|
||||
if (!mTasks.remove(taskInfo.taskId)) {
|
||||
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
|
||||
return;
|
||||
}
|
||||
@@ -82,23 +80,6 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
synchronized (mTasks) {
|
||||
if (!mTasks.containsKey(taskInfo.taskId)) {
|
||||
Slog.e(TAG, "Changed Task wasn't appeared or already vanished: #"
|
||||
+ taskInfo.taskId);
|
||||
return;
|
||||
}
|
||||
final SurfaceControl leash = mTasks.get(taskInfo.taskId);
|
||||
mSyncQueue.runInSync(t -> {
|
||||
// Reposition the task in case the bounds has been changed (such as Task level
|
||||
// letterboxing).
|
||||
updateSurfacePosition(t, taskInfo, leash);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
@@ -112,12 +93,4 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
|
||||
}
|
||||
|
||||
/** Places the Task surface to the latest position. */
|
||||
private static void updateSurfacePosition(SurfaceControl.Transaction t,
|
||||
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
// TODO(170725334) drop this after ag/12876439
|
||||
final Configuration config = taskInfo.getConfiguration();
|
||||
final Rect bounds = config.windowConfiguration.getBounds();
|
||||
t.setPosition(leash, bounds.left, bounds.top);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.wm.shell;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
|
||||
/**
|
||||
* Organizes a task in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN} when
|
||||
* it's presented in the letterbox mode either because orientations of a top activity and a device
|
||||
* don't match or because a top activity is in a size compat mode.
|
||||
*/
|
||||
final class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
private static final String TAG = "LetterboxTaskListener";
|
||||
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
|
||||
private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>();
|
||||
|
||||
LetterboxTaskListener(SyncTransactionQueue syncQueue) {
|
||||
mSyncQueue = syncQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
|
||||
synchronized (mLeashByTaskId) {
|
||||
if (mLeashByTaskId.get(taskInfo.taskId) != null) {
|
||||
throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
|
||||
}
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Appeared: #%d",
|
||||
taskInfo.taskId);
|
||||
mLeashByTaskId.put(taskInfo.taskId, leash);
|
||||
final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
|
||||
final Rect activtyBounds = taskInfo.letterboxActivityBounds;
|
||||
final Point taskPositionInParent = taskInfo.positionInParent;
|
||||
mSyncQueue.runInSync(t -> {
|
||||
setPositionAndWindowCrop(
|
||||
t, leash, activtyBounds, taskBounds, taskPositionInParent);
|
||||
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
|
||||
t.setAlpha(leash, 1f);
|
||||
t.setMatrix(leash, 1, 0, 0, 1);
|
||||
t.show(leash);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
synchronized (mLeashByTaskId) {
|
||||
if (mLeashByTaskId.get(taskInfo.taskId) == null) {
|
||||
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
|
||||
return;
|
||||
}
|
||||
mLeashByTaskId.remove(taskInfo.taskId);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Vanished: #%d",
|
||||
taskInfo.taskId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
synchronized (mLeashByTaskId) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Changed: #%d",
|
||||
taskInfo.taskId);
|
||||
final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId);
|
||||
final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
|
||||
final Rect activtyBounds = taskInfo.letterboxActivityBounds;
|
||||
final Point taskPositionInParent = taskInfo.positionInParent;
|
||||
mSyncQueue.runInSync(t -> {
|
||||
setPositionAndWindowCrop(
|
||||
t, leash, activtyBounds, taskBounds, taskPositionInParent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void setPositionAndWindowCrop(
|
||||
SurfaceControl.Transaction transaction,
|
||||
SurfaceControl leash,
|
||||
final Rect activityBounds,
|
||||
final Rect taskBounds,
|
||||
final Point taskPositionInParent) {
|
||||
Rect activtyInTaskCoordinates = new Rect(activityBounds);
|
||||
activtyInTaskCoordinates.offset(-taskBounds.left, -taskBounds.top);
|
||||
transaction.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
|
||||
transaction.setWindowCrop(leash, activtyInTaskCoordinates);
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,6 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
@@ -65,6 +64,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3;
|
||||
public static final int TASK_LISTENER_TYPE_PIP = -4;
|
||||
public static final int TASK_LISTENER_TYPE_SPLIT_SCREEN = -5;
|
||||
public static final int TASK_LISTENER_TYPE_LETTERBOX = -6;
|
||||
|
||||
@IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = {
|
||||
TASK_LISTENER_TYPE_UNDEFINED,
|
||||
@@ -72,6 +72,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
TASK_LISTENER_TYPE_MULTI_WINDOW,
|
||||
TASK_LISTENER_TYPE_PIP,
|
||||
TASK_LISTENER_TYPE_SPLIT_SCREEN,
|
||||
TASK_LISTENER_TYPE_LETTERBOX,
|
||||
})
|
||||
public @interface TaskListenerType {}
|
||||
|
||||
@@ -118,6 +119,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
ShellExecutor mainExecutor, ShellExecutor animExecutor) {
|
||||
super(taskOrganizerController, mainExecutor);
|
||||
addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
|
||||
addListenerForType(new LetterboxTaskListener(syncQueue), TASK_LISTENER_TYPE_LETTERBOX);
|
||||
mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
|
||||
}
|
||||
@@ -329,8 +331,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
if (listener != null) return listener;
|
||||
|
||||
// Next we try type specific listeners.
|
||||
final int windowingMode = getWindowingMode(runningTaskInfo);
|
||||
final int taskListenerType = windowingModeToTaskListenerType(windowingMode);
|
||||
final int taskListenerType = taskInfoToTaskListenerType(runningTaskInfo);
|
||||
return mTaskListeners.get(taskListenerType);
|
||||
}
|
||||
|
||||
@@ -339,11 +340,14 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
return taskInfo.configuration.windowConfiguration.getWindowingMode();
|
||||
}
|
||||
|
||||
private static @TaskListenerType int windowingModeToTaskListenerType(
|
||||
@WindowingMode int windowingMode) {
|
||||
@VisibleForTesting
|
||||
static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) {
|
||||
final int windowingMode = getWindowingMode(runningTaskInfo);
|
||||
switch (windowingMode) {
|
||||
case WINDOWING_MODE_FULLSCREEN:
|
||||
return TASK_LISTENER_TYPE_FULLSCREEN;
|
||||
return runningTaskInfo.letterboxActivityBounds != null
|
||||
? TASK_LISTENER_TYPE_LETTERBOX
|
||||
: TASK_LISTENER_TYPE_FULLSCREEN;
|
||||
case WINDOWING_MODE_MULTI_WINDOW:
|
||||
return TASK_LISTENER_TYPE_MULTI_WINDOW;
|
||||
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
|
||||
@@ -362,6 +366,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
switch (type) {
|
||||
case TASK_LISTENER_TYPE_FULLSCREEN:
|
||||
return "TASK_LISTENER_TYPE_FULLSCREEN";
|
||||
case TASK_LISTENER_TYPE_LETTERBOX:
|
||||
return "TASK_LISTENER_TYPE_LETTERBOX";
|
||||
case TASK_LISTENER_TYPE_MULTI_WINDOW:
|
||||
return "TASK_LISTENER_TYPE_MULTI_WINDOW";
|
||||
case TASK_LISTENER_TYPE_SPLIT_SCREEN:
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.wm.shell;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Tests for {@link LetterboxTaskListener}.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LetterboxTaskListenerTest {
|
||||
|
||||
private static final Rect ACTIVITY_BOUNDS = new Rect(300, 200, 700, 400);
|
||||
private static final Rect TASK_BOUNDS = new Rect(200, 100, 800, 500);
|
||||
private static final Rect TASK_BOUNDS_2 = new Rect(300, 200, 800, 500);
|
||||
private static final Point TASK_POSITION_IN_PARENT = new Point(100, 50);
|
||||
private static final Point TASK_POSITION_IN_PARENT_2 = new Point(200, 100);
|
||||
|
||||
private static final Rect EXPECTED_WINDOW_CROP = new Rect(100, 100, 500, 300);
|
||||
private static final Rect EXPECTED_WINDOW_CROP_2 = new Rect(0, 0, 400, 200);
|
||||
|
||||
private static final RunningTaskInfo TASK_INFO = createTaskInfo(
|
||||
/* taskId */ 1, ACTIVITY_BOUNDS, TASK_BOUNDS, TASK_POSITION_IN_PARENT);
|
||||
|
||||
private static final RunningTaskInfo TASK_INFO_2 = createTaskInfo(
|
||||
/* taskId */ 1, ACTIVITY_BOUNDS, TASK_BOUNDS_2, TASK_POSITION_IN_PARENT_2);
|
||||
|
||||
@Mock private SurfaceControl mLeash;
|
||||
@Mock private SurfaceControl.Transaction mTransaction;
|
||||
private LetterboxTaskListener mLetterboxTaskListener;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mLetterboxTaskListener = new LetterboxTaskListener(
|
||||
new SyncTransactionQueue(
|
||||
new TransactionPool() {
|
||||
@Override
|
||||
public SurfaceControl.Transaction acquire() {
|
||||
return mTransaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(SurfaceControl.Transaction t) {
|
||||
}
|
||||
},
|
||||
new Handler(Looper.getMainLooper())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnTaskAppearedAndonTaskInfoChanged_setCorrectPositionAndCrop() {
|
||||
mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
|
||||
|
||||
verify(mTransaction).setPosition(
|
||||
eq(mLeash),
|
||||
eq((float) TASK_POSITION_IN_PARENT.x),
|
||||
eq((float) TASK_POSITION_IN_PARENT.y));
|
||||
// Should return activty coordinates offset by task coordinates
|
||||
verify(mTransaction).setWindowCrop(eq(mLeash), eq(EXPECTED_WINDOW_CROP));
|
||||
|
||||
mLetterboxTaskListener.onTaskInfoChanged(TASK_INFO_2);
|
||||
|
||||
verify(mTransaction).setPosition(
|
||||
eq(mLeash),
|
||||
eq((float) TASK_POSITION_IN_PARENT_2.x),
|
||||
eq((float) TASK_POSITION_IN_PARENT_2.y));
|
||||
// Should return activty coordinates offset by task coordinates
|
||||
verify(mTransaction).setWindowCrop(eq(mLeash), eq(EXPECTED_WINDOW_CROP_2));
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
|
||||
mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
|
||||
mLetterboxTaskListener.onTaskAppeared(TASK_INFO, mLeash);
|
||||
}
|
||||
|
||||
private static RunningTaskInfo createTaskInfo(
|
||||
int taskId,
|
||||
final Rect activityBounds,
|
||||
final Rect taskBounds,
|
||||
final Point taskPositionInParent) {
|
||||
RunningTaskInfo taskInfo = new RunningTaskInfo();
|
||||
taskInfo.taskId = taskId;
|
||||
taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
|
||||
taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
|
||||
taskInfo.positionInParent = new Point(taskPositionInParent);
|
||||
return taskInfo;
|
||||
}
|
||||
}
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
package com.android.wm.shell;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
|
||||
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
|
||||
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERBOX;
|
||||
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
|
||||
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
@@ -34,6 +37,7 @@ import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -42,6 +46,7 @@ import android.window.ITaskOrganizer;
|
||||
import android.window.ITaskOrganizerController;
|
||||
import android.window.TaskAppearedInfo;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -242,10 +247,41 @@ public class ShellTaskOrganizerTests {
|
||||
assertTrue(gotException);
|
||||
}
|
||||
|
||||
private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
|
||||
@Test
|
||||
public void testTaskInfoToTaskListenerType_whenLetterboxBoundsPassed_returnsLetterboxType() {
|
||||
RunningTaskInfo taskInfo = createTaskInfo(
|
||||
/* taskId */ 1,
|
||||
WINDOWING_MODE_FULLSCREEN,
|
||||
/* letterboxActivityBounds */ new Rect(1, 1, 1, 1));
|
||||
|
||||
assertEquals(
|
||||
ShellTaskOrganizer.taskInfoToTaskListenerType(taskInfo),
|
||||
TASK_LISTENER_TYPE_LETTERBOX);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskInfoToTaskListenerType_whenLetterboxBoundsIsNull_returnsFullscreenType() {
|
||||
RunningTaskInfo taskInfo = createTaskInfo(
|
||||
/* taskId */ 1, WINDOWING_MODE_FULLSCREEN, /* letterboxActivityBounds */ null);
|
||||
|
||||
assertEquals(
|
||||
ShellTaskOrganizer.taskInfoToTaskListenerType(taskInfo),
|
||||
TASK_LISTENER_TYPE_FULLSCREEN);
|
||||
}
|
||||
|
||||
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
|
||||
RunningTaskInfo taskInfo = new RunningTaskInfo();
|
||||
taskInfo.taskId = taskId;
|
||||
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
|
||||
return taskInfo;
|
||||
}
|
||||
|
||||
private static RunningTaskInfo createTaskInfo(
|
||||
int taskId, int windowingMode, @Nullable Rect letterboxActivityBounds) {
|
||||
RunningTaskInfo taskInfo = new RunningTaskInfo();
|
||||
taskInfo.taskId = taskId;
|
||||
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
|
||||
taskInfo.letterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds);
|
||||
return taskInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1364,6 +1364,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
} else if (mLetterbox != null) {
|
||||
mLetterbox.hide();
|
||||
}
|
||||
task.maybeUpdateLetterboxBounds(this, getLetterboxParams(w));
|
||||
}
|
||||
|
||||
void updateLetterboxSurface(WindowState winHint) {
|
||||
@@ -1377,6 +1378,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Rect getLetterboxParams(WindowState w) {
|
||||
boolean isLetterboxed = w.isLetterboxedAppWindow() && fillsParent();
|
||||
return isLetterboxed ? getBounds() : null;
|
||||
}
|
||||
|
||||
Rect getLetterboxInsets() {
|
||||
if (mLetterbox != null) {
|
||||
return mLetterbox.getInsets();
|
||||
|
||||
@@ -528,6 +528,11 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
// {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag of the root activity.
|
||||
boolean mSupportsPictureInPicture;
|
||||
|
||||
// Activity bounds if this task or its top activity is presented in letterbox mode and
|
||||
// {@code null} otherwise.
|
||||
@Nullable
|
||||
private Rect mLetterboxActivityBounds;
|
||||
|
||||
// Whether the task is currently being drag-resized
|
||||
private boolean mDragResizing;
|
||||
private int mDragResizeMode;
|
||||
@@ -4072,6 +4077,12 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
|
||||
info.topActivityType = top.getActivityType();
|
||||
info.isResizeable = isResizeable();
|
||||
// Don't query getTopNonFinishingActivity().getBounds() directly because when fillTaskInfo
|
||||
// is triggered for the first time after activities change, getBounds() may return non final
|
||||
// bounds, e.g. fullscreen bounds instead of letterboxed bounds. To work around this,
|
||||
// assigning bounds from ActivityRecord#layoutLetterbox when they are ready.
|
||||
info.letterboxActivityBounds = Rect.copyOrNull(mLetterboxActivityBounds);
|
||||
info.positionInParent = getRelativePosition();
|
||||
|
||||
info.pictureInPictureParams = getPictureInPictureParams();
|
||||
info.topActivityInfo = mReuseActivitiesReport.top != null
|
||||
@@ -4090,6 +4101,21 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
? null : rootActivity.pictureInPictureArgs;
|
||||
}
|
||||
|
||||
void maybeUpdateLetterboxBounds(
|
||||
ActivityRecord activityRecord, @Nullable Rect letterboxActivityBounds) {
|
||||
if (isOrganized()
|
||||
&& mReuseActivitiesReport.top == activityRecord
|
||||
// Want to force update only if letterbox bounds have changed.
|
||||
&& !Objects.equals(
|
||||
mLetterboxActivityBounds,
|
||||
letterboxActivityBounds)) {
|
||||
mLetterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds);
|
||||
// Forcing update to reduce visual jank during the transition.
|
||||
mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
|
||||
this, /* force= */ true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link TaskInfo} with information from this task.
|
||||
*/
|
||||
@@ -7467,6 +7493,12 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
outPos.y -= outset;
|
||||
}
|
||||
|
||||
private Point getRelativePosition() {
|
||||
Point position = new Point();
|
||||
getRelativePosition(position);
|
||||
return position;
|
||||
}
|
||||
|
||||
boolean shouldIgnoreInput() {
|
||||
if (inSplitScreenPrimaryWindowingMode() && !isFocusable()) {
|
||||
return true;
|
||||
|
||||
@@ -53,6 +53,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -475,6 +476,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
boolean changed = lastInfo == null
|
||||
|| mTmpTaskInfo.topActivityType != lastInfo.topActivityType
|
||||
|| mTmpTaskInfo.isResizeable != lastInfo.isResizeable
|
||||
|| !Objects.equals(
|
||||
mTmpTaskInfo.letterboxActivityBounds,
|
||||
lastInfo.letterboxActivityBounds)
|
||||
|| !Objects.equals(
|
||||
mTmpTaskInfo.positionInParent,
|
||||
lastInfo.positionInParent)
|
||||
|| mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
|
||||
|| mTmpTaskInfo.getConfiguration().windowConfiguration.getWindowingMode()
|
||||
!= lastInfo.getConfiguration().windowConfiguration.getWindowingMode()
|
||||
|
||||
Reference in New Issue
Block a user