Merge "Mirror TaskSnapshotSurface to WMShell(2/N)"
This commit is contained in:
committed by
Android (Google) Code Review
commit
612dbde32a
@@ -2461,6 +2461,13 @@ package android.window {
|
||||
field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2
|
||||
}
|
||||
|
||||
public final class StartingWindowInfo implements android.os.Parcelable {
|
||||
ctor public StartingWindowInfo();
|
||||
method public int describeContents();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.window.StartingWindowInfo> CREATOR;
|
||||
}
|
||||
|
||||
public final class TaskAppearedInfo implements android.os.Parcelable {
|
||||
ctor public TaskAppearedInfo(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
|
||||
method public int describeContents();
|
||||
@@ -2472,7 +2479,7 @@ package android.window {
|
||||
|
||||
public class TaskOrganizer extends android.window.WindowOrganizer {
|
||||
ctor public TaskOrganizer();
|
||||
method @BinderThread public void addStartingWindow(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.os.IBinder);
|
||||
method @BinderThread public void addStartingWindow(@NonNull android.window.StartingWindowInfo, @NonNull android.os.IBinder);
|
||||
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void createRootTask(int, int, @Nullable android.os.IBinder);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken);
|
||||
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
|
||||
@@ -2483,7 +2490,7 @@ package android.window {
|
||||
method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
|
||||
method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
|
||||
method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
|
||||
method @BinderThread public void removeStartingWindow(@NonNull android.app.ActivityManager.RunningTaskInfo);
|
||||
method @BinderThread public void removeStartingWindow(int);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
|
||||
method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer();
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.window;
|
||||
|
||||
import android.view.SurfaceControl;
|
||||
import android.app.ActivityManager;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
/**
|
||||
@@ -30,15 +31,15 @@ oneway interface ITaskOrganizer {
|
||||
* application is starting. The client is responsible to add/remove the starting window if it
|
||||
* has create a starting window for the Task.
|
||||
*
|
||||
* @param taskInfo The information about the Task that's available
|
||||
* @param info The information about the Task that's available
|
||||
* @param appToken Token of the application being started.
|
||||
*/
|
||||
void addStartingWindow(in ActivityManager.RunningTaskInfo taskInfo, IBinder appToken);
|
||||
void addStartingWindow(in StartingWindowInfo info, IBinder appToken);
|
||||
|
||||
/**
|
||||
* Called when the Task want to remove the starting window.
|
||||
*/
|
||||
void removeStartingWindow(in ActivityManager.RunningTaskInfo taskInfo);
|
||||
void removeStartingWindow(int taskId);
|
||||
|
||||
/**
|
||||
* A callback when the Task is available for the registered organizer. The client is responsible
|
||||
|
||||
20
core/java/android/window/StartingWindowInfo.aidl
Normal file
20
core/java/android/window/StartingWindowInfo.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 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 android.window;
|
||||
|
||||
/** @hide */
|
||||
parcelable StartingWindowInfo;
|
||||
150
core/java/android/window/StartingWindowInfo.java
Normal file
150
core/java/android/window/StartingWindowInfo.java
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 android.window;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.InsetsState;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* Information you can retrieve about a starting window of a particular task that is currently
|
||||
* start in the system.
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class StartingWindowInfo implements Parcelable {
|
||||
/**
|
||||
* The {@link TaskInfo} from this task.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public ActivityManager.RunningTaskInfo taskInfo;
|
||||
|
||||
/**
|
||||
* InsetsState of TopFullscreenOpaqueWindow
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public InsetsState topOpaqueWindowInsetsState;
|
||||
|
||||
/**
|
||||
* LayoutParams of TopFullscreenOpaqueWindow
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public WindowManager.LayoutParams topOpaqueWindowLayoutParams;
|
||||
|
||||
/**
|
||||
* LayoutParams of MainWindow
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public WindowManager.LayoutParams mainWindowLayoutParams;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@IntDef(flag = true, prefix = "TYPE_PARAMETER_", value = {
|
||||
TYPE_PARAMETER_NEW_TASK,
|
||||
TYPE_PARAMETER_TASK_SWITCH,
|
||||
TYPE_PARAMETER_PROCESS_RUNNING,
|
||||
TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
|
||||
TYPE_PARAMETER_ACTIVITY_CREATED
|
||||
})
|
||||
public @interface StartingTypeParams {}
|
||||
|
||||
/**
|
||||
* The parameters of the starting window...
|
||||
* @hide
|
||||
*/
|
||||
public static final int TYPE_PARAMETER_NEW_TASK = 0x00000001;
|
||||
/** @hide */
|
||||
public static final int TYPE_PARAMETER_TASK_SWITCH = 0x00000002;
|
||||
/** @hide */
|
||||
public static final int TYPE_PARAMETER_PROCESS_RUNNING = 0x00000004;
|
||||
/** @hide */
|
||||
public static final int TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT = 0x00000008;
|
||||
/** @hide */
|
||||
public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
|
||||
|
||||
/**
|
||||
* The parameters which effect the starting window type.
|
||||
* @see android.window.StartingWindowInfo.StartingTypeParams
|
||||
* @hide
|
||||
*/
|
||||
public int startingWindowTypeParameter;
|
||||
|
||||
public StartingWindowInfo() {
|
||||
|
||||
}
|
||||
|
||||
private StartingWindowInfo(@NonNull Parcel source) {
|
||||
readFromParcel(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeTypedObject(taskInfo, flags);
|
||||
dest.writeInt(startingWindowTypeParameter);
|
||||
dest.writeTypedObject(topOpaqueWindowInsetsState, flags);
|
||||
dest.writeTypedObject(topOpaqueWindowLayoutParams, flags);
|
||||
dest.writeTypedObject(mainWindowLayoutParams, flags);
|
||||
}
|
||||
|
||||
void readFromParcel(@NonNull Parcel source) {
|
||||
taskInfo = source.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
|
||||
startingWindowTypeParameter = source.readInt();
|
||||
topOpaqueWindowInsetsState = source.readTypedObject(InsetsState.CREATOR);
|
||||
topOpaqueWindowLayoutParams = source.readTypedObject(
|
||||
WindowManager.LayoutParams.CREATOR);
|
||||
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StartingWindowInfo{taskId=" + taskInfo.taskId
|
||||
+ " displayId=" + taskInfo.displayId
|
||||
+ " topActivityType=" + taskInfo.topActivityType
|
||||
+ " preferredStartingWindowType="
|
||||
+ Integer.toHexString(startingWindowTypeParameter)
|
||||
+ " insetsState=" + topOpaqueWindowInsetsState
|
||||
+ " topWindowLayoutParams=" + topOpaqueWindowLayoutParams
|
||||
+ " mainWindowLayoutParams=" + mainWindowLayoutParams;
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<StartingWindowInfo> CREATOR =
|
||||
new Creator<StartingWindowInfo>() {
|
||||
public StartingWindowInfo createFromParcel(@NonNull Parcel source) {
|
||||
return new StartingWindowInfo(source);
|
||||
}
|
||||
public StartingWindowInfo[] newArray(int size) {
|
||||
return new StartingWindowInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -89,19 +89,19 @@ public class TaskOrganizer extends WindowOrganizer {
|
||||
* application is starting. The client is responsible to add/remove the starting window if it
|
||||
* has create a starting window for the Task.
|
||||
*
|
||||
* @param taskInfo The information about the Task that's available
|
||||
* @param info The information about the Task that's available
|
||||
* @param appToken Token of the application being started.
|
||||
* context to for resources
|
||||
*/
|
||||
@BinderThread
|
||||
public void addStartingWindow(@NonNull ActivityManager.RunningTaskInfo taskInfo,
|
||||
public void addStartingWindow(@NonNull StartingWindowInfo info,
|
||||
@NonNull IBinder appToken) {}
|
||||
|
||||
/**
|
||||
* Called when the Task want to remove the starting window.
|
||||
*/
|
||||
@BinderThread
|
||||
public void removeStartingWindow(@NonNull ActivityManager.RunningTaskInfo taskInfo) {}
|
||||
public void removeStartingWindow(int taskId) {}
|
||||
|
||||
/**
|
||||
* Called when a task with the registered windowing mode can be controlled by this task
|
||||
@@ -221,13 +221,15 @@ public class TaskOrganizer extends WindowOrganizer {
|
||||
|
||||
private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
|
||||
@Override
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo taskInfo, IBinder appToken) {
|
||||
mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(taskInfo, appToken));
|
||||
|
||||
public void addStartingWindow(StartingWindowInfo windowInfo,
|
||||
IBinder appToken) {
|
||||
mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskInfo));
|
||||
public void removeStartingWindow(int taskId) {
|
||||
mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.ITaskOrganizerController;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskAppearedInfo;
|
||||
import android.window.TaskOrganizer;
|
||||
|
||||
@@ -112,7 +113,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
// TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled
|
||||
// by a controller, that class should be create while porting
|
||||
// ActivityRecord#addStartingWindow to WMShell.
|
||||
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context);
|
||||
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, mainExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -229,13 +230,13 @@ public class ShellTaskOrganizer extends TaskOrganizer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addStartingWindow(RunningTaskInfo taskInfo, IBinder appToken) {
|
||||
mStartingSurfaceDrawer.addStartingWindow(taskInfo, appToken);
|
||||
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||
mStartingSurfaceDrawer.addStartingWindow(info, appToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStartingWindow(RunningTaskInfo taskInfo) {
|
||||
mStartingSurfaceDrawer.removeStartingWindow(taskInfo);
|
||||
public void removeStartingWindow(int taskId) {
|
||||
mStartingSurfaceDrawer.removeStartingWindow(taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,11 +16,18 @@
|
||||
|
||||
package com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.content.Context.CONTEXT_RESTRICTED;
|
||||
import static android.content.res.Configuration.EMPTY;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -30,18 +37,20 @@ import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskOrganizer;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.policy.PhoneWindow;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -56,22 +65,24 @@ import java.util.function.Consumer;
|
||||
* @hide
|
||||
*/
|
||||
public class StartingSurfaceDrawer {
|
||||
private static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
|
||||
private static final boolean DEBUG_SPLASH_SCREEN = false;
|
||||
static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
|
||||
static final boolean DEBUG_SPLASH_SCREEN = false;
|
||||
static final boolean DEBUG_TASK_SNAPSHOT = false;
|
||||
|
||||
private final Context mContext;
|
||||
private final DisplayManager mDisplayManager;
|
||||
final ShellExecutor mMainExecutor;
|
||||
|
||||
// TODO(b/131727939) remove this when clearing ActivityRecord
|
||||
private static final int REMOVE_WHEN_TIMEOUT = 2000;
|
||||
|
||||
public StartingSurfaceDrawer(Context context) {
|
||||
public StartingSurfaceDrawer(Context context, ShellExecutor mainExecutor) {
|
||||
mContext = context;
|
||||
mDisplayManager = mContext.getSystemService(DisplayManager.class);
|
||||
mMainExecutor = mainExecutor;
|
||||
}
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||
private final SparseArray<TaskScreenView> mTaskScreenViews = new SparseArray<>();
|
||||
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
|
||||
|
||||
/** Obtain proper context for showing splash screen on the provided display. */
|
||||
private Context getDisplayContext(Context context, int displayId) {
|
||||
@@ -90,12 +101,111 @@ public class StartingSurfaceDrawer {
|
||||
return context.createDisplayContext(targetDisplay);
|
||||
}
|
||||
|
||||
private static class PreferredStartingTypeHelper {
|
||||
private static final int STARTING_TYPE_NO = 0;
|
||||
private static final int STARTING_TYPE_SPLASH_SCREEN = 1;
|
||||
private static final int STARTING_TYPE_SNAPSHOT = 2;
|
||||
|
||||
TaskSnapshot mSnapshot;
|
||||
int mPreferredType;
|
||||
|
||||
PreferredStartingTypeHelper(StartingWindowInfo taskInfo) {
|
||||
final int parameter = taskInfo.startingWindowTypeParameter;
|
||||
final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
|
||||
final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
|
||||
final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
|
||||
final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
|
||||
final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
|
||||
mPreferredType = preferredStartingWindowType(taskInfo, newTask, taskSwitch,
|
||||
processRunning, allowTaskSnapshot, activityCreated);
|
||||
}
|
||||
|
||||
// reference from ActivityRecord#getStartingWindowType
|
||||
private int preferredStartingWindowType(StartingWindowInfo windowInfo,
|
||||
boolean newTask, boolean taskSwitch, boolean processRunning,
|
||||
boolean allowTaskSnapshot, boolean activityCreated) {
|
||||
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
|
||||
Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
|
||||
+ " taskSwitch " + taskSwitch
|
||||
+ " processRunning " + processRunning
|
||||
+ " allowTaskSnapshot " + allowTaskSnapshot
|
||||
+ " activityCreated " + activityCreated);
|
||||
}
|
||||
|
||||
if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
|
||||
return STARTING_TYPE_SPLASH_SCREEN;
|
||||
} else if (taskSwitch && allowTaskSnapshot) {
|
||||
final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
|
||||
if (isSnapshotCompatible(windowInfo, snapshot)) {
|
||||
return STARTING_TYPE_SNAPSHOT;
|
||||
}
|
||||
if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
|
||||
return STARTING_TYPE_SPLASH_SCREEN;
|
||||
}
|
||||
return STARTING_TYPE_NO;
|
||||
} else {
|
||||
return STARTING_TYPE_NO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the task snapshot is compatible with this activity (at least the
|
||||
* rotation must be the same).
|
||||
*/
|
||||
private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) {
|
||||
if (snapshot == null) {
|
||||
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
|
||||
Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final int taskRotation = windowInfo.taskInfo.configuration
|
||||
.windowConfiguration.getRotation();
|
||||
final int snapshotRotation = snapshot.getRotation();
|
||||
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
|
||||
Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation
|
||||
+ " snapshot " + snapshotRotation);
|
||||
}
|
||||
return taskRotation == snapshotRotation;
|
||||
}
|
||||
|
||||
private TaskSnapshot getTaskSnapshot(int taskId) {
|
||||
if (mSnapshot != null) {
|
||||
return mSnapshot;
|
||||
}
|
||||
try {
|
||||
mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId,
|
||||
false/* isLowResolution */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e);
|
||||
return null;
|
||||
}
|
||||
return mSnapshot;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a task need a starting window.
|
||||
*/
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo taskInfo, IBinder appToken) {
|
||||
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
|
||||
final PreferredStartingTypeHelper helper =
|
||||
new PreferredStartingTypeHelper(windowInfo);
|
||||
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
|
||||
if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SPLASH_SCREEN) {
|
||||
addSplashScreenStartingWindow(runningTaskInfo, appToken);
|
||||
} else if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SNAPSHOT) {
|
||||
final TaskSnapshot snapshot = helper.mSnapshot;
|
||||
makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
|
||||
}
|
||||
// If prefer don't show, then don't show!
|
||||
}
|
||||
|
||||
private void addSplashScreenStartingWindow(RunningTaskInfo taskInfo, IBinder appToken) {
|
||||
final ActivityInfo activityInfo = taskInfo.topActivityInfo;
|
||||
if (activityInfo == null) {
|
||||
return;
|
||||
}
|
||||
final int displayId = taskInfo.displayId;
|
||||
if (activityInfo.packageName == null) {
|
||||
return;
|
||||
@@ -249,19 +359,35 @@ public class StartingSurfaceDrawer {
|
||||
postAddWindow(taskInfo.taskId, appToken, view, wm, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a task need a snapshot starting window.
|
||||
*/
|
||||
private void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo,
|
||||
IBinder appToken, TaskSnapshot snapshot) {
|
||||
final int taskId = startingWindowInfo.taskInfo.taskId;
|
||||
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
|
||||
snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
|
||||
mMainExecutor.execute(() -> {
|
||||
mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
|
||||
final StartingWindowRecord tView =
|
||||
new StartingWindowRecord(null/* decorView */, surface);
|
||||
mStartingWindowRecords.put(taskId, tView);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the content of a task is ready to show, starting window can be removed.
|
||||
*/
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
if (DEBUG_SPLASH_SCREEN) {
|
||||
Slog.d(TAG, "Task start finish, remove starting surface for task " + taskInfo.taskId);
|
||||
public void removeStartingWindow(int taskId) {
|
||||
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
|
||||
Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
|
||||
}
|
||||
mHandler.post(() -> removeWindowSynced(taskInfo.taskId));
|
||||
mMainExecutor.execute(() -> removeWindowSynced(taskId));
|
||||
}
|
||||
|
||||
protected void postAddWindow(int taskId, IBinder appToken,
|
||||
View view, WindowManager wm, WindowManager.LayoutParams params) {
|
||||
mHandler.post(() -> {
|
||||
mMainExecutor.execute(() -> {
|
||||
boolean shouldSaveView = true;
|
||||
try {
|
||||
wm.addView(view, params);
|
||||
@@ -286,25 +412,32 @@ public class StartingSurfaceDrawer {
|
||||
|
||||
if (shouldSaveView) {
|
||||
removeWindowSynced(taskId);
|
||||
mHandler.postDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
|
||||
final TaskScreenView tView = new TaskScreenView(view);
|
||||
mTaskScreenViews.put(taskId, tView);
|
||||
mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
|
||||
final StartingWindowRecord tView =
|
||||
new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
|
||||
mStartingWindowRecords.put(taskId, tView);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void removeWindowSynced(int taskId) {
|
||||
final TaskScreenView preView = mTaskScreenViews.get(taskId);
|
||||
if (preView != null) {
|
||||
if (preView.mDecorView != null) {
|
||||
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
||||
if (record != null) {
|
||||
if (record.mDecorView != null) {
|
||||
if (DEBUG_SPLASH_SCREEN) {
|
||||
Slog.v(TAG, "Removing splash screen window for task: " + taskId);
|
||||
}
|
||||
final WindowManager wm = preView.mDecorView.getContext()
|
||||
final WindowManager wm = record.mDecorView.getContext()
|
||||
.getSystemService(WindowManager.class);
|
||||
wm.removeView(preView.mDecorView);
|
||||
wm.removeView(record.mDecorView);
|
||||
}
|
||||
mTaskScreenViews.remove(taskId);
|
||||
if (record.mTaskSnapshotWindow != null) {
|
||||
if (DEBUG_TASK_SNAPSHOT) {
|
||||
Slog.v(TAG, "Removing task snapshot window for " + taskId);
|
||||
}
|
||||
record.mTaskSnapshotWindow.remove(mMainExecutor);
|
||||
}
|
||||
mStartingWindowRecords.remove(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,13 +448,15 @@ public class StartingSurfaceDrawer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the views in a starting window.
|
||||
* Record the view or surface for a starting window.
|
||||
*/
|
||||
private static class TaskScreenView {
|
||||
private static class StartingWindowRecord {
|
||||
private final View mDecorView;
|
||||
private final TaskSnapshotWindow mTaskSnapshotWindow;
|
||||
|
||||
TaskScreenView(View decorView) {
|
||||
StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
|
||||
mDecorView = decorView;
|
||||
mTaskSnapshotWindow = taskSnapshotWindow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,622 @@
|
||||
/*
|
||||
* 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.startingsurface;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.graphics.Color.WHITE;
|
||||
import static android.graphics.Color.alpha;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
|
||||
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
|
||||
import static com.android.internal.policy.DecorView.getNavigationBarRect;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.MergedConfiguration;
|
||||
import android.util.Slog;
|
||||
import android.view.IWindowSession;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.ClientWindowFrames;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.policy.DecorView;
|
||||
import com.android.internal.view.BaseIWindow;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
|
||||
/**
|
||||
* This class represents a starting window that shows a snapshot.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class TaskSnapshotWindow {
|
||||
|
||||
private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
|
||||
|
||||
/**
|
||||
* When creating the starting window, we use the exact same layout flags such that we end up
|
||||
* with a window with the exact same dimensions etc. However, these flags are not used in layout
|
||||
* and might cause other side effects so we exclude them.
|
||||
*/
|
||||
static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE
|
||||
| FLAG_NOT_TOUCH_MODAL
|
||||
| FLAG_ALT_FOCUSABLE_IM
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_HARDWARE_ACCELERATED
|
||||
| FLAG_IGNORE_CHEEK_PRESSES
|
||||
| FLAG_LOCAL_FOCUS_MODE
|
||||
| FLAG_SLIPPERY
|
||||
| FLAG_WATCH_OUTSIDE_TOUCH
|
||||
| FLAG_SPLIT_TOUCH
|
||||
| FLAG_SCALED
|
||||
| FLAG_SECURE;
|
||||
|
||||
private static final String TAG = StartingSurfaceDrawer.TAG;
|
||||
private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_TASK_SNAPSHOT;
|
||||
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
|
||||
|
||||
//tmp vars for unused relayout params
|
||||
private static final Point TMP_SURFACE_SIZE = new Point();
|
||||
|
||||
private final Window mWindow;
|
||||
private final Surface mSurface;
|
||||
private final Runnable mClearWindowHandler;
|
||||
private SurfaceControl mSurfaceControl;
|
||||
private SurfaceControl mChildSurfaceControl;
|
||||
private final IWindowSession mSession;
|
||||
private final Rect mTaskBounds;
|
||||
private final Rect mFrame = new Rect();
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
private TaskSnapshot mSnapshot;
|
||||
private final RectF mTmpSnapshotSize = new RectF();
|
||||
private final RectF mTmpDstFrame = new RectF();
|
||||
private final CharSequence mTitle;
|
||||
private boolean mHasDrawn;
|
||||
private long mShownTime;
|
||||
private boolean mSizeMismatch;
|
||||
private final Paint mBackgroundPaint = new Paint();
|
||||
private final int mActivityType;
|
||||
private final int mStatusBarColor;
|
||||
private final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
|
||||
private final int mOrientationOnCreation;
|
||||
private final SurfaceControl.Transaction mTransaction;
|
||||
private final Matrix mSnapshotMatrix = new Matrix();
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
|
||||
TaskSnapshot snapshot, ShellExecutor mainExecutor, Runnable clearWindowHandler) {
|
||||
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
|
||||
final int taskId = runningTaskInfo.taskId;
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "create taskSnapshot surface for task: " + taskId);
|
||||
}
|
||||
|
||||
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
||||
final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
|
||||
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
|
||||
if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
|
||||
Slog.w(TAG, "unable to create taskSnapshot surface for task: " + taskId);
|
||||
return null;
|
||||
}
|
||||
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
|
||||
|
||||
final int appearance = attrs.insetsFlags.appearance;
|
||||
final int windowFlags = attrs.flags;
|
||||
final int windowPrivateFlags = attrs.privateFlags;
|
||||
|
||||
layoutParams.packageName = mainWindowParams.packageName;
|
||||
layoutParams.windowAnimations = mainWindowParams.windowAnimations;
|
||||
layoutParams.dimAmount = mainWindowParams.dimAmount;
|
||||
layoutParams.type = TYPE_APPLICATION_STARTING;
|
||||
layoutParams.format = snapshot.getHardwareBuffer().getFormat();
|
||||
layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
|
||||
| FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE;
|
||||
// Setting as trusted overlay to let touches pass through. This is safe because this
|
||||
// window is controlled by the system.
|
||||
layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS)
|
||||
| PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
layoutParams.token = appToken;
|
||||
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.insetsFlags.appearance = appearance;
|
||||
layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
|
||||
layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
|
||||
layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
|
||||
layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
|
||||
layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
|
||||
|
||||
layoutParams.setTitle(String.format(TITLE_FORMAT, taskId));
|
||||
|
||||
final Point taskSize = snapshot.getTaskSize();
|
||||
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
|
||||
final int orientation = snapshot.getOrientation();
|
||||
|
||||
final int activityType = runningTaskInfo.topActivityType;
|
||||
final int displayId = runningTaskInfo.displayId;
|
||||
|
||||
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
||||
final SurfaceControl surfaceControl = new SurfaceControl();
|
||||
final ClientWindowFrames tmpFrames = new ClientWindowFrames();
|
||||
|
||||
final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0];
|
||||
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
|
||||
|
||||
final TaskDescription taskDescription;
|
||||
if (runningTaskInfo.taskDescription != null) {
|
||||
taskDescription = runningTaskInfo.taskDescription;
|
||||
} else {
|
||||
taskDescription = new TaskDescription();
|
||||
taskDescription.setBackgroundColor(WHITE);
|
||||
}
|
||||
|
||||
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
|
||||
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
|
||||
windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
|
||||
topWindowInsetsState, clearWindowHandler);
|
||||
final Window window = snapshotSurface.mWindow;
|
||||
|
||||
final InsetsState mTmpInsetsState = new InsetsState();
|
||||
final InputChannel tmpInputChannel = new InputChannel();
|
||||
mainExecutor.execute(() -> {
|
||||
try {
|
||||
final int res = session.addToDisplay(window, layoutParams, View.GONE,
|
||||
displayId, mTmpInsetsState, tmpFrames.frame,
|
||||
tmpFrames.displayCutout, tmpInputChannel/* outInputChannel */,
|
||||
mTmpInsetsState, mTempControls);
|
||||
if (res < 0) {
|
||||
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
snapshotSurface.clearWindowSynced();
|
||||
}
|
||||
window.setOuter(snapshotSurface, mainExecutor);
|
||||
try {
|
||||
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
|
||||
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
|
||||
mTempControls, TMP_SURFACE_SIZE);
|
||||
} catch (RemoteException e) {
|
||||
snapshotSurface.clearWindowSynced();
|
||||
}
|
||||
|
||||
final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState);
|
||||
snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
|
||||
snapshotSurface.drawSnapshot();
|
||||
});
|
||||
return snapshotSurface;
|
||||
}
|
||||
|
||||
public TaskSnapshotWindow(SurfaceControl surfaceControl,
|
||||
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
|
||||
int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
|
||||
int currentOrientation, int activityType, InsetsState topWindowInsetsState,
|
||||
Runnable clearWindowHandler) {
|
||||
mSurface = new Surface();
|
||||
mSession = WindowManagerGlobal.getWindowSession();
|
||||
mWindow = new Window();
|
||||
mWindow.setSession(mSession);
|
||||
mSurfaceControl = surfaceControl;
|
||||
mSnapshot = snapshot;
|
||||
mTitle = title;
|
||||
int backgroundColor = taskDescription.getBackgroundColor();
|
||||
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
||||
mTaskBounds = taskBounds;
|
||||
mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
|
||||
windowPrivateFlags, appearance, taskDescription, 1f, topWindowInsetsState);
|
||||
mStatusBarColor = taskDescription.getStatusBarColor();
|
||||
mOrientationOnCreation = currentOrientation;
|
||||
mActivityType = activityType;
|
||||
mTransaction = new SurfaceControl.Transaction();
|
||||
mClearWindowHandler = clearWindowHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask system bar background painter to draw status bar background.
|
||||
* @hide
|
||||
*/
|
||||
public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
|
||||
mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask system bar background painter to draw navigation bar background.
|
||||
* @hide
|
||||
*/
|
||||
public void drawNavigationBarBackground(Canvas c) {
|
||||
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
void remove(ShellExecutor mainExecutor) {
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
|
||||
// Show the latest content as soon as possible for unlocking to home.
|
||||
&& mActivityType != ACTIVITY_TYPE_HOME) {
|
||||
final long delayTime = mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS - now;
|
||||
mainExecutor.executeDelayed(() -> remove(mainExecutor), delayTime);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
|
||||
}
|
||||
return;
|
||||
}
|
||||
mainExecutor.execute(() -> {
|
||||
try {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
|
||||
}
|
||||
mSession.remove(mWindow);
|
||||
} catch (RemoteException e) {
|
||||
// nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set frame size.
|
||||
* @hide
|
||||
*/
|
||||
public void setFrames(Rect frame, Rect systemBarInsets) {
|
||||
mFrame.set(frame);
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
mSizeMismatch = (mFrame.width() != snapshot.getWidth()
|
||||
|| mFrame.height() != snapshot.getHeight());
|
||||
mSystemBarBackgroundPainter.setInsets(systemBarInsets);
|
||||
}
|
||||
|
||||
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
|
||||
return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
|
||||
false /* ignoreVisibility */);
|
||||
}
|
||||
|
||||
private void drawSnapshot() {
|
||||
mSurface.copyFrom(mSurfaceControl);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Drawing snapshot surface sizeMismatch= " + mSizeMismatch);
|
||||
}
|
||||
if (mSizeMismatch) {
|
||||
// The dimensions of the buffer and the window don't match, so attaching the buffer
|
||||
// will fail. Better create a child window with the exact dimensions and fill the parent
|
||||
// window with the background color!
|
||||
drawSizeMismatchSnapshot();
|
||||
} else {
|
||||
drawSizeMatchSnapshot();
|
||||
}
|
||||
mShownTime = SystemClock.uptimeMillis();
|
||||
mHasDrawn = true;
|
||||
reportDrawn();
|
||||
|
||||
// In case window manager leaks us, make sure we don't retain the snapshot.
|
||||
mSnapshot = null;
|
||||
}
|
||||
|
||||
private void drawSizeMatchSnapshot() {
|
||||
mSurface.attachAndQueueBufferWithColorSpace(mSnapshot.getHardwareBuffer(),
|
||||
mSnapshot.getColorSpace());
|
||||
mSurface.release();
|
||||
}
|
||||
|
||||
private void drawSizeMismatchSnapshot() {
|
||||
if (!mSurface.isValid()) {
|
||||
throw new IllegalStateException("mSurface does not hold a valid surface.");
|
||||
}
|
||||
final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
|
||||
final SurfaceSession session = new SurfaceSession();
|
||||
|
||||
// We consider nearly matched dimensions as there can be rounding errors and the user won't
|
||||
// notice very minute differences from scaling one dimension more than the other
|
||||
final boolean aspectRatioMismatch = Math.abs(
|
||||
((float) buffer.getWidth() / buffer.getHeight())
|
||||
- ((float) mFrame.width() / mFrame.height())) > 0.01f;
|
||||
|
||||
// Keep a reference to it such that it doesn't get destroyed when finalized.
|
||||
mChildSurfaceControl = new SurfaceControl.Builder(session)
|
||||
.setName(mTitle + " - task-snapshot-surface")
|
||||
.setBufferSize(buffer.getWidth(), buffer.getHeight())
|
||||
.setFormat(buffer.getFormat())
|
||||
.setParent(mSurfaceControl)
|
||||
.setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
|
||||
.build();
|
||||
Surface surface = new Surface();
|
||||
surface.copyFrom(mChildSurfaceControl);
|
||||
|
||||
final Rect frame;
|
||||
// We can just show the surface here as it will still be hidden as the parent is
|
||||
// still hidden.
|
||||
mTransaction.show(mChildSurfaceControl);
|
||||
if (aspectRatioMismatch) {
|
||||
// Clip off ugly navigation bar.
|
||||
final Rect crop = calculateSnapshotCrop();
|
||||
frame = calculateSnapshotFrame(crop);
|
||||
mTransaction.setWindowCrop(mChildSurfaceControl, crop);
|
||||
mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top);
|
||||
mTmpSnapshotSize.set(crop);
|
||||
mTmpDstFrame.set(frame);
|
||||
} else {
|
||||
frame = null;
|
||||
mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
mTmpDstFrame.set(mFrame);
|
||||
mTmpDstFrame.offsetTo(0, 0);
|
||||
}
|
||||
|
||||
// Scale the mismatch dimensions to fill the task bounds
|
||||
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
|
||||
mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9);
|
||||
|
||||
mTransaction.apply();
|
||||
surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
|
||||
surface.release();
|
||||
|
||||
if (aspectRatioMismatch) {
|
||||
final Canvas c = mSurface.lockCanvas(null);
|
||||
drawBackgroundAndBars(c, frame);
|
||||
mSurface.unlockCanvasAndPost(c);
|
||||
mSurface.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot crop in snapshot coordinate space.
|
||||
*
|
||||
* @return crop rect in snapshot coordinate space.
|
||||
*/
|
||||
public Rect calculateSnapshotCrop() {
|
||||
final Rect rect = new Rect();
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight());
|
||||
final Rect insets = mSnapshot.getContentInsets();
|
||||
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Let's remove all system decorations except the status bar, but only if the task is at the
|
||||
// very top of the screen.
|
||||
final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
|
||||
rect.inset((int) (insets.left * scaleX),
|
||||
isTop ? 0 : (int) (insets.top * scaleY),
|
||||
(int) (insets.right * scaleX),
|
||||
(int) (insets.bottom * scaleY));
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the snapshot frame in window coordinate space from crop.
|
||||
*
|
||||
* @param crop rect that is in snapshot coordinate space.
|
||||
*/
|
||||
public Rect calculateSnapshotFrame(Rect crop) {
|
||||
final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer();
|
||||
final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x;
|
||||
final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y;
|
||||
|
||||
// Rescale the frame from snapshot to window coordinate space
|
||||
final Rect frame = new Rect(0, 0,
|
||||
(int) (crop.width() / scaleX + 0.5f),
|
||||
(int) (crop.height() / scaleY + 0.5f)
|
||||
);
|
||||
|
||||
// However, we also need to make space for the navigation bar on the left side.
|
||||
frame.offset(mSystemBarInsets.left, 0);
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw status bar and navigation bar background.
|
||||
* @hide
|
||||
*/
|
||||
public void drawBackgroundAndBars(Canvas c, Rect frame) {
|
||||
final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
|
||||
final boolean fillHorizontally = c.getWidth() > frame.right;
|
||||
final boolean fillVertically = c.getHeight() > frame.bottom;
|
||||
if (fillHorizontally) {
|
||||
c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
|
||||
c.getWidth(), fillVertically
|
||||
? frame.bottom
|
||||
: c.getHeight(),
|
||||
mBackgroundPaint);
|
||||
}
|
||||
if (fillVertically) {
|
||||
c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
|
||||
}
|
||||
mSystemBarBackgroundPainter.drawDecors(c, frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear window from drawer, must be post on main executor.
|
||||
*/
|
||||
private void clearWindowSynced() {
|
||||
if (mClearWindowHandler != null) {
|
||||
mClearWindowHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void reportDrawn() {
|
||||
try {
|
||||
mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
|
||||
} catch (RemoteException e) {
|
||||
clearWindowSynced();
|
||||
}
|
||||
}
|
||||
|
||||
static class Window extends BaseIWindow {
|
||||
private TaskSnapshotWindow mOuter;
|
||||
private ShellExecutor mMainExecutor;
|
||||
|
||||
public void setOuter(TaskSnapshotWindow outer, ShellExecutor mainExecutor) {
|
||||
mOuter = outer;
|
||||
mMainExecutor = mainExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resized(ClientWindowFrames frames, boolean reportDraw,
|
||||
MergedConfiguration mergedConfiguration, boolean forceLayout,
|
||||
boolean alwaysConsumeSystemBars, int displayId) {
|
||||
if (mOuter != null) {
|
||||
if (mergedConfiguration != null
|
||||
&& mOuter.mOrientationOnCreation
|
||||
!= mergedConfiguration.getMergedConfiguration().orientation) {
|
||||
// The orientation of the screen is changing. We better remove the snapshot ASAP
|
||||
// as we are going to wait on the new window in any case to unfreeze the screen,
|
||||
// and the starting window is not needed anymore.
|
||||
mMainExecutor.execute(() -> {
|
||||
mOuter.clearWindowSynced();
|
||||
});
|
||||
} else if (reportDraw) {
|
||||
mMainExecutor.execute(() -> {
|
||||
if (mOuter.mHasDrawn) {
|
||||
mOuter.reportDrawn();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to draw the background of the system bars in regions the task snapshot isn't
|
||||
* filling the window.
|
||||
*/
|
||||
static class SystemBarBackgroundPainter {
|
||||
private final Paint mStatusBarPaint = new Paint();
|
||||
private final Paint mNavigationBarPaint = new Paint();
|
||||
private final int mStatusBarColor;
|
||||
private final int mNavigationBarColor;
|
||||
private final int mWindowFlags;
|
||||
private final int mWindowPrivateFlags;
|
||||
private final float mScale;
|
||||
private final InsetsState mInsetsState;
|
||||
private final Rect mSystemBarInsets = new Rect();
|
||||
|
||||
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
|
||||
TaskDescription taskDescription, float scale, InsetsState insetsState) {
|
||||
mWindowFlags = windowFlags;
|
||||
mWindowPrivateFlags = windowPrivateFlags;
|
||||
mScale = scale;
|
||||
final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
|
||||
final int semiTransparent = context.getColor(
|
||||
R.color.system_bar_background_semi_transparent);
|
||||
mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
|
||||
semiTransparent, taskDescription.getStatusBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_STATUS_BARS,
|
||||
taskDescription.getEnsureStatusBarContrastWhenTransparent());
|
||||
mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
|
||||
FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
|
||||
taskDescription.getNavigationBarColor(), appearance,
|
||||
APPEARANCE_LIGHT_NAVIGATION_BARS,
|
||||
taskDescription.getEnsureNavigationBarContrastWhenTransparent()
|
||||
&& context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
|
||||
mStatusBarPaint.setColor(mStatusBarColor);
|
||||
mNavigationBarPaint.setColor(mNavigationBarColor);
|
||||
mInsetsState = insetsState;
|
||||
}
|
||||
|
||||
void setInsets(Rect systemBarInsets) {
|
||||
mSystemBarInsets.set(systemBarInsets);
|
||||
}
|
||||
|
||||
int getStatusBarColorViewHeight() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) {
|
||||
return (int) (mSystemBarInsets.top * mScale);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNavigationBarColorViewVisible() {
|
||||
final boolean forceBarBackground =
|
||||
(mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
|
||||
return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
|
||||
mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground);
|
||||
}
|
||||
|
||||
void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
|
||||
drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
|
||||
drawNavigationBarBackground(c);
|
||||
}
|
||||
|
||||
void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
|
||||
int statusBarHeight) {
|
||||
if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
|
||||
&& (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
|
||||
final int rightInset = (int) (mSystemBarInsets.right * mScale);
|
||||
final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
|
||||
c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void drawNavigationBarBackground(Canvas c) {
|
||||
final Rect navigationBarRect = new Rect();
|
||||
getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
|
||||
mScale);
|
||||
final boolean visible = isNavigationBarColorViewVisible();
|
||||
if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
|
||||
c.drawRect(navigationBarRect, mNavigationBarPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,14 @@ import android.testing.TestableContext;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
import android.window.StartingWindowInfo;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.wm.shell.common.HandlerExecutor;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -70,8 +73,8 @@ public class StartingSurfaceDrawerTests {
|
||||
static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{
|
||||
int mAddWindowForTask = 0;
|
||||
|
||||
TestStartingSurfaceDrawer(Context context) {
|
||||
super(context);
|
||||
TestStartingSurfaceDrawer(Context context, ShellExecutor executor) {
|
||||
super(context, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,36 +112,38 @@ public class StartingSurfaceDrawerTests {
|
||||
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
|
||||
doNothing().when(mMockWindowManager).addView(any(), any());
|
||||
|
||||
mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context));
|
||||
mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context,
|
||||
new HandlerExecutor(new Handler(Looper.getMainLooper()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSplashScreenSurface() {
|
||||
final int taskId = 1;
|
||||
final Handler mainLoop = new Handler(Looper.getMainLooper());
|
||||
final ActivityManager.RunningTaskInfo taskInfo =
|
||||
createTaskInfo(taskId, WINDOWING_MODE_FULLSCREEN);
|
||||
mStartingSurfaceDrawer.addStartingWindow(taskInfo, mBinder);
|
||||
final StartingWindowInfo windowInfo =
|
||||
createWindowInfo(taskId, WINDOWING_MODE_FULLSCREEN);
|
||||
mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder);
|
||||
waitHandlerIdle(mainLoop);
|
||||
verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
|
||||
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
|
||||
|
||||
mStartingSurfaceDrawer.removeStartingWindow(taskInfo);
|
||||
mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId);
|
||||
waitHandlerIdle(mainLoop);
|
||||
verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId));
|
||||
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
|
||||
}
|
||||
|
||||
private ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
|
||||
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
|
||||
private StartingWindowInfo createWindowInfo(int taskId, int windowingMode) {
|
||||
StartingWindowInfo windowInfo = new StartingWindowInfo();
|
||||
final ActivityInfo info = new ActivityInfo();
|
||||
info.applicationInfo = new ApplicationInfo();
|
||||
info.packageName = "test";
|
||||
info.theme = android.R.style.Theme;
|
||||
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
|
||||
taskInfo.topActivityInfo = info;
|
||||
taskInfo.taskId = taskId;
|
||||
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
|
||||
return taskInfo;
|
||||
windowInfo.taskInfo = taskInfo;
|
||||
return windowInfo;
|
||||
}
|
||||
|
||||
private static void waitHandlerIdle(Handler handler) {
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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 unittest.src.com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
|
||||
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.verify;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.content.ComponentName;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorSpace;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.view.InsetsState;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test class for {@link TaskSnapshotWindow}.
|
||||
*
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TaskSnapshotWindowTest {
|
||||
|
||||
private TaskSnapshotWindow mWindow;
|
||||
|
||||
private void setupSurface(int width, int height) {
|
||||
setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, width, height));
|
||||
}
|
||||
|
||||
private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis,
|
||||
int windowFlags, Rect taskBounds) {
|
||||
// Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic
|
||||
// this behavior set the taskSize to be the same as the taskBounds width and height. The
|
||||
// taskBounds passed here are assumed to be the same task bounds as when the snapshot was
|
||||
// taken. We assume there is no aspect ratio mismatch between the screenshot and the
|
||||
// taskBounds
|
||||
assertEquals(width, taskBounds.width());
|
||||
assertEquals(height, taskBounds.height());
|
||||
Point taskSize = new Point(taskBounds.width(), taskBounds.height());
|
||||
|
||||
final TaskSnapshot snapshot = createTaskSnapshot(width, height, taskSize, contentInsets);
|
||||
mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test",
|
||||
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
|
||||
0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
|
||||
taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState(),
|
||||
null /* clearWindow */);
|
||||
}
|
||||
|
||||
private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
|
||||
Rect contentInsets) {
|
||||
final HardwareBuffer buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888,
|
||||
1, HardwareBuffer.USAGE_CPU_READ_RARELY);
|
||||
return new TaskSnapshot(
|
||||
System.currentTimeMillis(),
|
||||
new ComponentName("", ""), buffer,
|
||||
ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT,
|
||||
Surface.ROTATION_0, taskSize, contentInsets, false,
|
||||
true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN,
|
||||
0 /* systemUiVisibility */, false /* isTranslucent */);
|
||||
}
|
||||
|
||||
private static TaskDescription createTaskDescription(int background, int statusBar,
|
||||
int navigationBar) {
|
||||
final TaskDescription td = new TaskDescription();
|
||||
td.setBackgroundColor(background);
|
||||
td.setStatusBarColor(statusBar);
|
||||
td.setNavigationBarColor(navigationBar);
|
||||
return td;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_fillHorizontally() {
|
||||
setupSurface(200, 100);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(200);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200));
|
||||
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_fillVertically() {
|
||||
setupSurface(100, 200);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(200);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100));
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_fillBoth() {
|
||||
setupSurface(200, 200);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(200);
|
||||
when(mockCanvas.getHeight()).thenReturn(200);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any());
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_dontFill_sameSize() {
|
||||
setupSurface(100, 100);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillEmptyBackground_dontFill_bitmapLarger() {
|
||||
setupSurface(100, 100);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 100, 90), mWindow.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_taskNotOnTop() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150));
|
||||
assertEquals(new Rect(0, 10, 100, 90), mWindow.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_navBarLeft() {
|
||||
setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(10, 0, 100, 100), mWindow.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_navBarRight() {
|
||||
setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(0, 0, 90, 100), mWindow.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotCrop_waterfall() {
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
assertEquals(new Rect(5, 0, 95, 90), mWindow.calculateSnapshotCrop());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
assertEquals(new Rect(0, 0, 100, 80),
|
||||
mWindow.calculateSnapshotFrame(new Rect(0, 10, 100, 90)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame_navBarLeft() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(10, 10, 0, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
assertEquals(new Rect(10, 0, 100, 90),
|
||||
mWindow.calculateSnapshotFrame(new Rect(10, 10, 100, 100)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculateSnapshotFrame_waterfall() {
|
||||
setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100));
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
mWindow.setFrames(new Rect(5, 0, 95, 100), insets);
|
||||
assertEquals(new Rect(0, 0, 90, 90),
|
||||
mWindow.calculateSnapshotFrame(new Rect(5, 0, 95, 90)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100));
|
||||
verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground_nullFrame() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawStatusBarBackground(mockCanvas, null);
|
||||
verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawStatusBarBackground_nope() {
|
||||
setupSurface(100, 100);
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100));
|
||||
verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground() {
|
||||
final Rect insets = new Rect(0, 10, 0, 10);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground_left() {
|
||||
final Rect insets = new Rect(10, 10, 0, 0);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrawNavigationBarBackground_right() {
|
||||
final Rect insets = new Rect(0, 10, 10, 0);
|
||||
setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
|
||||
new Rect(0, 0, 100, 100));
|
||||
mWindow.setFrames(new Rect(0, 0, 100, 100), insets);
|
||||
final Canvas mockCanvas = mock(Canvas.class);
|
||||
when(mockCanvas.getWidth()).thenReturn(100);
|
||||
when(mockCanvas.getHeight()).thenReturn(100);
|
||||
mWindow.drawNavigationBarBackground(mockCanvas);
|
||||
verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any());
|
||||
}
|
||||
}
|
||||
@@ -1759,6 +1759,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
final TaskSnapshot snapshot =
|
||||
mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId,
|
||||
false /* restoreFromDisk */, false /* isLowResolution */);
|
||||
final int typeParameter = mWmService.mStartingSurfaceController
|
||||
.makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
|
||||
allowTaskSnapshot, activityCreated);
|
||||
final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
|
||||
allowTaskSnapshot, activityCreated, snapshot);
|
||||
|
||||
@@ -1773,7 +1776,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return createSnapshot(snapshot);
|
||||
return createSnapshot(snapshot, typeParameter);
|
||||
}
|
||||
|
||||
// If this is a translucent window, then don't show a starting window -- the current
|
||||
@@ -1833,18 +1836,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SplashScreenStartingData");
|
||||
mStartingData = new SplashScreenStartingData(mWmService, pkg,
|
||||
theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
|
||||
getMergedOverrideConfiguration());
|
||||
getMergedOverrideConfiguration(), typeParameter);
|
||||
scheduleAddStartingWindow();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean createSnapshot(TaskSnapshot snapshot) {
|
||||
private boolean createSnapshot(TaskSnapshot snapshot, int typeParams) {
|
||||
if (snapshot == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
|
||||
mStartingData = new SnapshotStartingData(mWmService, snapshot);
|
||||
mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
|
||||
scheduleAddStartingWindow();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,14 +28,15 @@ class SnapshotStartingData extends StartingData {
|
||||
private final WindowManagerService mService;
|
||||
private final TaskSnapshot mSnapshot;
|
||||
|
||||
SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot) {
|
||||
super(service);
|
||||
SnapshotStartingData(WindowManagerService service, TaskSnapshot snapshot, int typeParams) {
|
||||
super(service, typeParams);
|
||||
mService = service;
|
||||
mSnapshot = snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
StartingSurface createStartingSurface(ActivityRecord activity) {
|
||||
return mService.mTaskSnapshotController.createStartingSurface(activity, mSnapshot);
|
||||
return mService.mStartingSurfaceController.createTaskSnapshotSurface(activity,
|
||||
mSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ class SplashScreenStartingData extends StartingData {
|
||||
|
||||
SplashScreenStartingData(WindowManagerService service, String pkg, int theme,
|
||||
CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
|
||||
int logo, int windowFlags, Configuration mergedOverrideConfiguration) {
|
||||
super(service);
|
||||
int logo, int windowFlags, Configuration mergedOverrideConfiguration, int typeParams) {
|
||||
super(service, typeParams);
|
||||
mPkg = pkg;
|
||||
mTheme = theme;
|
||||
mCompatInfo = compatInfo;
|
||||
|
||||
@@ -24,9 +24,11 @@ import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
public abstract class StartingData {
|
||||
|
||||
protected final WindowManagerService mService;
|
||||
protected final int mTypeParams;
|
||||
|
||||
protected StartingData(WindowManagerService service) {
|
||||
protected StartingData(WindowManagerService service, int typeParams) {
|
||||
mService = service;
|
||||
mTypeParams = typeParams;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,9 +16,20 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
|
||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
|
||||
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Slog;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
|
||||
@@ -26,7 +37,8 @@ import com.android.server.policy.WindowManagerPolicy.StartingSurface;
|
||||
* Managing to create and release a starting window surface.
|
||||
*/
|
||||
public class StartingSurfaceController {
|
||||
|
||||
private static final String TAG = TAG_WITH_CLASS_NAME
|
||||
? StartingSurfaceController.class.getSimpleName() : TAG_WM;
|
||||
/** Set to {@code true} to enable shell starting surface drawer. */
|
||||
private static final boolean DEBUG_ENABLE_SHELL_DRAWER =
|
||||
SystemProperties.getBoolean("persist.debug.shell_starting_surface", false);
|
||||
@@ -49,15 +61,79 @@ public class StartingSurfaceController {
|
||||
final Task task = activity.getTask();
|
||||
if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
|
||||
activity.token)) {
|
||||
return new SplashScreenContainerSurface(task);
|
||||
return new ShellStartingSurface(task);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final class SplashScreenContainerSurface implements StartingSurface {
|
||||
int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
|
||||
boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated) {
|
||||
int parameter = 0;
|
||||
if (newTask) {
|
||||
parameter |= TYPE_PARAMETER_NEW_TASK;
|
||||
}
|
||||
if (taskSwitch) {
|
||||
parameter |= TYPE_PARAMETER_TASK_SWITCH;
|
||||
}
|
||||
if (processRunning) {
|
||||
parameter |= TYPE_PARAMETER_PROCESS_RUNNING;
|
||||
}
|
||||
if (allowTaskSnapshot) {
|
||||
parameter |= TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
|
||||
}
|
||||
if (activityCreated) {
|
||||
parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
|
||||
StartingSurface createTaskSnapshotSurface(ActivityRecord activity, TaskSnapshot taskSnapshot) {
|
||||
final WindowState topFullscreenOpaqueWindow;
|
||||
final Task task;
|
||||
synchronized (mService.mGlobalLock) {
|
||||
final WindowState mainWindow = activity.findMainWindow();
|
||||
task = activity.getTask();
|
||||
if (task == null) {
|
||||
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity="
|
||||
+ activity);
|
||||
return null;
|
||||
}
|
||||
final ActivityRecord topFullscreenActivity =
|
||||
activity.getTask().getTopFullscreenActivity();
|
||||
if (topFullscreenActivity == null) {
|
||||
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
|
||||
+ task);
|
||||
return null;
|
||||
}
|
||||
topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow();
|
||||
if (mainWindow == null || topFullscreenOpaqueWindow == null) {
|
||||
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for activity="
|
||||
+ activity);
|
||||
return null;
|
||||
}
|
||||
if (topFullscreenActivity.getWindowConfiguration().getRotation()
|
||||
!= taskSnapshot.getRotation()) {
|
||||
// The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
|
||||
// that the activity will be updated to the same rotation as the snapshot. Since
|
||||
// the transition is not started yet, fixed rotation transform needs to be applied
|
||||
// earlier to make the snapshot show in a rotated container.
|
||||
activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
|
||||
topFullscreenActivity, false /* checkOpening */);
|
||||
}
|
||||
}
|
||||
if (!DEBUG_ENABLE_SHELL_DRAWER) {
|
||||
return mService.mTaskSnapshotController
|
||||
.createStartingSurface(activity, taskSnapshot);
|
||||
}
|
||||
mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, activity.token);
|
||||
return new ShellStartingSurface(task);
|
||||
}
|
||||
|
||||
|
||||
private final class ShellStartingSurface implements StartingSurface {
|
||||
private final Task mTask;
|
||||
|
||||
SplashScreenContainerSurface(Task task) {
|
||||
ShellStartingSurface(Task task) {
|
||||
mTask = task;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,6 @@ import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.AppGlobals;
|
||||
@@ -207,6 +206,8 @@ import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.TransitionOldType;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -4145,6 +4146,34 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
return info;
|
||||
}
|
||||
|
||||
StartingWindowInfo getStartingWindowInfo() {
|
||||
final StartingWindowInfo info = new StartingWindowInfo();
|
||||
info.taskInfo = getTaskInfo();
|
||||
|
||||
final ActivityRecord topActivity = getTopMostActivity();
|
||||
if (topActivity != null) {
|
||||
info.startingWindowTypeParameter =
|
||||
topActivity.mStartingData != null
|
||||
? topActivity.mStartingData.mTypeParams
|
||||
: 0;
|
||||
final WindowState mainWindow = topActivity.findMainWindow();
|
||||
if (mainWindow != null) {
|
||||
info.mainWindowLayoutParams = mainWindow.getAttrs();
|
||||
}
|
||||
}
|
||||
final ActivityRecord topFullscreenActivity = getTopFullscreenActivity();
|
||||
if (topFullscreenActivity != null) {
|
||||
final WindowState topFullscreenOpaqueWindow =
|
||||
topFullscreenActivity.getTopFullscreenOpaqueWindow();
|
||||
if (topFullscreenOpaqueWindow != null) {
|
||||
info.topOpaqueWindowInsetsState =
|
||||
topFullscreenOpaqueWindow.getInsetsStateWithVisibilityOverride();
|
||||
info.topOpaqueWindowLayoutParams = topFullscreenOpaqueWindow.getAttrs();
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
boolean isTaskId(int taskId) {
|
||||
return mTaskId == taskId;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.util.Slog;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.ITaskOrganizerController;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskAppearedInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
|
||||
@@ -116,10 +117,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
void addStartingWindow(Task task, IBinder appToken) {
|
||||
final RunningTaskInfo taskInfo = task.getTaskInfo();
|
||||
final StartingWindowInfo info = task.getStartingWindowInfo();
|
||||
mDeferTaskOrgCallbacksConsumer.accept(() -> {
|
||||
try {
|
||||
mTaskOrganizer.addStartingWindow(taskInfo, appToken);
|
||||
mTaskOrganizer.addStartingWindow(info, appToken);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Exception sending onTaskStart callback", e);
|
||||
}
|
||||
@@ -127,10 +128,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
||||
}
|
||||
|
||||
void removeStartingWindow(Task task) {
|
||||
final RunningTaskInfo taskInfo = task.getTaskInfo();
|
||||
mDeferTaskOrgCallbacksConsumer.accept(() -> {
|
||||
try {
|
||||
mTaskOrganizer.removeStartingWindow(taskInfo);
|
||||
mTaskOrganizer.removeStartingWindow(task.mTaskId);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
@@ -78,6 +77,7 @@ import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.ClientWindowFrames;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -100,7 +100,7 @@ class TaskSnapshotSurface implements StartingSurface {
|
||||
* with a window with the exact same dimensions etc. However, these flags are not used in layout
|
||||
* and might cause other side effects so we exclude them.
|
||||
*/
|
||||
private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
|
||||
static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
|
||||
| FLAG_NOT_TOUCHABLE
|
||||
| FLAG_NOT_TOUCH_MODAL
|
||||
| FLAG_ALT_FOCUSABLE_IM
|
||||
@@ -180,34 +180,10 @@ class TaskSnapshotSurface implements StartingSurface {
|
||||
synchronized (service.mGlobalLock) {
|
||||
final WindowState mainWindow = activity.findMainWindow();
|
||||
final Task task = activity.getTask();
|
||||
if (task == null) {
|
||||
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity="
|
||||
+ activity);
|
||||
return null;
|
||||
}
|
||||
final ActivityRecord topFullscreenActivity =
|
||||
activity.getTask().getTopFullscreenActivity();
|
||||
if (topFullscreenActivity == null) {
|
||||
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
|
||||
+ task);
|
||||
return null;
|
||||
}
|
||||
// Already check the nullity in StartingSurfaceController#createTaskSnapshotSurface
|
||||
topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow();
|
||||
if (mainWindow == null || topFullscreenOpaqueWindow == null) {
|
||||
Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for activity="
|
||||
+ activity);
|
||||
return null;
|
||||
}
|
||||
if (topFullscreenActivity.getWindowConfiguration().getRotation()
|
||||
!= snapshot.getRotation()) {
|
||||
// The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
|
||||
// that the activity will be updated to the same rotation as the snapshot. Since
|
||||
// the transition is not started yet, fixed rotation transform needs to be applied
|
||||
// earlier to make the snapshot show in a rotated container.
|
||||
activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
|
||||
topFullscreenActivity, false /* checkOpening */);
|
||||
}
|
||||
|
||||
WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs;
|
||||
appearance = attrs.insetsFlags.appearance;
|
||||
windowFlags = attrs.flags;
|
||||
|
||||
@@ -75,7 +75,6 @@ import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
import android.window.TaskSnapshot;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.app.servertransaction.ActivityConfigurationChangeItem;
|
||||
@@ -102,6 +101,7 @@ import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.window.TaskSnapshot;
|
||||
|
||||
import androidx.test.filters.MediumTest;
|
||||
|
||||
@@ -1662,7 +1662,8 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
any() /* requestedVisibility */, any() /* outFrame */,
|
||||
any() /* outDisplayCutout */, any() /* outInputChannel */,
|
||||
any() /* outInsetsState */, any() /* outActiveControls */);
|
||||
TaskSnapshotSurface.create(mAtm.mWindowManager, activity, snapshot);
|
||||
mAtm.mWindowManager.mStartingSurfaceController
|
||||
.createTaskSnapshotSurface(activity, snapshot);
|
||||
} catch (RemoteException ignored) {
|
||||
} finally {
|
||||
reset(session);
|
||||
|
||||
@@ -74,6 +74,7 @@ import android.view.Display;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.IWindowContainerTransactionCallback;
|
||||
import android.window.StartingWindowInfo;
|
||||
import android.window.TaskAppearedInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
@@ -506,12 +507,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
public void testTileAddRemoveChild() {
|
||||
ITaskOrganizer listener = new ITaskOrganizer.Stub() {
|
||||
@Override
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) {
|
||||
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { }
|
||||
public void removeStartingWindow(int taskId) { }
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
|
||||
@@ -571,12 +572,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
final boolean[] called = {false};
|
||||
ITaskOrganizer listener = new ITaskOrganizer.Stub() {
|
||||
@Override
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) {
|
||||
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { }
|
||||
public void removeStartingWindow(int taskId) { }
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
|
||||
@@ -642,12 +643,12 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
|
||||
ITaskOrganizer listener = new ITaskOrganizer.Stub() {
|
||||
@Override
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) {
|
||||
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { }
|
||||
public void removeStartingWindow(int taskId) { }
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { }
|
||||
@@ -786,9 +787,9 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
RunningTaskInfo mInfo;
|
||||
|
||||
@Override
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) { }
|
||||
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
|
||||
@Override
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { }
|
||||
public void removeStartingWindow(int taskId) { }
|
||||
@Override
|
||||
public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) {
|
||||
mInfo = info;
|
||||
|
||||
@@ -84,6 +84,7 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.DisplayImePolicy;
|
||||
import android.window.ITaskOrganizer;
|
||||
import android.window.StartingWindowInfo;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.server.AttributeCache;
|
||||
@@ -1090,10 +1091,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
mMoveToSecondaryOnEnter = move;
|
||||
}
|
||||
@Override
|
||||
public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) {
|
||||
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||
}
|
||||
@Override
|
||||
public void removeStartingWindow(ActivityManager.RunningTaskInfo info) {
|
||||
public void removeStartingWindow(int taskId) {
|
||||
}
|
||||
@Override
|
||||
public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
|
||||
|
||||
Reference in New Issue
Block a user