Merge "Revert "Introduce Windowless starting window."" into udc-dev am: f515de9517
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21590159 Change-Id: I1f1dbad0af46ce639e4c948bf70a2d0ab640bfa7 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -34,8 +34,9 @@ oneway interface ITaskOrganizer {
|
|||||||
* has create a starting window for the Task.
|
* has create a starting window for the Task.
|
||||||
*
|
*
|
||||||
* @param info 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 StartingWindowInfo info);
|
void addStartingWindow(in StartingWindowInfo info, IBinder appToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the Task want to remove the starting window.
|
* Called when the Task want to remove the starting window.
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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.view.SurfaceControl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface to be invoked when a windowless starting surface added.
|
|
||||||
*
|
|
||||||
* @param addedSurface The starting surface.
|
|
||||||
* {@hide}
|
|
||||||
*/
|
|
||||||
interface IWindowlessStartingSurfaceCallback {
|
|
||||||
void onSurfaceAdded(in SurfaceControl addedSurface);
|
|
||||||
}
|
|
||||||
@@ -328,21 +328,6 @@ public class SnapshotDrawerUtils {
|
|||||||
- ((float) frame.width() / frame.height())) <= 0.01f;
|
- ((float) frame.width() / frame.height())) <= 0.01f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create a TaskDescription from a RunningTaskInfo.
|
|
||||||
*/
|
|
||||||
public static ActivityManager.TaskDescription getOrCreateTaskDescription(
|
|
||||||
ActivityManager.RunningTaskInfo runningTaskInfo) {
|
|
||||||
final ActivityManager.TaskDescription taskDescription;
|
|
||||||
if (runningTaskInfo.taskDescription != null) {
|
|
||||||
taskDescription = runningTaskInfo.taskDescription;
|
|
||||||
} else {
|
|
||||||
taskDescription = new ActivityManager.TaskDescription();
|
|
||||||
taskDescription.setBackgroundColor(WHITE);
|
|
||||||
}
|
|
||||||
return taskDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Help method to draw the snapshot on a surface.
|
* Help method to draw the snapshot on a surface.
|
||||||
*/
|
*/
|
||||||
@@ -359,8 +344,13 @@ public class SnapshotDrawerUtils {
|
|||||||
|
|
||||||
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
|
||||||
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
|
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
|
||||||
final ActivityManager.TaskDescription taskDescription =
|
final ActivityManager.TaskDescription taskDescription;
|
||||||
getOrCreateTaskDescription(runningTaskInfo);
|
if (runningTaskInfo.taskDescription != null) {
|
||||||
|
taskDescription = runningTaskInfo.taskDescription;
|
||||||
|
} else {
|
||||||
|
taskDescription = new ActivityManager.TaskDescription();
|
||||||
|
taskDescription.setBackgroundColor(WHITE);
|
||||||
|
}
|
||||||
drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
|
drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
|
||||||
attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
|
attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
|
||||||
final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
|
final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
|
||||||
|
|||||||
@@ -22,12 +22,9 @@ import android.annotation.Nullable;
|
|||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.TaskInfo;
|
import android.app.TaskInfo;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.view.InsetsState;
|
import android.view.InsetsState;
|
||||||
import android.view.SurfaceControl;
|
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.WindowInsets.Type.InsetsType;
|
import android.view.WindowInsets.Type.InsetsType;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -62,8 +59,6 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
/** @hide **/
|
/** @hide **/
|
||||||
public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4;
|
public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4;
|
||||||
|
|
||||||
public static final int STARTING_WINDOW_TYPE_WINDOWLESS = 5;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@@ -72,8 +67,7 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
STARTING_WINDOW_TYPE_SPLASH_SCREEN,
|
STARTING_WINDOW_TYPE_SPLASH_SCREEN,
|
||||||
STARTING_WINDOW_TYPE_SNAPSHOT,
|
STARTING_WINDOW_TYPE_SNAPSHOT,
|
||||||
STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN,
|
STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN,
|
||||||
STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN,
|
STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
|
||||||
STARTING_WINDOW_TYPE_WINDOWLESS
|
|
||||||
})
|
})
|
||||||
public @interface StartingWindowType {}
|
public @interface StartingWindowType {}
|
||||||
|
|
||||||
@@ -124,7 +118,6 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
TYPE_PARAMETER_ACTIVITY_CREATED,
|
TYPE_PARAMETER_ACTIVITY_CREATED,
|
||||||
TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
|
TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN,
|
||||||
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
|
TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN,
|
||||||
TYPE_PARAMETER_WINDOWLESS,
|
|
||||||
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
|
TYPE_PARAMETER_LEGACY_SPLASH_SCREEN
|
||||||
})
|
})
|
||||||
public @interface StartingTypeParams {}
|
public @interface StartingTypeParams {}
|
||||||
@@ -158,12 +151,6 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public static final int TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN = 0x00000080;
|
public static final int TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN = 0x00000080;
|
||||||
|
|
||||||
/**
|
|
||||||
* Windowless surface
|
|
||||||
*/
|
|
||||||
public static final int TYPE_PARAMETER_WINDOWLESS = 0x00000100;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application is allowed to use the legacy splash screen
|
* Application is allowed to use the legacy splash screen
|
||||||
* @hide
|
* @hide
|
||||||
@@ -195,33 +182,7 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public TaskSnapshot taskSnapshot;
|
public TaskSnapshot taskSnapshot;
|
||||||
|
|
||||||
@InsetsType public int requestedVisibleTypes = WindowInsets.Type.defaultVisible();
|
public @InsetsType int requestedVisibleTypes = WindowInsets.Type.defaultVisible();
|
||||||
|
|
||||||
/**
|
|
||||||
* App token where the starting window should add to.
|
|
||||||
*/
|
|
||||||
public IBinder appToken;
|
|
||||||
|
|
||||||
public IWindowlessStartingSurfaceCallback windowlessStartingSurfaceCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root surface where windowless surface should attach on.
|
|
||||||
*/
|
|
||||||
public SurfaceControl rootSurface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify windowless surface is created.
|
|
||||||
* @param addedSurface Created surface.
|
|
||||||
*/
|
|
||||||
public void notifyAddComplete(SurfaceControl addedSurface) {
|
|
||||||
if (windowlessStartingSurfaceCallback != null) {
|
|
||||||
try {
|
|
||||||
windowlessStartingSurfaceCallback.onSurfaceAdded(addedSurface);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public StartingWindowInfo() {
|
public StartingWindowInfo() {
|
||||||
|
|
||||||
@@ -255,9 +216,6 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
dest.writeBoolean(isKeyguardOccluded);
|
dest.writeBoolean(isKeyguardOccluded);
|
||||||
dest.writeTypedObject(taskSnapshot, flags);
|
dest.writeTypedObject(taskSnapshot, flags);
|
||||||
dest.writeInt(requestedVisibleTypes);
|
dest.writeInt(requestedVisibleTypes);
|
||||||
dest.writeStrongBinder(appToken);
|
|
||||||
dest.writeStrongInterface(windowlessStartingSurfaceCallback);
|
|
||||||
dest.writeTypedObject(rootSurface, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void readFromParcel(@NonNull Parcel source) {
|
void readFromParcel(@NonNull Parcel source) {
|
||||||
@@ -272,10 +230,6 @@ public final class StartingWindowInfo implements Parcelable {
|
|||||||
isKeyguardOccluded = source.readBoolean();
|
isKeyguardOccluded = source.readBoolean();
|
||||||
taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
|
taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
|
||||||
requestedVisibleTypes = source.readInt();
|
requestedVisibleTypes = source.readInt();
|
||||||
appToken = source.readStrongBinder();
|
|
||||||
windowlessStartingSurfaceCallback = IWindowlessStartingSurfaceCallback.Stub
|
|
||||||
.asInterface(source.readStrongBinder());
|
|
||||||
rootSurface = source.readTypedObject(SurfaceControl.CREATOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -67,16 +67,6 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public float roundedCornerRadius;
|
public float roundedCornerRadius;
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove windowless surface.
|
|
||||||
*/
|
|
||||||
public boolean windowlessSurface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove immediately.
|
|
||||||
*/
|
|
||||||
public boolean removeImmediately;
|
|
||||||
|
|
||||||
public StartingWindowRemovalInfo() {
|
public StartingWindowRemovalInfo() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -97,8 +87,6 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
|||||||
playRevealAnimation = source.readBoolean();
|
playRevealAnimation = source.readBoolean();
|
||||||
deferRemoveForIme = source.readBoolean();
|
deferRemoveForIme = source.readBoolean();
|
||||||
roundedCornerRadius = source.readFloat();
|
roundedCornerRadius = source.readFloat();
|
||||||
windowlessSurface = source.readBoolean();
|
|
||||||
removeImmediately = source.readBoolean();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,8 +97,6 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
|||||||
dest.writeBoolean(playRevealAnimation);
|
dest.writeBoolean(playRevealAnimation);
|
||||||
dest.writeBoolean(deferRemoveForIme);
|
dest.writeBoolean(deferRemoveForIme);
|
||||||
dest.writeFloat(roundedCornerRadius);
|
dest.writeFloat(roundedCornerRadius);
|
||||||
dest.writeBoolean(windowlessSurface);
|
|
||||||
dest.writeBoolean(removeImmediately);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -119,9 +105,7 @@ public final class StartingWindowRemovalInfo implements Parcelable {
|
|||||||
+ " frame=" + mainFrame
|
+ " frame=" + mainFrame
|
||||||
+ " playRevealAnimation=" + playRevealAnimation
|
+ " playRevealAnimation=" + playRevealAnimation
|
||||||
+ " roundedCornerRadius=" + roundedCornerRadius
|
+ " roundedCornerRadius=" + roundedCornerRadius
|
||||||
+ " deferRemoveForIme=" + deferRemoveForIme
|
+ " deferRemoveForIme=" + deferRemoveForIme + "}";
|
||||||
+ " windowlessSurface=" + windowlessSurface
|
|
||||||
+ " removeImmediately=" + removeImmediately + "}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR =
|
public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR =
|
||||||
|
|||||||
@@ -92,10 +92,13 @@ public class TaskOrganizer extends WindowOrganizer {
|
|||||||
* has create a starting window for the Task.
|
* has create a starting window for the Task.
|
||||||
*
|
*
|
||||||
* @param info 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
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
@BinderThread
|
@BinderThread
|
||||||
public void addStartingWindow(@NonNull StartingWindowInfo info) {}
|
public void addStartingWindow(@NonNull StartingWindowInfo info,
|
||||||
|
@NonNull IBinder appToken) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the Task want to remove the starting window.
|
* Called when the Task want to remove the starting window.
|
||||||
@@ -294,8 +297,9 @@ public class TaskOrganizer extends WindowOrganizer {
|
|||||||
|
|
||||||
private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
|
private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void addStartingWindow(StartingWindowInfo windowInfo) {
|
public void addStartingWindow(StartingWindowInfo windowInfo,
|
||||||
mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo));
|
IBinder appToken) {
|
||||||
|
mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -428,9 +428,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addStartingWindow(StartingWindowInfo info) {
|
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||||
if (mStartingWindow != null) {
|
if (mStartingWindow != null) {
|
||||||
mStartingWindow.addStartingWindow(info);
|
mStartingWindow.addStartingWindow(info, appToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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 android.content.Context;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.hardware.display.DisplayManager;
|
|
||||||
import android.view.Display;
|
|
||||||
|
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
|
||||||
|
|
||||||
// abstract class to create splash screen window(or windowless window)
|
|
||||||
abstract class AbsSplashWindowCreator {
|
|
||||||
protected static final String TAG = StartingWindowController.TAG;
|
|
||||||
protected final SplashscreenContentDrawer mSplashscreenContentDrawer;
|
|
||||||
protected final Context mContext;
|
|
||||||
protected final DisplayManager mDisplayManager;
|
|
||||||
protected final ShellExecutor mSplashScreenExecutor;
|
|
||||||
protected final StartingSurfaceDrawer.StartingWindowRecordManager mStartingWindowRecordManager;
|
|
||||||
|
|
||||||
private StartingSurface.SysuiProxy mSysuiProxy;
|
|
||||||
|
|
||||||
AbsSplashWindowCreator(SplashscreenContentDrawer contentDrawer, Context context,
|
|
||||||
ShellExecutor splashScreenExecutor, DisplayManager displayManager,
|
|
||||||
StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) {
|
|
||||||
mSplashscreenContentDrawer = contentDrawer;
|
|
||||||
mContext = context;
|
|
||||||
mSplashScreenExecutor = splashScreenExecutor;
|
|
||||||
mDisplayManager = displayManager;
|
|
||||||
mStartingWindowRecordManager = startingWindowRecordManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) {
|
|
||||||
return splashScreenThemeResId != 0
|
|
||||||
? splashScreenThemeResId
|
|
||||||
: activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource()
|
|
||||||
: com.android.internal.R.style.Theme_DeviceDefault_DayNight;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Display getDisplay(int displayId) {
|
|
||||||
return mDisplayManager.getDisplay(displayId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) {
|
|
||||||
mSysuiProxy = sysuiProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void requestTopUi(boolean requestTopUi) {
|
|
||||||
if (mSysuiProxy != null) {
|
|
||||||
mSysuiProxy.requestTopUi(requestTopUi, TAG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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 android.window.StartingWindowInfo;
|
|
||||||
import android.window.TaskSnapshot;
|
|
||||||
|
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
|
||||||
|
|
||||||
class SnapshotWindowCreator {
|
|
||||||
private final ShellExecutor mMainExecutor;
|
|
||||||
private final StartingSurfaceDrawer.StartingWindowRecordManager
|
|
||||||
mStartingWindowRecordManager;
|
|
||||||
|
|
||||||
SnapshotWindowCreator(ShellExecutor mainExecutor,
|
|
||||||
StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) {
|
|
||||||
mMainExecutor = mainExecutor;
|
|
||||||
mStartingWindowRecordManager = startingWindowRecordManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) {
|
|
||||||
final int taskId = startingWindowInfo.taskInfo.taskId;
|
|
||||||
// Remove any existing starting window for this task before adding.
|
|
||||||
mStartingWindowRecordManager.removeWindow(taskId, true);
|
|
||||||
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo,
|
|
||||||
startingWindowInfo.appToken, snapshot, mMainExecutor,
|
|
||||||
() -> mStartingWindowRecordManager.removeWindow(taskId, true));
|
|
||||||
if (surface != null) {
|
|
||||||
final SnapshotWindowRecord tView = new SnapshotWindowRecord(surface,
|
|
||||||
startingWindowInfo.taskInfo.topActivityType, mMainExecutor);
|
|
||||||
mStartingWindowRecordManager.addRecord(taskId, tView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord {
|
|
||||||
private final TaskSnapshotWindow mTaskSnapshotWindow;
|
|
||||||
|
|
||||||
SnapshotWindowRecord(TaskSnapshotWindow taskSnapshotWindow,
|
|
||||||
int activityType, ShellExecutor removeExecutor) {
|
|
||||||
super(activityType, removeExecutor);
|
|
||||||
mTaskSnapshotWindow = taskSnapshotWindow;
|
|
||||||
mBGColor = mTaskSnapshotWindow.getBackgroundColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void removeImmediately() {
|
|
||||||
super.removeImmediately();
|
|
||||||
mTaskSnapshotWindow.removeImmediately();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean hasImeSurface() {
|
|
||||||
return mTaskSnapshotWindow.hasImeSurface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,9 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLA
|
|||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION;
|
||||||
|
import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION;
|
||||||
|
|
||||||
import android.annotation.ColorInt;
|
import android.annotation.ColorInt;
|
||||||
import android.annotation.IntDef;
|
import android.annotation.IntDef;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
@@ -93,25 +96,6 @@ import java.util.function.UnaryOperator;
|
|||||||
public class SplashscreenContentDrawer {
|
public class SplashscreenContentDrawer {
|
||||||
private static final String TAG = StartingWindowController.TAG;
|
private static final String TAG = StartingWindowController.TAG;
|
||||||
|
|
||||||
/**
|
|
||||||
* The minimum duration during which the splash screen is shown when the splash screen icon is
|
|
||||||
* animated.
|
|
||||||
*/
|
|
||||||
static final long MINIMAL_ANIMATION_DURATION = 400L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow the icon style splash screen to be displayed for longer to give time for the animation
|
|
||||||
* to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly
|
|
||||||
* longer than the {@link #MINIMAL_ANIMATION_DURATION} duration.
|
|
||||||
*/
|
|
||||||
static final long TIME_WINDOW_DURATION = 100L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum duration during which the splash screen will be shown if the application is ready
|
|
||||||
* to show before the icon animation finishes.
|
|
||||||
*/
|
|
||||||
static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION;
|
|
||||||
|
|
||||||
// The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an
|
// The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an
|
||||||
// icon which it's non-transparent foreground area is similar to it's background area, then
|
// icon which it's non-transparent foreground area is similar to it's background area, then
|
||||||
// do not enlarge the foreground drawable.
|
// do not enlarge the foreground drawable.
|
||||||
|
|||||||
@@ -1,506 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
|
||||||
import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
|
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
|
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.app.ActivityTaskManager;
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.TaskInfo;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.pm.IPackageManager;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.hardware.display.DisplayManager;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteCallback;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.os.Trace;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.util.Slog;
|
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.Choreographer;
|
|
||||||
import android.view.Display;
|
|
||||||
import android.view.SurfaceControlViewHost;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.WindowInsetsController;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.view.WindowManagerGlobal;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.window.SplashScreenView;
|
|
||||||
import android.window.StartingWindowInfo;
|
|
||||||
import android.window.StartingWindowRemovalInfo;
|
|
||||||
|
|
||||||
import com.android.internal.R;
|
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
|
||||||
import com.android.internal.util.ContrastColorUtil;
|
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class which able to draw splash screen as the starting window for a task.
|
|
||||||
*
|
|
||||||
* In order to speed up, there will use two threads to creating a splash screen in parallel.
|
|
||||||
* Right now we are still using PhoneWindow to create splash screen window, so the view is added to
|
|
||||||
* the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call
|
|
||||||
* scheduleTraversal to register a callback from Choreographer, so the drawing result of the view
|
|
||||||
* can synchronize on each frame.
|
|
||||||
*
|
|
||||||
* The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing
|
|
||||||
* the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background
|
|
||||||
* thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after
|
|
||||||
* the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very
|
|
||||||
* quickly.
|
|
||||||
*
|
|
||||||
* So basically we are using the spare time to prepare the SplashScreenView while splash screen
|
|
||||||
* thread is waiting for
|
|
||||||
* 1. WindowManager#addView(binder call to WM),
|
|
||||||
* 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device),
|
|
||||||
* 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will
|
|
||||||
* always happen before #draw).
|
|
||||||
* Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on
|
|
||||||
* splash-screen background tread can make they execute in parallel, which ensure it is faster then
|
|
||||||
* to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame.
|
|
||||||
*
|
|
||||||
* Here is the sequence to compare the difference between using single and two thread.
|
|
||||||
*
|
|
||||||
* Single thread:
|
|
||||||
* => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout
|
|
||||||
* -> draw -> AdaptiveIconDrawable#draw
|
|
||||||
*
|
|
||||||
* Two threads:
|
|
||||||
* => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw)
|
|
||||||
* => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint
|
|
||||||
* directly).
|
|
||||||
*/
|
|
||||||
class SplashscreenWindowCreator extends AbsSplashWindowCreator {
|
|
||||||
private static final int LIGHT_BARS_MASK =
|
|
||||||
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
|
|
||||||
| WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
|
||||||
|
|
||||||
private final WindowManagerGlobal mWindowManagerGlobal;
|
|
||||||
private Choreographer mChoreographer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
|
|
||||||
* rendered and that have not yet been removed by their client.
|
|
||||||
*/
|
|
||||||
private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
|
|
||||||
new SparseArray<>(1);
|
|
||||||
|
|
||||||
SplashscreenWindowCreator(SplashscreenContentDrawer contentDrawer, Context context,
|
|
||||||
ShellExecutor splashScreenExecutor, DisplayManager displayManager,
|
|
||||||
StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) {
|
|
||||||
super(contentDrawer, context, splashScreenExecutor, displayManager,
|
|
||||||
startingWindowRecordManager);
|
|
||||||
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
|
|
||||||
mWindowManagerGlobal = WindowManagerGlobal.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addSplashScreenStartingWindow(StartingWindowInfo windowInfo,
|
|
||||||
@StartingWindowInfo.StartingWindowType int suggestType) {
|
|
||||||
final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
|
|
||||||
final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
|
|
||||||
? windowInfo.targetActivityInfo
|
|
||||||
: taskInfo.topActivityInfo;
|
|
||||||
if (activityInfo == null || activityInfo.packageName == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// replace with the default theme if the application didn't set
|
|
||||||
final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
|
|
||||||
final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme,
|
|
||||||
suggestType, mDisplayManager);
|
|
||||||
if (context == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(
|
|
||||||
context, windowInfo, suggestType, activityInfo.packageName,
|
|
||||||
suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
|
|
||||||
? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, windowInfo.appToken);
|
|
||||||
|
|
||||||
final int displayId = taskInfo.displayId;
|
|
||||||
final int taskId = taskInfo.taskId;
|
|
||||||
final Display display = getDisplay(displayId);
|
|
||||||
|
|
||||||
// TODO(b/173975965) tracking performance
|
|
||||||
// Prepare the splash screen content view on splash screen worker thread in parallel, so the
|
|
||||||
// content view won't be blocked by binder call like addWindow and relayout.
|
|
||||||
// 1. Trigger splash screen worker thread to create SplashScreenView before/while
|
|
||||||
// Session#addWindow.
|
|
||||||
// 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start
|
|
||||||
// traversal, which will call Session#relayout on splash screen thread.
|
|
||||||
// 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at
|
|
||||||
// the same time the splash screen thread should be executing Session#relayout. Blocking the
|
|
||||||
// traversal -> draw on splash screen thread until the BitmapShader of the icon is ready.
|
|
||||||
|
|
||||||
// Record whether create splash screen view success, notify to current thread after
|
|
||||||
// create splash screen view finished.
|
|
||||||
final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
|
|
||||||
final FrameLayout rootLayout = new FrameLayout(
|
|
||||||
mSplashscreenContentDrawer.createViewContextWrapper(context));
|
|
||||||
rootLayout.setPadding(0, 0, 0, 0);
|
|
||||||
rootLayout.setFitsSystemWindows(false);
|
|
||||||
final Runnable setViewSynchronized = () -> {
|
|
||||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView");
|
|
||||||
// waiting for setContentView before relayoutWindow
|
|
||||||
SplashScreenView contentView = viewSupplier.get();
|
|
||||||
final StartingSurfaceDrawer.StartingWindowRecord sRecord =
|
|
||||||
mStartingWindowRecordManager.getRecord(taskId);
|
|
||||||
final SplashWindowRecord record = sRecord instanceof SplashWindowRecord
|
|
||||||
? (SplashWindowRecord) sRecord : null;
|
|
||||||
// If record == null, either the starting window added fail or removed already.
|
|
||||||
// Do not add this view if the token is mismatch.
|
|
||||||
if (record != null && windowInfo.appToken == record.mAppToken) {
|
|
||||||
// if view == null then creation of content view was failed.
|
|
||||||
if (contentView != null) {
|
|
||||||
try {
|
|
||||||
rootLayout.addView(contentView);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Slog.w(TAG, "failed set content view to starting window "
|
|
||||||
+ "at taskId: " + taskId, e);
|
|
||||||
contentView = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
record.setSplashScreenView(contentView);
|
|
||||||
}
|
|
||||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
|
||||||
};
|
|
||||||
requestTopUi(true);
|
|
||||||
mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo,
|
|
||||||
viewSupplier::setView, viewSupplier::setUiThreadInitTask);
|
|
||||||
try {
|
|
||||||
if (addWindow(taskId, windowInfo.appToken, rootLayout, display, params, suggestType)) {
|
|
||||||
// We use the splash screen worker thread to create SplashScreenView while adding
|
|
||||||
// the window, as otherwise Choreographer#doFrame might be delayed on this thread.
|
|
||||||
// And since Choreographer#doFrame won't happen immediately after adding the window,
|
|
||||||
// if the view is not added to the PhoneWindow on the first #doFrame, the view will
|
|
||||||
// not be rendered on the first frame. So here we need to synchronize the view on
|
|
||||||
// the window before first round relayoutWindow, which will happen after insets
|
|
||||||
// animation.
|
|
||||||
mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
|
|
||||||
final SplashWindowRecord record =
|
|
||||||
(SplashWindowRecord) mStartingWindowRecordManager.getRecord(taskId);
|
|
||||||
if (record != null) {
|
|
||||||
record.parseAppSystemBarColor(context);
|
|
||||||
// Block until we get the background color.
|
|
||||||
final SplashScreenView contentView = viewSupplier.get();
|
|
||||||
if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
|
||||||
contentView.addOnAttachStateChangeListener(
|
|
||||||
new View.OnAttachStateChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onViewAttachedToWindow(View v) {
|
|
||||||
final int lightBarAppearance =
|
|
||||||
ContrastColorUtil.isColorLight(
|
|
||||||
contentView.getInitBackgroundColor())
|
|
||||||
? LIGHT_BARS_MASK : 0;
|
|
||||||
contentView.getWindowInsetsController()
|
|
||||||
.setSystemBarsAppearance(
|
|
||||||
lightBarAppearance, LIGHT_BARS_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewDetachedFromWindow(View v) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// release the icon view host
|
|
||||||
final SplashScreenView contentView = viewSupplier.get();
|
|
||||||
if (contentView.getSurfaceHost() != null) {
|
|
||||||
SplashScreenView.releaseIconHost(contentView.getSurfaceHost());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
// don't crash if something else bad happens, for example a
|
|
||||||
// failure loading resources because we are loading from an app
|
|
||||||
// on external storage that has been unmounted.
|
|
||||||
Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int estimateTaskBackgroundColor(TaskInfo taskInfo) {
|
|
||||||
if (taskInfo.topActivityInfo == null) {
|
|
||||||
return Color.TRANSPARENT;
|
|
||||||
}
|
|
||||||
final ActivityInfo activityInfo = taskInfo.topActivityInfo;
|
|
||||||
final String packageName = activityInfo.packageName;
|
|
||||||
final int userId = taskInfo.userId;
|
|
||||||
final Context windowContext;
|
|
||||||
try {
|
|
||||||
windowContext = mContext.createPackageContextAsUser(
|
|
||||||
packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId));
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
Slog.w(TAG, "Failed creating package context with package name "
|
|
||||||
+ packageName + " for user " + taskInfo.userId, e);
|
|
||||||
return Color.TRANSPARENT;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final IPackageManager packageManager = ActivityThread.getPackageManager();
|
|
||||||
final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName,
|
|
||||||
userId);
|
|
||||||
final int splashScreenThemeId = splashScreenThemeName != null
|
|
||||||
? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo);
|
|
||||||
|
|
||||||
if (theme != windowContext.getThemeResId()) {
|
|
||||||
windowContext.setTheme(theme);
|
|
||||||
}
|
|
||||||
return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext);
|
|
||||||
} catch (RuntimeException | RemoteException e) {
|
|
||||||
Slog.w(TAG, "failed get starting window background color at taskId: "
|
|
||||||
+ taskInfo.taskId, e);
|
|
||||||
}
|
|
||||||
return Color.TRANSPARENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the Task wants to copy the splash screen.
|
|
||||||
*/
|
|
||||||
public void copySplashScreenView(int taskId) {
|
|
||||||
final StartingSurfaceDrawer.StartingWindowRecord record =
|
|
||||||
mStartingWindowRecordManager.getRecord(taskId);
|
|
||||||
final SplashWindowRecord preView = record instanceof SplashWindowRecord
|
|
||||||
? (SplashWindowRecord) record : null;
|
|
||||||
SplashScreenView.SplashScreenViewParcelable parcelable;
|
|
||||||
SplashScreenView splashScreenView = preView != null ? preView.mSplashView : null;
|
|
||||||
if (splashScreenView != null && splashScreenView.isCopyable()) {
|
|
||||||
parcelable = new SplashScreenView.SplashScreenViewParcelable(splashScreenView);
|
|
||||||
parcelable.setClientCallback(
|
|
||||||
new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
|
|
||||||
() -> onAppSplashScreenViewRemoved(taskId, false))));
|
|
||||||
splashScreenView.onCopied();
|
|
||||||
mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
|
|
||||||
} else {
|
|
||||||
parcelable = null;
|
|
||||||
}
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
|
||||||
"Copying splash screen window view for task: %d with parcelable %b",
|
|
||||||
taskId, parcelable != null);
|
|
||||||
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy
|
|
||||||
* or when the Activity is clean up.
|
|
||||||
*
|
|
||||||
* @param taskId The Task id on which the splash screen was attached
|
|
||||||
*/
|
|
||||||
public void onAppSplashScreenViewRemoved(int taskId) {
|
|
||||||
onAppSplashScreenViewRemoved(taskId, true /* fromServer */);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param fromServer If true, this means the removal was notified by the server. This is only
|
|
||||||
* used for debugging purposes.
|
|
||||||
* @see #onAppSplashScreenViewRemoved(int)
|
|
||||||
*/
|
|
||||||
private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) {
|
|
||||||
SurfaceControlViewHost viewHost =
|
|
||||||
mAnimatedSplashScreenSurfaceHosts.get(taskId);
|
|
||||||
if (viewHost == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mAnimatedSplashScreenSurfaceHosts.remove(taskId);
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
|
||||||
"%s the splash screen. Releasing SurfaceControlViewHost for task: %d",
|
|
||||||
fromServer ? "Server cleaned up" : "App removed", taskId);
|
|
||||||
SplashScreenView.releaseIconHost(viewHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
|
|
||||||
WindowManager.LayoutParams params,
|
|
||||||
@StartingWindowInfo.StartingWindowType int suggestType) {
|
|
||||||
boolean shouldSaveView = true;
|
|
||||||
final Context context = view.getContext();
|
|
||||||
try {
|
|
||||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView");
|
|
||||||
mWindowManagerGlobal.addView(view, params, display,
|
|
||||||
null /* parentWindow */, context.getUserId());
|
|
||||||
} catch (WindowManager.BadTokenException e) {
|
|
||||||
// ignore
|
|
||||||
Slog.w(TAG, appToken + " already running, starting window not displayed. "
|
|
||||||
+ e.getMessage());
|
|
||||||
shouldSaveView = false;
|
|
||||||
} finally {
|
|
||||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
|
||||||
if (view.getParent() == null) {
|
|
||||||
Slog.w(TAG, "view not successfully added to wm, removing view");
|
|
||||||
mWindowManagerGlobal.removeView(view, true /* immediate */);
|
|
||||||
shouldSaveView = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (shouldSaveView) {
|
|
||||||
mStartingWindowRecordManager.removeWindow(taskId, true);
|
|
||||||
saveSplashScreenRecord(appToken, taskId, view, suggestType);
|
|
||||||
}
|
|
||||||
return shouldSaveView;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
|
|
||||||
@StartingWindowInfo.StartingWindowType int suggestType) {
|
|
||||||
final SplashWindowRecord tView =
|
|
||||||
new SplashWindowRecord(appToken, view, suggestType);
|
|
||||||
mStartingWindowRecordManager.addRecord(taskId, tView);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeWindowInner(View decorView, boolean hideView) {
|
|
||||||
requestTopUi(false);
|
|
||||||
if (hideView) {
|
|
||||||
decorView.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
mWindowManagerGlobal.removeView(decorView, false /* immediate */);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
|
|
||||||
private SplashScreenView mView;
|
|
||||||
private boolean mIsViewSet;
|
|
||||||
private Runnable mUiThreadInitTask;
|
|
||||||
void setView(SplashScreenView view) {
|
|
||||||
synchronized (this) {
|
|
||||||
mView = view;
|
|
||||||
mIsViewSet = true;
|
|
||||||
notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUiThreadInitTask(Runnable initTask) {
|
|
||||||
synchronized (this) {
|
|
||||||
mUiThreadInitTask = initTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public SplashScreenView get() {
|
|
||||||
synchronized (this) {
|
|
||||||
while (!mIsViewSet) {
|
|
||||||
try {
|
|
||||||
wait();
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mUiThreadInitTask != null) {
|
|
||||||
mUiThreadInitTask.run();
|
|
||||||
mUiThreadInitTask = null;
|
|
||||||
}
|
|
||||||
return mView;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord {
|
|
||||||
private final IBinder mAppToken;
|
|
||||||
private final View mRootView;
|
|
||||||
@StartingWindowInfo.StartingWindowType private final int mSuggestType;
|
|
||||||
private final long mCreateTime;
|
|
||||||
|
|
||||||
private boolean mSetSplashScreen;
|
|
||||||
private SplashScreenView mSplashView;
|
|
||||||
private int mSystemBarAppearance;
|
|
||||||
private boolean mDrawsSystemBarBackgrounds;
|
|
||||||
|
|
||||||
SplashWindowRecord(IBinder appToken, View decorView,
|
|
||||||
@StartingWindowInfo.StartingWindowType int suggestType) {
|
|
||||||
mAppToken = appToken;
|
|
||||||
mRootView = decorView;
|
|
||||||
mSuggestType = suggestType;
|
|
||||||
mCreateTime = SystemClock.uptimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSplashScreenView(SplashScreenView splashScreenView) {
|
|
||||||
if (mSetSplashScreen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mSplashView = splashScreenView;
|
|
||||||
mBGColor = mSplashView.getInitBackgroundColor();
|
|
||||||
mSetSplashScreen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseAppSystemBarColor(Context context) {
|
|
||||||
final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
|
|
||||||
mDrawsSystemBarBackgrounds = a.getBoolean(
|
|
||||||
R.styleable.Window_windowDrawsSystemBarBackgrounds, false);
|
|
||||||
if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
|
|
||||||
mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
|
||||||
}
|
|
||||||
if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
|
|
||||||
mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
|
||||||
}
|
|
||||||
a.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the system bar color which set by splash screen, make it align to the app.
|
|
||||||
void clearSystemBarColor() {
|
|
||||||
if (mRootView == null || !mRootView.isAttachedToWindow()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mRootView.getLayoutParams() instanceof WindowManager.LayoutParams) {
|
|
||||||
final WindowManager.LayoutParams lp =
|
|
||||||
(WindowManager.LayoutParams) mRootView.getLayoutParams();
|
|
||||||
if (mDrawsSystemBarBackgrounds) {
|
|
||||||
lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
|
||||||
} else {
|
|
||||||
lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
|
||||||
}
|
|
||||||
mRootView.setLayoutParams(lp);
|
|
||||||
}
|
|
||||||
mRootView.getWindowInsetsController().setSystemBarsAppearance(
|
|
||||||
mSystemBarAppearance, LIGHT_BARS_MASK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
|
||||||
if (mRootView != null) {
|
|
||||||
if (mSplashView != null) {
|
|
||||||
clearSystemBarColor();
|
|
||||||
if (immediately
|
|
||||||
|| mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
|
||||||
removeWindowInner(mRootView, false);
|
|
||||||
} else {
|
|
||||||
if (info.playRevealAnimation) {
|
|
||||||
mSplashscreenContentDrawer.applyExitAnimation(mSplashView,
|
|
||||||
info.windowAnimationLeash, info.mainFrame,
|
|
||||||
() -> removeWindowInner(mRootView, true),
|
|
||||||
mCreateTime, info.roundedCornerRadius);
|
|
||||||
} else {
|
|
||||||
// the SplashScreenView has been copied to client, hide the view to skip
|
|
||||||
// default exit animation
|
|
||||||
removeWindowInner(mRootView, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// shouldn't happen
|
|
||||||
Slog.e(TAG, "Found empty splash screen, remove!");
|
|
||||||
removeWindowInner(mRootView, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,80 +16,169 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.startingsurface;
|
package com.android.wm.shell.startingsurface;
|
||||||
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||||
|
import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
|
||||||
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
|
||||||
|
|
||||||
import android.annotation.CallSuper;
|
import android.annotation.Nullable;
|
||||||
|
import android.app.ActivityManager.RunningTaskInfo;
|
||||||
|
import android.app.ActivityTaskManager;
|
||||||
|
import android.app.ActivityThread;
|
||||||
import android.app.TaskInfo;
|
import android.app.TaskInfo;
|
||||||
import android.app.WindowConfiguration;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.pm.IPackageManager;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
import android.hardware.display.DisplayManager;
|
import android.hardware.display.DisplayManager;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.RemoteCallback;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.os.Trace;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.IWindow;
|
import android.view.Choreographer;
|
||||||
import android.view.SurfaceControl;
|
import android.view.Display;
|
||||||
import android.view.SurfaceSession;
|
import android.view.SurfaceControlViewHost;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowInsetsController;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.WindowlessWindowManager;
|
import android.view.WindowManagerGlobal;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.window.SplashScreenView;
|
import android.window.SplashScreenView;
|
||||||
|
import android.window.SplashScreenView.SplashScreenViewParcelable;
|
||||||
import android.window.StartingWindowInfo;
|
import android.window.StartingWindowInfo;
|
||||||
import android.window.StartingWindowInfo.StartingWindowType;
|
import android.window.StartingWindowInfo.StartingWindowType;
|
||||||
import android.window.StartingWindowRemovalInfo;
|
import android.window.StartingWindowRemovalInfo;
|
||||||
import android.window.TaskSnapshot;
|
import android.window.TaskSnapshot;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
import com.android.internal.util.ContrastColorUtil;
|
||||||
import com.android.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class which able to draw splash screen or snapshot as the starting window for a task.
|
* A class which able to draw splash screen or snapshot as the starting window for a task.
|
||||||
|
*
|
||||||
|
* In order to speed up, there will use two threads to creating a splash screen in parallel.
|
||||||
|
* Right now we are still using PhoneWindow to create splash screen window, so the view is added to
|
||||||
|
* the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call
|
||||||
|
* scheduleTraversal to register a callback from Choreographer, so the drawing result of the view
|
||||||
|
* can synchronize on each frame.
|
||||||
|
*
|
||||||
|
* The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing
|
||||||
|
* the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background
|
||||||
|
* thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after
|
||||||
|
* the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very
|
||||||
|
* quickly.
|
||||||
|
*
|
||||||
|
* So basically we are using the spare time to prepare the SplashScreenView while splash screen
|
||||||
|
* thread is waiting for
|
||||||
|
* 1. WindowManager#addView(binder call to WM),
|
||||||
|
* 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device),
|
||||||
|
* 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will
|
||||||
|
* always happen before #draw).
|
||||||
|
* Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on
|
||||||
|
* splash-screen background tread can make they execute in parallel, which ensure it is faster then
|
||||||
|
* to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame.
|
||||||
|
*
|
||||||
|
* Here is the sequence to compare the difference between using single and two thread.
|
||||||
|
*
|
||||||
|
* Single thread:
|
||||||
|
* => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout
|
||||||
|
* -> draw -> AdaptiveIconDrawable#draw
|
||||||
|
*
|
||||||
|
* Two threads:
|
||||||
|
* => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw)
|
||||||
|
* => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint
|
||||||
|
* directly).
|
||||||
*/
|
*/
|
||||||
@ShellSplashscreenThread
|
@ShellSplashscreenThread
|
||||||
public class StartingSurfaceDrawer {
|
public class StartingSurfaceDrawer {
|
||||||
|
private static final String TAG = StartingWindowController.TAG;
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final DisplayManager mDisplayManager;
|
||||||
private final ShellExecutor mSplashScreenExecutor;
|
private final ShellExecutor mSplashScreenExecutor;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final SplashscreenContentDrawer mSplashscreenContentDrawer;
|
final SplashscreenContentDrawer mSplashscreenContentDrawer;
|
||||||
@VisibleForTesting
|
private Choreographer mChoreographer;
|
||||||
final SplashscreenWindowCreator mSplashscreenWindowCreator;
|
private final WindowManagerGlobal mWindowManagerGlobal;
|
||||||
private final SnapshotWindowCreator mSnapshotWindowCreator;
|
private StartingSurface.SysuiProxy mSysuiProxy;
|
||||||
private final WindowlessSplashWindowCreator mWindowlessSplashWindowCreator;
|
private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
|
||||||
private final WindowlessSnapshotWindowCreator mWindowlessSnapshotWindowCreator;
|
|
||||||
|
private static final int LIGHT_BARS_MASK =
|
||||||
|
WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
|
||||||
|
| WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||||
|
/**
|
||||||
|
* The minimum duration during which the splash screen is shown when the splash screen icon is
|
||||||
|
* animated.
|
||||||
|
*/
|
||||||
|
static final long MINIMAL_ANIMATION_DURATION = 400L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the icon style splash screen to be displayed for longer to give time for the animation
|
||||||
|
* to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly
|
||||||
|
* longer than the {@link #MINIMAL_ANIMATION_DURATION} duration.
|
||||||
|
*/
|
||||||
|
static final long TIME_WINDOW_DURATION = 100L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum duration during which the splash screen will be shown if the application is ready
|
||||||
|
* to show before the icon animation finishes.
|
||||||
|
*/
|
||||||
|
static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION;
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager();
|
|
||||||
// Windowless surface could co-exist with starting window in a task.
|
|
||||||
@VisibleForTesting
|
|
||||||
final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager();
|
|
||||||
/**
|
/**
|
||||||
* @param splashScreenExecutor The thread used to control add and remove starting window.
|
* @param splashScreenExecutor The thread used to control add and remove starting window.
|
||||||
*/
|
*/
|
||||||
public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
|
public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
|
||||||
IconProvider iconProvider, TransactionPool pool) {
|
IconProvider iconProvider, TransactionPool pool) {
|
||||||
|
mContext = context;
|
||||||
|
mDisplayManager = mContext.getSystemService(DisplayManager.class);
|
||||||
mSplashScreenExecutor = splashScreenExecutor;
|
mSplashScreenExecutor = splashScreenExecutor;
|
||||||
final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
|
mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool);
|
||||||
mSplashscreenContentDrawer = new SplashscreenContentDrawer(context, iconProvider, pool);
|
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
|
||||||
displayManager.getDisplay(DEFAULT_DISPLAY);
|
mWindowManagerGlobal = WindowManagerGlobal.getInstance();
|
||||||
|
mDisplayManager.getDisplay(DEFAULT_DISPLAY);
|
||||||
|
}
|
||||||
|
|
||||||
mSplashscreenWindowCreator = new SplashscreenWindowCreator(mSplashscreenContentDrawer,
|
@VisibleForTesting
|
||||||
context, splashScreenExecutor, displayManager, mWindowRecords);
|
final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
|
||||||
mSnapshotWindowCreator = new SnapshotWindowCreator(splashScreenExecutor,
|
|
||||||
mWindowRecords);
|
/**
|
||||||
mWindowlessSplashWindowCreator = new WindowlessSplashWindowCreator(
|
* Records of {@link SurfaceControlViewHost} where the splash screen icon animation is
|
||||||
mSplashscreenContentDrawer, context, splashScreenExecutor, displayManager,
|
* rendered and that have not yet been removed by their client.
|
||||||
mWindowlessRecords, pool);
|
*/
|
||||||
mWindowlessSnapshotWindowCreator = new WindowlessSnapshotWindowCreator(
|
private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
|
||||||
mWindowlessRecords, context, displayManager, mSplashscreenContentDrawer, pool);
|
new SparseArray<>(1);
|
||||||
|
|
||||||
|
private Display getDisplay(int displayId) {
|
||||||
|
return mDisplayManager.getDisplay(displayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) {
|
||||||
|
return splashScreenThemeResId != 0
|
||||||
|
? splashScreenThemeResId
|
||||||
|
: activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource()
|
||||||
|
: com.android.internal.R.style.Theme_DeviceDefault_DayNight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) {
|
void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) {
|
||||||
mSplashscreenWindowCreator.setSysuiProxy(sysuiProxy);
|
mSysuiProxy = sysuiProxy;
|
||||||
mWindowlessSplashWindowCreator.setSysuiProxy(sysuiProxy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,55 +186,231 @@ public class StartingSurfaceDrawer {
|
|||||||
*
|
*
|
||||||
* @param suggestType The suggestion type to draw the splash screen.
|
* @param suggestType The suggestion type to draw the splash screen.
|
||||||
*/
|
*/
|
||||||
void addSplashScreenStartingWindow(StartingWindowInfo windowInfo,
|
void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken,
|
||||||
@StartingWindowType int suggestType) {
|
@StartingWindowType int suggestType) {
|
||||||
mSplashscreenWindowCreator.addSplashScreenStartingWindow(windowInfo, suggestType);
|
final RunningTaskInfo taskInfo = windowInfo.taskInfo;
|
||||||
|
final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
|
||||||
|
? windowInfo.targetActivityInfo
|
||||||
|
: taskInfo.topActivityInfo;
|
||||||
|
if (activityInfo == null || activityInfo.packageName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// replace with the default theme if the application didn't set
|
||||||
|
final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo);
|
||||||
|
final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme,
|
||||||
|
suggestType, mDisplayManager);
|
||||||
|
if (context == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters(
|
||||||
|
context, windowInfo, suggestType, activityInfo.packageName,
|
||||||
|
suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
|
||||||
|
? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken);
|
||||||
|
|
||||||
|
final int displayId = taskInfo.displayId;
|
||||||
|
final int taskId = taskInfo.taskId;
|
||||||
|
final Display display = getDisplay(displayId);
|
||||||
|
|
||||||
|
// TODO(b/173975965) tracking performance
|
||||||
|
// Prepare the splash screen content view on splash screen worker thread in parallel, so the
|
||||||
|
// content view won't be blocked by binder call like addWindow and relayout.
|
||||||
|
// 1. Trigger splash screen worker thread to create SplashScreenView before/while
|
||||||
|
// Session#addWindow.
|
||||||
|
// 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start
|
||||||
|
// traversal, which will call Session#relayout on splash screen thread.
|
||||||
|
// 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at
|
||||||
|
// the same time the splash screen thread should be executing Session#relayout. Blocking the
|
||||||
|
// traversal -> draw on splash screen thread until the BitmapShader of the icon is ready.
|
||||||
|
|
||||||
|
// Record whether create splash screen view success, notify to current thread after
|
||||||
|
// create splash screen view finished.
|
||||||
|
final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
|
||||||
|
final FrameLayout rootLayout = new FrameLayout(
|
||||||
|
mSplashscreenContentDrawer.createViewContextWrapper(context));
|
||||||
|
rootLayout.setPadding(0, 0, 0, 0);
|
||||||
|
rootLayout.setFitsSystemWindows(false);
|
||||||
|
final Runnable setViewSynchronized = () -> {
|
||||||
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView");
|
||||||
|
// waiting for setContentView before relayoutWindow
|
||||||
|
SplashScreenView contentView = viewSupplier.get();
|
||||||
|
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
||||||
|
// If record == null, either the starting window added fail or removed already.
|
||||||
|
// Do not add this view if the token is mismatch.
|
||||||
|
if (record != null && appToken == record.mAppToken) {
|
||||||
|
// if view == null then creation of content view was failed.
|
||||||
|
if (contentView != null) {
|
||||||
|
try {
|
||||||
|
rootLayout.addView(contentView);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Slog.w(TAG, "failed set content view to starting window "
|
||||||
|
+ "at taskId: " + taskId, e);
|
||||||
|
contentView = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
record.setSplashScreenView(contentView);
|
||||||
|
}
|
||||||
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||||
|
};
|
||||||
|
if (mSysuiProxy != null) {
|
||||||
|
mSysuiProxy.requestTopUi(true, TAG);
|
||||||
|
}
|
||||||
|
mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo,
|
||||||
|
viewSupplier::setView, viewSupplier::setUiThreadInitTask);
|
||||||
|
try {
|
||||||
|
if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) {
|
||||||
|
// We use the splash screen worker thread to create SplashScreenView while adding
|
||||||
|
// the window, as otherwise Choreographer#doFrame might be delayed on this thread.
|
||||||
|
// And since Choreographer#doFrame won't happen immediately after adding the window,
|
||||||
|
// if the view is not added to the PhoneWindow on the first #doFrame, the view will
|
||||||
|
// not be rendered on the first frame. So here we need to synchronize the view on
|
||||||
|
// the window before first round relayoutWindow, which will happen after insets
|
||||||
|
// animation.
|
||||||
|
mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null);
|
||||||
|
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
||||||
|
record.parseAppSystemBarColor(context);
|
||||||
|
// Block until we get the background color.
|
||||||
|
final SplashScreenView contentView = viewSupplier.get();
|
||||||
|
if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
||||||
|
contentView.addOnAttachStateChangeListener(
|
||||||
|
new View.OnAttachStateChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onViewAttachedToWindow(View v) {
|
||||||
|
final int lightBarAppearance = ContrastColorUtil.isColorLight(
|
||||||
|
contentView.getInitBackgroundColor())
|
||||||
|
? LIGHT_BARS_MASK : 0;
|
||||||
|
contentView.getWindowInsetsController().setSystemBarsAppearance(
|
||||||
|
lightBarAppearance, LIGHT_BARS_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewDetachedFromWindow(View v) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
record.mBGColor = contentView.getInitBackgroundColor();
|
||||||
|
} else {
|
||||||
|
// release the icon view host
|
||||||
|
final SplashScreenView contentView = viewSupplier.get();
|
||||||
|
if (contentView.getSurfaceHost() != null) {
|
||||||
|
SplashScreenView.releaseIconHost(contentView.getSurfaceHost());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
// don't crash if something else bad happens, for example a
|
||||||
|
// failure loading resources because we are loading from an app
|
||||||
|
// on external storage that has been unmounted.
|
||||||
|
Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int getStartingWindowBackgroundColorForTask(int taskId) {
|
int getStartingWindowBackgroundColorForTask(int taskId) {
|
||||||
final StartingWindowRecord startingWindowRecord = mWindowRecords.getRecord(taskId);
|
final StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId);
|
||||||
if (startingWindowRecord == null) {
|
if (startingWindowRecord == null) {
|
||||||
return Color.TRANSPARENT;
|
return Color.TRANSPARENT;
|
||||||
}
|
}
|
||||||
return startingWindowRecord.getBGColor();
|
return startingWindowRecord.mBGColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
|
||||||
|
private SplashScreenView mView;
|
||||||
|
private boolean mIsViewSet;
|
||||||
|
private Runnable mUiThreadInitTask;
|
||||||
|
void setView(SplashScreenView view) {
|
||||||
|
synchronized (this) {
|
||||||
|
mView = view;
|
||||||
|
mIsViewSet = true;
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUiThreadInitTask(Runnable initTask) {
|
||||||
|
synchronized (this) {
|
||||||
|
mUiThreadInitTask = initTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public SplashScreenView get() {
|
||||||
|
synchronized (this) {
|
||||||
|
while (!mIsViewSet) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mUiThreadInitTask != null) {
|
||||||
|
mUiThreadInitTask.run();
|
||||||
|
mUiThreadInitTask = null;
|
||||||
|
}
|
||||||
|
return mView;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int estimateTaskBackgroundColor(TaskInfo taskInfo) {
|
int estimateTaskBackgroundColor(TaskInfo taskInfo) {
|
||||||
return mSplashscreenWindowCreator.estimateTaskBackgroundColor(taskInfo);
|
if (taskInfo.topActivityInfo == null) {
|
||||||
|
return Color.TRANSPARENT;
|
||||||
|
}
|
||||||
|
final ActivityInfo activityInfo = taskInfo.topActivityInfo;
|
||||||
|
final String packageName = activityInfo.packageName;
|
||||||
|
final int userId = taskInfo.userId;
|
||||||
|
final Context windowContext;
|
||||||
|
try {
|
||||||
|
windowContext = mContext.createPackageContextAsUser(
|
||||||
|
packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId));
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
Slog.w(TAG, "Failed creating package context with package name "
|
||||||
|
+ packageName + " for user " + taskInfo.userId, e);
|
||||||
|
return Color.TRANSPARENT;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final IPackageManager packageManager = ActivityThread.getPackageManager();
|
||||||
|
final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName,
|
||||||
|
userId);
|
||||||
|
final int splashScreenThemeId = splashScreenThemeName != null
|
||||||
|
? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo);
|
||||||
|
|
||||||
|
if (theme != windowContext.getThemeResId()) {
|
||||||
|
windowContext.setTheme(theme);
|
||||||
|
}
|
||||||
|
return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext);
|
||||||
|
} catch (RuntimeException | RemoteException e) {
|
||||||
|
Slog.w(TAG, "failed get starting window background color at taskId: "
|
||||||
|
+ taskInfo.taskId, e);
|
||||||
|
}
|
||||||
|
return Color.TRANSPARENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a task need a snapshot starting window.
|
* Called when a task need a snapshot starting window.
|
||||||
*/
|
*/
|
||||||
void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) {
|
void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken,
|
||||||
mSnapshotWindowCreator.makeTaskSnapshotWindow(startingWindowInfo, snapshot);
|
TaskSnapshot snapshot) {
|
||||||
|
final int taskId = startingWindowInfo.taskInfo.taskId;
|
||||||
|
// Remove any existing starting window for this task before adding.
|
||||||
|
removeWindowNoAnimate(taskId);
|
||||||
|
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
|
||||||
|
snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId));
|
||||||
|
if (surface == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final StartingWindowRecord tView = new StartingWindowRecord(appToken,
|
||||||
|
null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT);
|
||||||
|
mStartingWindowRecords.put(taskId, tView);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the content of a task is ready to show, starting window can be removed.
|
* Called when the content of a task is ready to show, starting window can be removed.
|
||||||
*/
|
*/
|
||||||
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
|
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
|
||||||
if (removalInfo.windowlessSurface) {
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
mWindowlessRecords.removeWindow(removalInfo, removalInfo.removeImmediately);
|
"Task start finish, remove starting surface for task: %d",
|
||||||
} else {
|
removalInfo.taskId);
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
removeWindowSynced(removalInfo, false /* immediately */);
|
||||||
"Task start finish, remove starting surface for task: %d",
|
|
||||||
removalInfo.taskId);
|
|
||||||
mWindowRecords.removeWindow(removalInfo, removalInfo.removeImmediately);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a windowless starting surface and attach to the root surface.
|
|
||||||
*/
|
|
||||||
void addWindowlessStartingSurface(StartingWindowInfo windowInfo) {
|
|
||||||
if (windowInfo.taskSnapshot != null) {
|
|
||||||
mWindowlessSnapshotWindowCreator.makeTaskSnapshotWindow(windowInfo,
|
|
||||||
windowInfo.rootSurface, windowInfo.taskSnapshot, mSplashScreenExecutor);
|
|
||||||
} else {
|
|
||||||
mWindowlessSplashWindowCreator.addSplashScreenStartingWindow(
|
|
||||||
windowInfo, windowInfo.rootSurface);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,15 +419,37 @@ public class StartingSurfaceDrawer {
|
|||||||
public void clearAllWindows() {
|
public void clearAllWindows() {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
"Clear all starting windows immediately");
|
"Clear all starting windows immediately");
|
||||||
mWindowRecords.clearAllWindows();
|
final int taskSize = mStartingWindowRecords.size();
|
||||||
mWindowlessRecords.clearAllWindows();
|
final int[] taskIds = new int[taskSize];
|
||||||
|
for (int i = taskSize - 1; i >= 0; --i) {
|
||||||
|
taskIds[i] = mStartingWindowRecords.keyAt(i);
|
||||||
|
}
|
||||||
|
for (int i = taskSize - 1; i >= 0; --i) {
|
||||||
|
removeWindowNoAnimate(taskIds[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the Task wants to copy the splash screen.
|
* Called when the Task wants to copy the splash screen.
|
||||||
*/
|
*/
|
||||||
public void copySplashScreenView(int taskId) {
|
public void copySplashScreenView(int taskId) {
|
||||||
mSplashscreenWindowCreator.copySplashScreenView(taskId);
|
final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
|
||||||
|
SplashScreenViewParcelable parcelable;
|
||||||
|
SplashScreenView splashScreenView = preView != null ? preView.mContentView : null;
|
||||||
|
if (splashScreenView != null && splashScreenView.isCopyable()) {
|
||||||
|
parcelable = new SplashScreenViewParcelable(splashScreenView);
|
||||||
|
parcelable.setClientCallback(
|
||||||
|
new RemoteCallback((bundle) -> mSplashScreenExecutor.execute(
|
||||||
|
() -> onAppSplashScreenViewRemoved(taskId, false))));
|
||||||
|
splashScreenView.onCopied();
|
||||||
|
mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost());
|
||||||
|
} else {
|
||||||
|
parcelable = null;
|
||||||
|
}
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
|
"Copying splash screen window view for task: %d with parcelable %b",
|
||||||
|
taskId, parcelable != null);
|
||||||
|
ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,148 +459,195 @@ public class StartingSurfaceDrawer {
|
|||||||
* @param taskId The Task id on which the splash screen was attached
|
* @param taskId The Task id on which the splash screen was attached
|
||||||
*/
|
*/
|
||||||
public void onAppSplashScreenViewRemoved(int taskId) {
|
public void onAppSplashScreenViewRemoved(int taskId) {
|
||||||
mSplashscreenWindowCreator.onAppSplashScreenViewRemoved(taskId);
|
onAppSplashScreenViewRemoved(taskId, true /* fromServer */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param fromServer If true, this means the removal was notified by the server. This is only
|
||||||
|
* used for debugging purposes.
|
||||||
|
* @see #onAppSplashScreenViewRemoved(int)
|
||||||
|
*/
|
||||||
|
private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) {
|
||||||
|
SurfaceControlViewHost viewHost =
|
||||||
|
mAnimatedSplashScreenSurfaceHosts.get(taskId);
|
||||||
|
if (viewHost == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mAnimatedSplashScreenSurfaceHosts.remove(taskId);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
|
"%s the splash screen. Releasing SurfaceControlViewHost for task: %d",
|
||||||
|
fromServer ? "Server cleaned up" : "App removed", taskId);
|
||||||
|
SplashScreenView.releaseIconHost(viewHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
|
||||||
|
WindowManager.LayoutParams params, @StartingWindowType int suggestType) {
|
||||||
|
boolean shouldSaveView = true;
|
||||||
|
final Context context = view.getContext();
|
||||||
|
try {
|
||||||
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView");
|
||||||
|
mWindowManagerGlobal.addView(view, params, display,
|
||||||
|
null /* parentWindow */, context.getUserId());
|
||||||
|
} catch (WindowManager.BadTokenException e) {
|
||||||
|
// ignore
|
||||||
|
Slog.w(TAG, appToken + " already running, starting window not displayed. "
|
||||||
|
+ e.getMessage());
|
||||||
|
shouldSaveView = false;
|
||||||
|
} finally {
|
||||||
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||||
|
if (view.getParent() == null) {
|
||||||
|
Slog.w(TAG, "view not successfully added to wm, removing view");
|
||||||
|
mWindowManagerGlobal.removeView(view, true /* immediate */);
|
||||||
|
shouldSaveView = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldSaveView) {
|
||||||
|
removeWindowNoAnimate(taskId);
|
||||||
|
saveSplashScreenRecord(appToken, taskId, view, suggestType);
|
||||||
|
}
|
||||||
|
return shouldSaveView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void saveSplashScreenRecord(IBinder appToken, int taskId, View view,
|
||||||
|
@StartingWindowType int suggestType) {
|
||||||
|
final StartingWindowRecord tView = new StartingWindowRecord(appToken, view,
|
||||||
|
null/* TaskSnapshotWindow */, suggestType);
|
||||||
|
mStartingWindowRecords.put(taskId, tView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeWindowNoAnimate(int taskId) {
|
||||||
|
mTmpRemovalInfo.taskId = taskId;
|
||||||
|
removeWindowSynced(mTmpRemovalInfo, true /* immediately */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onImeDrawnOnTask(int taskId) {
|
void onImeDrawnOnTask(int taskId) {
|
||||||
onImeDrawnOnTask(mWindowRecords, taskId);
|
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
||||||
onImeDrawnOnTask(mWindowlessRecords, taskId);
|
if (record != null && record.mTaskSnapshotWindow != null
|
||||||
}
|
&& record.mTaskSnapshotWindow.hasImeSurface()) {
|
||||||
|
removeWindowNoAnimate(taskId);
|
||||||
private void onImeDrawnOnTask(StartingWindowRecordManager records, int taskId) {
|
|
||||||
final StartingSurfaceDrawer.StartingWindowRecord sRecord =
|
|
||||||
records.getRecord(taskId);
|
|
||||||
final SnapshotRecord record = sRecord instanceof SnapshotRecord
|
|
||||||
? (SnapshotRecord) sRecord : null;
|
|
||||||
if (record != null && record.hasImeSurface()) {
|
|
||||||
records.removeWindow(taskId, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class WindowlessStartingWindow extends WindowlessWindowManager {
|
protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) {
|
||||||
SurfaceControl mChildSurface;
|
final int taskId = removalInfo.taskId;
|
||||||
|
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
||||||
|
if (record != null) {
|
||||||
|
if (record.mDecorView != null) {
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
|
"Removing splash screen window for task: %d", taskId);
|
||||||
|
if (record.mContentView != null) {
|
||||||
|
record.clearSystemBarColor();
|
||||||
|
if (immediately
|
||||||
|
|| record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
||||||
|
removeWindowInner(record.mDecorView, false);
|
||||||
|
} else {
|
||||||
|
if (removalInfo.playRevealAnimation) {
|
||||||
|
mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
|
||||||
|
removalInfo.windowAnimationLeash, removalInfo.mainFrame,
|
||||||
|
() -> removeWindowInner(record.mDecorView, true),
|
||||||
|
record.mCreateTime, removalInfo.roundedCornerRadius);
|
||||||
|
} else {
|
||||||
|
// the SplashScreenView has been copied to client, hide the view to skip
|
||||||
|
// default exit animation
|
||||||
|
removeWindowInner(record.mDecorView, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// shouldn't happen
|
||||||
|
Slog.e(TAG, "Found empty splash screen, remove!");
|
||||||
|
removeWindowInner(record.mDecorView, false);
|
||||||
|
}
|
||||||
|
|
||||||
WindowlessStartingWindow(Configuration c, SurfaceControl rootSurface) {
|
|
||||||
super(c, rootSurface, null /* hostInputToken */);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected SurfaceControl getParentSurface(IWindow window,
|
|
||||||
WindowManager.LayoutParams attrs) {
|
|
||||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
|
|
||||||
.setContainerLayer()
|
|
||||||
.setName("Windowless window")
|
|
||||||
.setHidden(false)
|
|
||||||
.setParent(mRootSurface)
|
|
||||||
.setCallsite("WindowlessStartingWindow#attachToParentSurface");
|
|
||||||
mChildSurface = builder.build();
|
|
||||||
try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
|
|
||||||
t.setLayer(mChildSurface, Integer.MAX_VALUE);
|
|
||||||
t.apply();
|
|
||||||
}
|
}
|
||||||
return mChildSurface;
|
if (record.mTaskSnapshotWindow != null) {
|
||||||
}
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
}
|
"Removing task snapshot window for %d", taskId);
|
||||||
abstract static class StartingWindowRecord {
|
if (immediately) {
|
||||||
protected int mBGColor;
|
record.mTaskSnapshotWindow.removeImmediately();
|
||||||
abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately);
|
} else {
|
||||||
int getBGColor() {
|
record.mTaskSnapshotWindow.scheduleRemove(removalInfo.deferRemoveForIme);
|
||||||
return mBGColor;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract static class SnapshotRecord extends StartingWindowRecord {
|
|
||||||
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
|
|
||||||
/**
|
|
||||||
* The max delay time in milliseconds for removing the task snapshot window with IME
|
|
||||||
* visible.
|
|
||||||
* Ideally the delay time will be shorter when receiving
|
|
||||||
* {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
|
|
||||||
*/
|
|
||||||
private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
|
|
||||||
private final Runnable mScheduledRunnable = this::removeImmediately;
|
|
||||||
|
|
||||||
@WindowConfiguration.ActivityType protected final int mActivityType;
|
|
||||||
protected final ShellExecutor mRemoveExecutor;
|
|
||||||
|
|
||||||
SnapshotRecord(int activityType, ShellExecutor removeExecutor) {
|
|
||||||
mActivityType = activityType;
|
|
||||||
mRemoveExecutor = removeExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
|
||||||
if (immediately) {
|
|
||||||
removeImmediately();
|
|
||||||
} else {
|
|
||||||
scheduleRemove(info.deferRemoveForIme);
|
|
||||||
}
|
}
|
||||||
|
mStartingWindowRecords.remove(taskId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeWindowInner(View decorView, boolean hideView) {
|
||||||
|
if (mSysuiProxy != null) {
|
||||||
|
mSysuiProxy.requestTopUi(false, TAG);
|
||||||
|
}
|
||||||
|
if (hideView) {
|
||||||
|
decorView.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
mWindowManagerGlobal.removeView(decorView, false /* immediate */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record the view or surface for a starting window.
|
||||||
|
*/
|
||||||
|
private static class StartingWindowRecord {
|
||||||
|
private final IBinder mAppToken;
|
||||||
|
private final View mDecorView;
|
||||||
|
private final TaskSnapshotWindow mTaskSnapshotWindow;
|
||||||
|
private SplashScreenView mContentView;
|
||||||
|
private boolean mSetSplashScreen;
|
||||||
|
@StartingWindowType private int mSuggestType;
|
||||||
|
private int mBGColor;
|
||||||
|
private final long mCreateTime;
|
||||||
|
private int mSystemBarAppearance;
|
||||||
|
private boolean mDrawsSystemBarBackgrounds;
|
||||||
|
|
||||||
|
StartingWindowRecord(IBinder appToken, View decorView,
|
||||||
|
TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) {
|
||||||
|
mAppToken = appToken;
|
||||||
|
mDecorView = decorView;
|
||||||
|
mTaskSnapshotWindow = taskSnapshotWindow;
|
||||||
|
if (mTaskSnapshotWindow != null) {
|
||||||
|
mBGColor = mTaskSnapshotWindow.getBackgroundColor();
|
||||||
|
}
|
||||||
|
mSuggestType = suggestType;
|
||||||
|
mCreateTime = SystemClock.uptimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduleRemove(boolean deferRemoveForIme) {
|
private void setSplashScreenView(SplashScreenView splashScreenView) {
|
||||||
// Show the latest content as soon as possible for unlocking to home.
|
if (mSetSplashScreen) {
|
||||||
if (mActivityType == ACTIVITY_TYPE_HOME) {
|
|
||||||
removeImmediately();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mRemoveExecutor.removeCallbacks(mScheduledRunnable);
|
mContentView = splashScreenView;
|
||||||
final long delayRemovalTime = hasImeSurface() && deferRemoveForIme
|
mSetSplashScreen = true;
|
||||||
? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
|
|
||||||
: DELAY_REMOVAL_TIME_GENERAL;
|
|
||||||
mRemoveExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
|
||||||
"Defer removing snapshot surface in %d", delayRemovalTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean hasImeSurface();
|
private void parseAppSystemBarColor(Context context) {
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(R.styleable.Window);
|
||||||
@CallSuper
|
mDrawsSystemBarBackgrounds = a.getBoolean(
|
||||||
protected void removeImmediately() {
|
R.styleable.Window_windowDrawsSystemBarBackgrounds, false);
|
||||||
mRemoveExecutor.removeCallbacks(mScheduledRunnable);
|
if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
|
||||||
}
|
mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||||
}
|
|
||||||
|
|
||||||
static class StartingWindowRecordManager {
|
|
||||||
private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
|
|
||||||
private final SparseArray<StartingWindowRecord> mStartingWindowRecords =
|
|
||||||
new SparseArray<>();
|
|
||||||
|
|
||||||
void clearAllWindows() {
|
|
||||||
final int taskSize = mStartingWindowRecords.size();
|
|
||||||
final int[] taskIds = new int[taskSize];
|
|
||||||
for (int i = taskSize - 1; i >= 0; --i) {
|
|
||||||
taskIds[i] = mStartingWindowRecords.keyAt(i);
|
|
||||||
}
|
}
|
||||||
for (int i = taskSize - 1; i >= 0; --i) {
|
if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
|
||||||
removeWindow(taskIds[i], true);
|
mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||||
}
|
}
|
||||||
|
a.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addRecord(int taskId, StartingWindowRecord record) {
|
// Reset the system bar color which set by splash screen, make it align to the app.
|
||||||
mStartingWindowRecords.put(taskId, record);
|
private void clearSystemBarColor() {
|
||||||
}
|
if (mDecorView == null || !mDecorView.isAttachedToWindow()) {
|
||||||
|
return;
|
||||||
void removeWindow(StartingWindowRemovalInfo removeInfo, boolean immediately) {
|
|
||||||
final int taskId = removeInfo.taskId;
|
|
||||||
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
|
|
||||||
if (record != null) {
|
|
||||||
record.removeIfPossible(removeInfo, immediately);
|
|
||||||
mStartingWindowRecords.remove(taskId);
|
|
||||||
}
|
}
|
||||||
}
|
if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) {
|
||||||
|
final WindowManager.LayoutParams lp =
|
||||||
void removeWindow(int taskId, boolean immediately) {
|
(WindowManager.LayoutParams) mDecorView.getLayoutParams();
|
||||||
mTmpRemovalInfo.taskId = taskId;
|
if (mDrawsSystemBarBackgrounds) {
|
||||||
removeWindow(mTmpRemovalInfo, immediately);
|
lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||||
}
|
} else {
|
||||||
|
lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||||
StartingWindowRecord getRecord(int taskId) {
|
}
|
||||||
return mStartingWindowRecords.get(taskId);
|
mDecorView.setLayoutParams(lp);
|
||||||
}
|
}
|
||||||
|
mDecorView.getWindowInsetsController().setSystemBarsAppearance(
|
||||||
@VisibleForTesting
|
mSystemBarAppearance, LIGHT_BARS_MASK);
|
||||||
int recordSize() {
|
|
||||||
return mStartingWindowRecords.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
|
|||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS;
|
|
||||||
|
|
||||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
||||||
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
|
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
|
||||||
@@ -30,6 +29,7 @@ import android.app.ActivityManager.RunningTaskInfo;
|
|||||||
import android.app.TaskInfo;
|
import android.app.TaskInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Trace;
|
import android.os.Trace;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.window.StartingWindowInfo;
|
import android.window.StartingWindowInfo;
|
||||||
@@ -152,23 +152,22 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
|
|||||||
/**
|
/**
|
||||||
* Called when a task need a starting window.
|
* Called when a task need a starting window.
|
||||||
*/
|
*/
|
||||||
public void addStartingWindow(StartingWindowInfo windowInfo) {
|
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
|
||||||
mSplashScreenExecutor.execute(() -> {
|
mSplashScreenExecutor.execute(() -> {
|
||||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");
|
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");
|
||||||
|
|
||||||
final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType(
|
final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType(
|
||||||
windowInfo);
|
windowInfo);
|
||||||
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
|
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
|
||||||
if (suggestionType == STARTING_WINDOW_TYPE_WINDOWLESS) {
|
if (isSplashScreenType(suggestionType)) {
|
||||||
mStartingSurfaceDrawer.addWindowlessStartingSurface(windowInfo);
|
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
|
||||||
} else if (isSplashScreenType(suggestionType)) {
|
suggestionType);
|
||||||
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, suggestionType);
|
|
||||||
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
|
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
|
||||||
final TaskSnapshot snapshot = windowInfo.taskSnapshot;
|
final TaskSnapshot snapshot = windowInfo.taskSnapshot;
|
||||||
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot);
|
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
|
||||||
|
snapshot);
|
||||||
}
|
}
|
||||||
if (suggestionType != STARTING_WINDOW_TYPE_NONE
|
if (suggestionType != STARTING_WINDOW_TYPE_NONE) {
|
||||||
&& suggestionType != STARTING_WINDOW_TYPE_WINDOWLESS) {
|
|
||||||
int taskId = runningTaskInfo.taskId;
|
int taskId = runningTaskInfo.taskId;
|
||||||
int color = mStartingSurfaceDrawer
|
int color = mStartingSurfaceDrawer
|
||||||
.getStartingWindowBackgroundColorForTask(taskId);
|
.getStartingWindowBackgroundColorForTask(taskId);
|
||||||
@@ -219,13 +218,11 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
|
|||||||
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
|
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
|
||||||
mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow(
|
mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow(
|
||||||
removalInfo));
|
removalInfo));
|
||||||
if (!removalInfo.windowlessSurface) {
|
mSplashScreenExecutor.executeDelayed(() -> {
|
||||||
mSplashScreenExecutor.executeDelayed(() -> {
|
synchronized (mTaskBackgroundColors) {
|
||||||
synchronized (mTaskBackgroundColors) {
|
mTaskBackgroundColors.delete(removalInfo.taskId);
|
||||||
mTaskBackgroundColors.delete(removalInfo.taskId);
|
}
|
||||||
}
|
}, TASK_BG_COLOR_RETAIN_TIME_MS);
|
||||||
}, TASK_BG_COLOR_RETAIN_TIME_MS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.startingsurface;
|
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.WHITE;
|
||||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||||
@@ -62,14 +63,24 @@ public class TaskSnapshotWindow {
|
|||||||
private static final String TAG = StartingWindowController.TAG;
|
private static final String TAG = StartingWindowController.TAG;
|
||||||
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=";
|
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=";
|
||||||
|
|
||||||
|
private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
|
||||||
|
/**
|
||||||
|
* The max delay time in milliseconds for removing the task snapshot window with IME visible.
|
||||||
|
* Ideally the delay time will be shorter when receiving
|
||||||
|
* {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}.
|
||||||
|
*/
|
||||||
|
private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600;
|
||||||
|
|
||||||
private final Window mWindow;
|
private final Window mWindow;
|
||||||
private final Runnable mClearWindowHandler;
|
private final Runnable mClearWindowHandler;
|
||||||
private final ShellExecutor mSplashScreenExecutor;
|
private final ShellExecutor mSplashScreenExecutor;
|
||||||
private final IWindowSession mSession;
|
private final IWindowSession mSession;
|
||||||
private boolean mHasDrawn;
|
private boolean mHasDrawn;
|
||||||
private final Paint mBackgroundPaint = new Paint();
|
private final Paint mBackgroundPaint = new Paint();
|
||||||
|
private final int mActivityType;
|
||||||
private final int mOrientationOnCreation;
|
private final int mOrientationOnCreation;
|
||||||
|
|
||||||
|
private final Runnable mScheduledRunnable = this::removeImmediately;
|
||||||
private final boolean mHasImeSurface;
|
private final boolean mHasImeSurface;
|
||||||
|
|
||||||
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
|
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
|
||||||
@@ -93,6 +104,7 @@ public class TaskSnapshotWindow {
|
|||||||
final Point taskSize = snapshot.getTaskSize();
|
final Point taskSize = snapshot.getTaskSize();
|
||||||
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
|
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
|
||||||
final int orientation = snapshot.getOrientation();
|
final int orientation = snapshot.getOrientation();
|
||||||
|
final int activityType = runningTaskInfo.topActivityType;
|
||||||
final int displayId = runningTaskInfo.displayId;
|
final int displayId = runningTaskInfo.displayId;
|
||||||
|
|
||||||
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
||||||
@@ -102,11 +114,16 @@ public class TaskSnapshotWindow {
|
|||||||
final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array();
|
final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array();
|
||||||
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
|
final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
|
||||||
|
|
||||||
final TaskDescription taskDescription =
|
final TaskDescription taskDescription;
|
||||||
SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
|
if (runningTaskInfo.taskDescription != null) {
|
||||||
|
taskDescription = runningTaskInfo.taskDescription;
|
||||||
|
} else {
|
||||||
|
taskDescription = new TaskDescription();
|
||||||
|
taskDescription.setBackgroundColor(WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
|
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
|
||||||
snapshot, taskDescription, orientation,
|
snapshot, taskDescription, orientation, activityType,
|
||||||
clearWindowHandler, splashScreenExecutor);
|
clearWindowHandler, splashScreenExecutor);
|
||||||
final Window window = snapshotSurface.mWindow;
|
final Window window = snapshotSurface.mWindow;
|
||||||
|
|
||||||
@@ -136,8 +153,6 @@ public class TaskSnapshotWindow {
|
|||||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
snapshotSurface.clearWindowSynced();
|
snapshotSurface.clearWindowSynced();
|
||||||
Slog.w(TAG, "Failed to relayout snapshot starting window");
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
|
SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot,
|
||||||
@@ -149,7 +164,7 @@ public class TaskSnapshotWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription,
|
public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription,
|
||||||
int currentOrientation, Runnable clearWindowHandler,
|
int currentOrientation, int activityType, Runnable clearWindowHandler,
|
||||||
ShellExecutor splashScreenExecutor) {
|
ShellExecutor splashScreenExecutor) {
|
||||||
mSplashScreenExecutor = splashScreenExecutor;
|
mSplashScreenExecutor = splashScreenExecutor;
|
||||||
mSession = WindowManagerGlobal.getWindowSession();
|
mSession = WindowManagerGlobal.getWindowSession();
|
||||||
@@ -158,6 +173,7 @@ public class TaskSnapshotWindow {
|
|||||||
int backgroundColor = taskDescription.getBackgroundColor();
|
int backgroundColor = taskDescription.getBackgroundColor();
|
||||||
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
|
||||||
mOrientationOnCreation = currentOrientation;
|
mOrientationOnCreation = currentOrientation;
|
||||||
|
mActivityType = activityType;
|
||||||
mClearWindowHandler = clearWindowHandler;
|
mClearWindowHandler = clearWindowHandler;
|
||||||
mHasImeSurface = snapshot.hasImeSurface();
|
mHasImeSurface = snapshot.hasImeSurface();
|
||||||
}
|
}
|
||||||
@@ -170,7 +186,23 @@ public class TaskSnapshotWindow {
|
|||||||
return mHasImeSurface;
|
return mHasImeSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scheduleRemove(boolean deferRemoveForIme) {
|
||||||
|
// Show the latest content as soon as possible for unlocking to home.
|
||||||
|
if (mActivityType == ACTIVITY_TYPE_HOME) {
|
||||||
|
removeImmediately();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
|
||||||
|
final long delayRemovalTime = mHasImeSurface && deferRemoveForIme
|
||||||
|
? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE
|
||||||
|
: DELAY_REMOVAL_TIME_GENERAL;
|
||||||
|
mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
|
"Defer removing snapshot surface in %d", delayRemovalTime);
|
||||||
|
}
|
||||||
|
|
||||||
void removeImmediately() {
|
void removeImmediately() {
|
||||||
|
mSplashScreenExecutor.removeCallbacks(mScheduledRunnable);
|
||||||
try {
|
try {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
"Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn);
|
"Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn);
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.hardware.display.DisplayManager;
|
|
||||||
import android.view.Display;
|
|
||||||
import android.view.InsetsState;
|
|
||||||
import android.view.SurfaceControl;
|
|
||||||
import android.view.SurfaceControlViewHost;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.window.SnapshotDrawerUtils;
|
|
||||||
import android.window.StartingWindowInfo;
|
|
||||||
import android.window.TaskSnapshot;
|
|
||||||
|
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
|
||||||
|
|
||||||
class WindowlessSnapshotWindowCreator {
|
|
||||||
private static final int DEFAULT_FADEOUT_DURATION = 233;
|
|
||||||
private final StartingSurfaceDrawer.StartingWindowRecordManager
|
|
||||||
mStartingWindowRecordManager;
|
|
||||||
private final DisplayManager mDisplayManager;
|
|
||||||
private final Context mContext;
|
|
||||||
private final SplashscreenContentDrawer mSplashscreenContentDrawer;
|
|
||||||
private final TransactionPool mTransactionPool;
|
|
||||||
|
|
||||||
WindowlessSnapshotWindowCreator(
|
|
||||||
StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager,
|
|
||||||
Context context,
|
|
||||||
DisplayManager displayManager, SplashscreenContentDrawer splashscreenContentDrawer,
|
|
||||||
TransactionPool transactionPool) {
|
|
||||||
mStartingWindowRecordManager = startingWindowRecordManager;
|
|
||||||
mContext = context;
|
|
||||||
mDisplayManager = displayManager;
|
|
||||||
mSplashscreenContentDrawer = splashscreenContentDrawer;
|
|
||||||
mTransactionPool = transactionPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
void makeTaskSnapshotWindow(StartingWindowInfo info, SurfaceControl rootSurface,
|
|
||||||
TaskSnapshot snapshot, ShellExecutor removeExecutor) {
|
|
||||||
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
|
|
||||||
final int taskId = runningTaskInfo.taskId;
|
|
||||||
final String title = "Windowless Snapshot " + taskId;
|
|
||||||
final WindowManager.LayoutParams lp = SnapshotDrawerUtils.createLayoutParameters(
|
|
||||||
info, title, TYPE_APPLICATION_OVERLAY, snapshot.getHardwareBuffer().getFormat(),
|
|
||||||
null /* token */);
|
|
||||||
if (lp == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final Display display = mDisplayManager.getDisplay(runningTaskInfo.displayId);
|
|
||||||
final StartingSurfaceDrawer.WindowlessStartingWindow wlw =
|
|
||||||
new StartingSurfaceDrawer.WindowlessStartingWindow(
|
|
||||||
runningTaskInfo.configuration, rootSurface);
|
|
||||||
final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost(
|
|
||||||
mContext, display, wlw, "WindowlessSnapshotWindowCreator");
|
|
||||||
final Point taskSize = snapshot.getTaskSize();
|
|
||||||
final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y);
|
|
||||||
final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds();
|
|
||||||
final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
|
|
||||||
final FrameLayout rootLayout = new FrameLayout(
|
|
||||||
mSplashscreenContentDrawer.createViewContextWrapper(mContext));
|
|
||||||
mViewHost.setView(rootLayout, lp);
|
|
||||||
SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot,
|
|
||||||
snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */);
|
|
||||||
|
|
||||||
final ActivityManager.TaskDescription taskDescription =
|
|
||||||
SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo);
|
|
||||||
|
|
||||||
final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface,
|
|
||||||
taskDescription.getBackgroundColor(), snapshot.hasImeSurface(),
|
|
||||||
runningTaskInfo.topActivityType, removeExecutor);
|
|
||||||
mStartingWindowRecordManager.addRecord(taskId, record);
|
|
||||||
info.notifyAddComplete(wlw.mChildSurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord {
|
|
||||||
private SurfaceControlViewHost mViewHost;
|
|
||||||
private SurfaceControl mChildSurface;
|
|
||||||
private final boolean mHasImeSurface;
|
|
||||||
|
|
||||||
SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface,
|
|
||||||
int bgColor, boolean hasImeSurface, int activityType,
|
|
||||||
ShellExecutor removeExecutor) {
|
|
||||||
super(activityType, removeExecutor);
|
|
||||||
mViewHost = viewHost;
|
|
||||||
mChildSurface = childSurface;
|
|
||||||
mBGColor = bgColor;
|
|
||||||
mHasImeSurface = hasImeSurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void removeImmediately() {
|
|
||||||
super.removeImmediately();
|
|
||||||
fadeoutThenRelease();
|
|
||||||
}
|
|
||||||
|
|
||||||
void fadeoutThenRelease() {
|
|
||||||
final ValueAnimator fadeOutAnimator = ValueAnimator.ofFloat(1f, 0f);
|
|
||||||
fadeOutAnimator.setDuration(DEFAULT_FADEOUT_DURATION);
|
|
||||||
final SurfaceControl.Transaction t = mTransactionPool.acquire();
|
|
||||||
fadeOutAnimator.addUpdateListener(animation -> {
|
|
||||||
if (mChildSurface == null || !mChildSurface.isValid()) {
|
|
||||||
fadeOutAnimator.cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
t.setAlpha(mChildSurface, (float) animation.getAnimatedValue());
|
|
||||||
t.apply();
|
|
||||||
});
|
|
||||||
|
|
||||||
fadeOutAnimator.addListener(new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animator animation) {
|
|
||||||
if (mChildSurface == null || !mChildSurface.isValid()) {
|
|
||||||
fadeOutAnimator.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
mTransactionPool.release(t);
|
|
||||||
if (mChildSurface != null) {
|
|
||||||
final SurfaceControl.Transaction t = mTransactionPool.acquire();
|
|
||||||
t.remove(mChildSurface).apply();
|
|
||||||
mTransactionPool.release(t);
|
|
||||||
mChildSurface = null;
|
|
||||||
}
|
|
||||||
if (mViewHost != null) {
|
|
||||||
mViewHost.release();
|
|
||||||
mViewHost = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
fadeOutAnimator.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean hasImeSurface() {
|
|
||||||
return mHasImeSurface;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2023 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.graphics.Color.WHITE;
|
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.hardware.display.DisplayManager;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.view.Display;
|
|
||||||
import android.view.SurfaceControl;
|
|
||||||
import android.view.SurfaceControlViewHost;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.window.SplashScreenView;
|
|
||||||
import android.window.StartingWindowInfo;
|
|
||||||
import android.window.StartingWindowRemovalInfo;
|
|
||||||
|
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
|
||||||
|
|
||||||
class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
|
|
||||||
|
|
||||||
private final TransactionPool mTransactionPool;
|
|
||||||
|
|
||||||
WindowlessSplashWindowCreator(SplashscreenContentDrawer contentDrawer,
|
|
||||||
Context context,
|
|
||||||
ShellExecutor splashScreenExecutor,
|
|
||||||
DisplayManager displayManager,
|
|
||||||
StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager,
|
|
||||||
TransactionPool pool) {
|
|
||||||
super(contentDrawer, context, splashScreenExecutor, displayManager,
|
|
||||||
startingWindowRecordManager);
|
|
||||||
mTransactionPool = pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, SurfaceControl rootSurface) {
|
|
||||||
final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo;
|
|
||||||
final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null
|
|
||||||
? windowInfo.targetActivityInfo
|
|
||||||
: taskInfo.topActivityInfo;
|
|
||||||
if (activityInfo == null || activityInfo.packageName == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int displayId = taskInfo.displayId;
|
|
||||||
final Display display = mDisplayManager.getDisplay(displayId);
|
|
||||||
if (display == null) {
|
|
||||||
// Can't show splash screen on requested display, so skip showing at all.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final Context myContext = SplashscreenContentDrawer.createContext(mContext, windowInfo,
|
|
||||||
0 /* theme */, STARTING_WINDOW_TYPE_SPLASH_SCREEN, mDisplayManager);
|
|
||||||
if (myContext == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final StartingSurfaceDrawer.WindowlessStartingWindow wlw =
|
|
||||||
new StartingSurfaceDrawer.WindowlessStartingWindow(
|
|
||||||
taskInfo.configuration, rootSurface);
|
|
||||||
final SurfaceControlViewHost viewHost = new SurfaceControlViewHost(
|
|
||||||
myContext, display, wlw, "WindowlessSplashWindowCreator");
|
|
||||||
final String title = "Windowless Splash " + taskInfo.taskId;
|
|
||||||
final WindowManager.LayoutParams lp = SplashscreenContentDrawer.createLayoutParameters(
|
|
||||||
myContext, windowInfo, STARTING_WINDOW_TYPE_SPLASH_SCREEN, title,
|
|
||||||
PixelFormat.TRANSLUCENT, new Binder());
|
|
||||||
final Rect windowBounds = taskInfo.configuration.windowConfiguration.getBounds();
|
|
||||||
lp.width = windowBounds.width();
|
|
||||||
lp.height = windowBounds.height();
|
|
||||||
final ActivityManager.TaskDescription taskDescription;
|
|
||||||
if (taskInfo.taskDescription != null) {
|
|
||||||
taskDescription = taskInfo.taskDescription;
|
|
||||||
} else {
|
|
||||||
taskDescription = new ActivityManager.TaskDescription();
|
|
||||||
taskDescription.setBackgroundColor(WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
final FrameLayout rootLayout = new FrameLayout(
|
|
||||||
mSplashscreenContentDrawer.createViewContextWrapper(mContext));
|
|
||||||
viewHost.setView(rootLayout, lp);
|
|
||||||
|
|
||||||
final int bgColor = taskDescription.getBackgroundColor();
|
|
||||||
final SplashScreenView splashScreenView = mSplashscreenContentDrawer
|
|
||||||
.makeSimpleSplashScreenContentView(myContext, windowInfo, bgColor);
|
|
||||||
rootLayout.addView(splashScreenView);
|
|
||||||
final SplashWindowRecord record = new SplashWindowRecord(viewHost, splashScreenView,
|
|
||||||
wlw.mChildSurface, bgColor);
|
|
||||||
mStartingWindowRecordManager.addRecord(taskInfo.taskId, record);
|
|
||||||
windowInfo.notifyAddComplete(wlw.mChildSurface);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord {
|
|
||||||
private SurfaceControlViewHost mViewHost;
|
|
||||||
private final long mCreateTime;
|
|
||||||
private SurfaceControl mChildSurface;
|
|
||||||
private final SplashScreenView mSplashView;
|
|
||||||
|
|
||||||
SplashWindowRecord(SurfaceControlViewHost viewHost, SplashScreenView splashView,
|
|
||||||
SurfaceControl childSurface, int bgColor) {
|
|
||||||
mViewHost = viewHost;
|
|
||||||
mSplashView = splashView;
|
|
||||||
mChildSurface = childSurface;
|
|
||||||
mBGColor = bgColor;
|
|
||||||
mCreateTime = SystemClock.uptimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
|
|
||||||
if (!immediately) {
|
|
||||||
mSplashscreenContentDrawer.applyExitAnimation(mSplashView,
|
|
||||||
info.windowAnimationLeash, info.mainFrame,
|
|
||||||
this::release, mCreateTime, 0 /* roundedCornerRadius */);
|
|
||||||
} else {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void release() {
|
|
||||||
if (mChildSurface != null) {
|
|
||||||
final SurfaceControl.Transaction t = mTransactionPool.acquire();
|
|
||||||
t.remove(mChildSurface).apply();
|
|
||||||
mTransactionPool.release(t);
|
|
||||||
mChildSurface = null;
|
|
||||||
}
|
|
||||||
if (mViewHost != null) {
|
|
||||||
mViewHost.release();
|
|
||||||
mViewHost = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
|
|||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
|
||||||
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS;
|
|
||||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
|
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
|
||||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
|
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
|
||||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
|
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
|
||||||
@@ -31,7 +30,6 @@ 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_PROCESS_RUNNING;
|
||||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
|
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
|
||||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
|
import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN;
|
||||||
import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS;
|
|
||||||
|
|
||||||
import android.window.StartingWindowInfo;
|
import android.window.StartingWindowInfo;
|
||||||
|
|
||||||
@@ -57,7 +55,6 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
|
|||||||
final boolean legacySplashScreen =
|
final boolean legacySplashScreen =
|
||||||
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
|
((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
|
||||||
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
|
final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0;
|
||||||
final boolean windowlessSurface = (parameter & TYPE_PARAMETER_WINDOWLESS) != 0;
|
|
||||||
final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;
|
final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;
|
||||||
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
|
||||||
@@ -70,15 +67,10 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor
|
|||||||
+ "isSolidColorSplashScreen=%b, "
|
+ "isSolidColorSplashScreen=%b, "
|
||||||
+ "legacySplashScreen=%b, "
|
+ "legacySplashScreen=%b, "
|
||||||
+ "activityDrawn=%b, "
|
+ "activityDrawn=%b, "
|
||||||
+ "windowless=%b, "
|
|
||||||
+ "topIsHome=%b",
|
+ "topIsHome=%b",
|
||||||
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
|
newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated,
|
||||||
isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface,
|
isSolidColorSplashScreen, legacySplashScreen, activityDrawn, topIsHome);
|
||||||
topIsHome);
|
|
||||||
|
|
||||||
if (windowlessSurface) {
|
|
||||||
return STARTING_WINDOW_TYPE_WINDOWLESS;
|
|
||||||
}
|
|
||||||
if (!topIsHome) {
|
if (!topIsHome) {
|
||||||
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
|
if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
|
||||||
return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
|
return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen);
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
|||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||||
import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MAX_ANIMATION_DURATION;
|
import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION;
|
||||||
import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MINIMAL_ANIMATION_DURATION;
|
import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
@@ -56,9 +56,11 @@ import android.os.IBinder;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.testing.TestableContext;
|
import android.testing.TestableContext;
|
||||||
|
import android.view.Display;
|
||||||
import android.view.IWindowSession;
|
import android.view.IWindowSession;
|
||||||
import android.view.InsetsState;
|
import android.view.InsetsState;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.WindowManagerGlobal;
|
import android.view.WindowManagerGlobal;
|
||||||
import android.view.WindowMetrics;
|
import android.view.WindowMetrics;
|
||||||
@@ -104,7 +106,36 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
private ShellExecutor mTestExecutor;
|
private ShellExecutor mTestExecutor;
|
||||||
private final TestableContext mTestContext = new TestContext(
|
private final TestableContext mTestContext = new TestContext(
|
||||||
InstrumentationRegistry.getInstrumentation().getTargetContext());
|
InstrumentationRegistry.getInstrumentation().getTargetContext());
|
||||||
StartingSurfaceDrawer mStartingSurfaceDrawer;
|
TestStartingSurfaceDrawer mStartingSurfaceDrawer;
|
||||||
|
|
||||||
|
static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{
|
||||||
|
int mAddWindowForTask = 0;
|
||||||
|
|
||||||
|
TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
|
||||||
|
IconProvider iconProvider, TransactionPool pool) {
|
||||||
|
super(context, splashScreenExecutor, iconProvider, pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
|
||||||
|
WindowManager.LayoutParams params, int suggestType) {
|
||||||
|
// listen for addView
|
||||||
|
mAddWindowForTask = taskId;
|
||||||
|
saveSplashScreenRecord(appToken, taskId, view, suggestType);
|
||||||
|
// Do not wait for background color
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo,
|
||||||
|
boolean immediately) {
|
||||||
|
// listen for removeView
|
||||||
|
if (mAddWindowForTask == removalInfo.taskId) {
|
||||||
|
mAddWindowForTask = 0;
|
||||||
|
}
|
||||||
|
mStartingWindowRecords.remove(removalInfo.taskId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class TestContext extends TestableContext {
|
private static class TestContext extends TestableContext {
|
||||||
TestContext(Context context) {
|
TestContext(Context context) {
|
||||||
@@ -134,51 +165,44 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
|
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
|
||||||
doNothing().when(mMockWindowManager).addView(any(), any());
|
doNothing().when(mMockWindowManager).addView(any(), any());
|
||||||
mTestExecutor = new HandlerExecutor(mTestHandler);
|
mTestExecutor = new HandlerExecutor(mTestHandler);
|
||||||
mStartingSurfaceDrawer = new StartingSurfaceDrawer(mTestContext, mTestExecutor,
|
|
||||||
mIconProvider, mTransactionPool);
|
|
||||||
mStartingSurfaceDrawer = spy(
|
mStartingSurfaceDrawer = spy(
|
||||||
new StartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider,
|
new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider,
|
||||||
mTransactionPool));
|
mTransactionPool));
|
||||||
spyOn(mStartingSurfaceDrawer.mSplashscreenWindowCreator);
|
|
||||||
spyOn(mStartingSurfaceDrawer.mWindowRecords);
|
|
||||||
spyOn(mStartingSurfaceDrawer.mWindowlessRecords);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddSplashScreenSurface() {
|
public void testAddSplashScreenSurface() {
|
||||||
final int taskId = 1;
|
final int taskId = 1;
|
||||||
final StartingWindowInfo windowInfo =
|
final StartingWindowInfo windowInfo =
|
||||||
createWindowInfo(taskId, android.R.style.Theme, mBinder);
|
createWindowInfo(taskId, android.R.style.Theme);
|
||||||
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo,
|
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
|
||||||
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
|
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
|
||||||
waitHandlerIdle(mTestHandler);
|
waitHandlerIdle(mTestHandler);
|
||||||
verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator).addWindow(
|
verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
|
||||||
eq(taskId), eq(mBinder), any(), any(), any(),
|
|
||||||
eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
|
eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
|
||||||
|
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
|
||||||
|
|
||||||
StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
|
StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
|
||||||
removalInfo.taskId = windowInfo.taskInfo.taskId;
|
removalInfo.taskId = windowInfo.taskInfo.taskId;
|
||||||
mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
|
mStartingSurfaceDrawer.removeStartingWindow(removalInfo);
|
||||||
waitHandlerIdle(mTestHandler);
|
waitHandlerIdle(mTestHandler);
|
||||||
verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(false));
|
verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false));
|
||||||
assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0);
|
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFallbackDefaultTheme() {
|
public void testFallbackDefaultTheme() {
|
||||||
final int taskId = 1;
|
final int taskId = 1;
|
||||||
final StartingWindowInfo windowInfo =
|
final StartingWindowInfo windowInfo =
|
||||||
createWindowInfo(taskId, 0, mBinder);
|
createWindowInfo(taskId, 0);
|
||||||
final int[] theme = new int[1];
|
final int[] theme = new int[1];
|
||||||
doAnswer(invocation -> theme[0] = (Integer) invocation.callRealMethod())
|
doAnswer(invocation -> theme[0] = (Integer) invocation.callRealMethod())
|
||||||
.when(mStartingSurfaceDrawer.mSplashscreenWindowCreator)
|
.when(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any());
|
||||||
.getSplashScreenTheme(eq(0), any());
|
|
||||||
|
|
||||||
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo,
|
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
|
||||||
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
|
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
|
||||||
waitHandlerIdle(mTestHandler);
|
waitHandlerIdle(mTestHandler);
|
||||||
verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator)
|
verify(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any());
|
||||||
.getSplashScreenTheme(eq(0), any());
|
|
||||||
assertNotEquals(theme[0], 0);
|
assertNotEquals(theme[0], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +241,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception {
|
public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception {
|
||||||
final int taskId = 1;
|
final int taskId = 1;
|
||||||
final StartingWindowInfo windowInfo =
|
final StartingWindowInfo windowInfo =
|
||||||
createWindowInfo(taskId, android.R.style.Theme, mBinder);
|
createWindowInfo(taskId, android.R.style.Theme);
|
||||||
TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100),
|
TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100),
|
||||||
new Rect(0, 0, 0, 50), true /* hasImeSurface */);
|
new Rect(0, 0, 0, 50), true /* hasImeSurface */);
|
||||||
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
final IWindowSession session = WindowManagerGlobal.getWindowSession();
|
||||||
@@ -246,7 +270,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(),
|
when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(),
|
||||||
any())).thenReturn(mockSnapshotWindow);
|
any())).thenReturn(mockSnapshotWindow);
|
||||||
// Simulate a task snapshot window created with IME snapshot shown.
|
// Simulate a task snapshot window created with IME snapshot shown.
|
||||||
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot);
|
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot);
|
||||||
waitHandlerIdle(mTestHandler);
|
waitHandlerIdle(mTestHandler);
|
||||||
|
|
||||||
// Verify the task snapshot with IME snapshot will be removed when received the real IME
|
// Verify the task snapshot with IME snapshot will be removed when received the real IME
|
||||||
@@ -254,36 +278,27 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
// makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
|
// makeTaskSnapshotWindow shall call removeWindowSynced before there add a new
|
||||||
// StartingWindowRecord for the task.
|
// StartingWindowRecord for the task.
|
||||||
mStartingSurfaceDrawer.onImeDrawnOnTask(1);
|
mStartingSurfaceDrawer.onImeDrawnOnTask(1);
|
||||||
verify(mStartingSurfaceDrawer.mWindowRecords, times(2))
|
verify(mStartingSurfaceDrawer, times(2))
|
||||||
.removeWindow(any(), eq(true));
|
.removeWindowSynced(any(), eq(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClearAllWindows() {
|
public void testClearAllWindows() {
|
||||||
final int taskId = 1;
|
final int taskId = 1;
|
||||||
mStartingSurfaceDrawer.mWindowRecords.addRecord(taskId,
|
final StartingWindowInfo windowInfo =
|
||||||
new StartingSurfaceDrawer.StartingWindowRecord() {
|
createWindowInfo(taskId, android.R.style.Theme);
|
||||||
@Override
|
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder,
|
||||||
public void removeIfPossible(StartingWindowRemovalInfo info,
|
STARTING_WINDOW_TYPE_SPLASH_SCREEN);
|
||||||
boolean immediately) {
|
waitHandlerIdle(mTestHandler);
|
||||||
|
verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(),
|
||||||
|
eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN));
|
||||||
|
assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mStartingSurfaceDrawer.mWindowlessRecords.addRecord(taskId,
|
|
||||||
new StartingSurfaceDrawer.StartingWindowRecord() {
|
|
||||||
@Override
|
|
||||||
public void removeIfPossible(StartingWindowRemovalInfo info,
|
|
||||||
boolean immediately) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mStartingSurfaceDrawer.clearAllWindows();
|
mStartingSurfaceDrawer.clearAllWindows();
|
||||||
waitHandlerIdle(mTestHandler);
|
waitHandlerIdle(mTestHandler);
|
||||||
verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(true));
|
verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true));
|
||||||
assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0);
|
assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0);
|
||||||
verify(mStartingSurfaceDrawer.mWindowlessRecords).removeWindow(any(), eq(true));
|
|
||||||
assertEquals(mStartingSurfaceDrawer.mWindowlessRecords.recordSize(), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -336,7 +351,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
longAppDuration, longAppDuration));
|
longAppDuration, longAppDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
private StartingWindowInfo createWindowInfo(int taskId, int themeResId, IBinder appToken) {
|
private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
|
||||||
StartingWindowInfo windowInfo = new StartingWindowInfo();
|
StartingWindowInfo windowInfo = new StartingWindowInfo();
|
||||||
final ActivityInfo info = new ActivityInfo();
|
final ActivityInfo info = new ActivityInfo();
|
||||||
info.applicationInfo = new ApplicationInfo();
|
info.applicationInfo = new ApplicationInfo();
|
||||||
@@ -345,7 +360,6 @@ public class StartingSurfaceDrawerTests extends ShellTestCase {
|
|||||||
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
|
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
|
||||||
taskInfo.topActivityInfo = info;
|
taskInfo.topActivityInfo = info;
|
||||||
taskInfo.taskId = taskId;
|
taskInfo.taskId = taskId;
|
||||||
windowInfo.appToken = appToken;
|
|
||||||
windowInfo.targetActivityInfo = info;
|
windowInfo.targetActivityInfo = info;
|
||||||
windowInfo.taskInfo = taskInfo;
|
windowInfo.taskInfo = taskInfo;
|
||||||
windowInfo.topOpaqueWindowInsetsState = new InsetsState();
|
windowInfo.topOpaqueWindowInsetsState = new InsetsState();
|
||||||
|
|||||||
@@ -3512,10 +3512,7 @@ class Task extends TaskFragment {
|
|||||||
info.isKeyguardOccluded =
|
info.isKeyguardOccluded =
|
||||||
mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
|
mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
|
||||||
|
|
||||||
info.startingWindowTypeParameter = activity.mStartingData != null
|
info.startingWindowTypeParameter = activity.mStartingData.mTypeParams;
|
||||||
? activity.mStartingData.mTypeParams
|
|
||||||
: (StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED
|
|
||||||
| StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS);
|
|
||||||
if ((info.startingWindowTypeParameter
|
if ((info.startingWindowTypeParameter
|
||||||
& StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
|
& StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
|
||||||
final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
|
final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package com.android.server.wm;
|
package com.android.server.wm;
|
||||||
|
|
||||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
|
||||||
import static android.app.TaskInfo.cameraCompatControlStateToString;
|
import static android.app.TaskInfo.cameraCompatControlStateToString;
|
||||||
|
|
||||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
|
||||||
@@ -44,7 +43,6 @@ import android.view.Display;
|
|||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.window.ITaskOrganizer;
|
import android.window.ITaskOrganizer;
|
||||||
import android.window.ITaskOrganizerController;
|
import android.window.ITaskOrganizerController;
|
||||||
import android.window.IWindowlessStartingSurfaceCallback;
|
|
||||||
import android.window.SplashScreenView;
|
import android.window.SplashScreenView;
|
||||||
import android.window.StartingWindowInfo;
|
import android.window.StartingWindowInfo;
|
||||||
import android.window.StartingWindowRemovalInfo;
|
import android.window.StartingWindowRemovalInfo;
|
||||||
@@ -658,10 +656,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
|||||||
info.splashScreenThemeResId = launchTheme;
|
info.splashScreenThemeResId = launchTheme;
|
||||||
}
|
}
|
||||||
info.taskSnapshot = taskSnapshot;
|
info.taskSnapshot = taskSnapshot;
|
||||||
info.appToken = activity.token;
|
|
||||||
// make this happen prior than prepare surface
|
// make this happen prior than prepare surface
|
||||||
try {
|
try {
|
||||||
lastOrganizer.addStartingWindow(info);
|
lastOrganizer.addStartingWindow(info, activity.token);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Slog.e(TAG, "Exception sending onTaskStart callback", e);
|
Slog.e(TAG, "Exception sending onTaskStart callback", e);
|
||||||
return false;
|
return false;
|
||||||
@@ -707,55 +704,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a starting surface which attach on a given surface.
|
|
||||||
* @param activity Target activity, this isn't necessary to be the top activity.
|
|
||||||
* @param root The root surface which the created surface will attach on.
|
|
||||||
* @param taskSnapshot Whether to draw snapshot.
|
|
||||||
* @param callback Called when surface is drawn and attached to the root surface.
|
|
||||||
* @return The taskId, this is a token and should be used to remove the surface, even if
|
|
||||||
* the task was removed from hierarchy.
|
|
||||||
*/
|
|
||||||
int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root,
|
|
||||||
TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) {
|
|
||||||
final Task rootTask = task.getRootTask();
|
|
||||||
if (rootTask == null) {
|
|
||||||
return INVALID_TASK_ID;
|
|
||||||
}
|
|
||||||
final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
|
|
||||||
if (lastOrganizer == null) {
|
|
||||||
return INVALID_TASK_ID;
|
|
||||||
}
|
|
||||||
final StartingWindowInfo info = task.getStartingWindowInfo(activity);
|
|
||||||
info.taskInfo.taskDescription = activity.taskDescription;
|
|
||||||
info.taskSnapshot = taskSnapshot;
|
|
||||||
info.windowlessStartingSurfaceCallback = callback;
|
|
||||||
info.rootSurface = root;
|
|
||||||
try {
|
|
||||||
lastOrganizer.addStartingWindow(info);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Slog.e(TAG, "Exception sending addWindowlessStartingSurface ", e);
|
|
||||||
return INVALID_TASK_ID;
|
|
||||||
}
|
|
||||||
return task.mTaskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeWindowlessStartingSurface(int taskId, boolean immediately) {
|
|
||||||
final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
|
|
||||||
if (lastOrganizer == null || taskId == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
|
|
||||||
removalInfo.taskId = taskId;
|
|
||||||
removalInfo.windowlessSurface = true;
|
|
||||||
removalInfo.removeImmediately = immediately;
|
|
||||||
try {
|
|
||||||
lastOrganizer.removeStartingWindow(removalInfo);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Slog.e(TAG, "Exception sending removeWindowlessStartingSurface ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean copySplashScreenView(Task task) {
|
boolean copySplashScreenView(Task task) {
|
||||||
final Task rootTask = task.getRootTask();
|
final Task rootTask = task.getRootTask();
|
||||||
if (rootTask == null) {
|
if (rootTask == null) {
|
||||||
|
|||||||
@@ -1023,7 +1023,7 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
|||||||
RunningTaskInfo mInfo;
|
RunningTaskInfo mInfo;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addStartingWindow(StartingWindowInfo info) { }
|
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
|
||||||
@Override
|
@Override
|
||||||
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { }
|
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { }
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1583,10 +1583,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addStartingWindow(StartingWindowInfo info) {
|
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
|
||||||
synchronized (mWMService.mGlobalLock) {
|
synchronized (mWMService.mGlobalLock) {
|
||||||
final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
|
final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
|
||||||
info.appToken);
|
appToken);
|
||||||
IWindow iWindow = mock(IWindow.class);
|
IWindow iWindow = mock(IWindow.class);
|
||||||
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
|
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
|
||||||
final WindowState window = WindowTestsBase.createWindow(null,
|
final WindowState window = WindowTestsBase.createWindow(null,
|
||||||
@@ -1596,8 +1596,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
|||||||
iWindow,
|
iWindow,
|
||||||
mPowerManagerWrapper);
|
mPowerManagerWrapper);
|
||||||
activity.mStartingWindow = window;
|
activity.mStartingWindow = window;
|
||||||
mAppWindowMap.put(info.appToken, window);
|
mAppWindowMap.put(appToken, window);
|
||||||
mTaskAppMap.put(info.taskInfo.taskId, info.appToken);
|
mTaskAppMap.put(info.taskInfo.taskId, appToken);
|
||||||
}
|
}
|
||||||
if (mRunnableWhenAddingSplashScreen != null) {
|
if (mRunnableWhenAddingSplashScreen != null) {
|
||||||
mRunnableWhenAddingSplashScreen.run();
|
mRunnableWhenAddingSplashScreen.run();
|
||||||
|
|||||||
Reference in New Issue
Block a user