1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.startingsurface.phone;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
21 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
22 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
23 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
24 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
25 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED;
26 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
27 import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
28 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
29 import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
30 import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
31 import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN;
32 
33 import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_SPLASH_SCREEN;
34 import static com.android.wm.shell.startingsurface.StartingWindowController.DEBUG_TASK_SNAPSHOT;
35 
36 import android.util.Slog;
37 import android.window.StartingWindowInfo;
38 import android.window.TaskSnapshot;
39 
40 import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
41 
42 /**
43  * Algorithm for determining the type of a new starting window on handheld devices.
44  * At the moment also used on Android Auto.
45  */
46 public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgorithm {
47     private static final String TAG = PhoneStartingWindowTypeAlgorithm.class.getSimpleName();
48 
49     @Override
getSuggestedWindowType(StartingWindowInfo windowInfo)50     public int getSuggestedWindowType(StartingWindowInfo windowInfo) {
51         final int parameter = windowInfo.startingWindowTypeParameter;
52         final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
53         final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
54         final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
55         final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
56         final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
57         final boolean useEmptySplashScreen =
58                 (parameter & TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN) != 0;
59         final boolean legacySplashScreen =
60                 ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0);
61         final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME;
62 
63         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
64             Slog.d(TAG, "preferredStartingWindowType newTask:" + newTask
65                     + " taskSwitch:" + taskSwitch
66                     + " processRunning:" + processRunning
67                     + " allowTaskSnapshot:" + allowTaskSnapshot
68                     + " activityCreated:" + activityCreated
69                     + " useEmptySplashScreen:" + useEmptySplashScreen
70                     + " legacySplashScreen:" + legacySplashScreen
71                     + " topIsHome:" + topIsHome);
72         }
73 
74         if (!topIsHome) {
75             if (!processRunning || newTask || (taskSwitch && !activityCreated)) {
76                 return useEmptySplashScreen
77                         ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
78                         : legacySplashScreen
79                                 ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
80                                 : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
81             }
82         }
83         if (taskSwitch && allowTaskSnapshot) {
84             if (isSnapshotCompatible(windowInfo)) {
85                 return STARTING_WINDOW_TYPE_SNAPSHOT;
86             }
87             if (!topIsHome) {
88                 return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
89             }
90         }
91         return STARTING_WINDOW_TYPE_NONE;
92     }
93 
94 
95     /**
96      * Returns {@code true} if the task snapshot is compatible with this activity (at least the
97      * rotation must be the same).
98      */
isSnapshotCompatible(StartingWindowInfo windowInfo)99     private boolean isSnapshotCompatible(StartingWindowInfo windowInfo) {
100         final TaskSnapshot snapshot = windowInfo.taskSnapshot;
101         if (snapshot == null) {
102             if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
103                 Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId);
104             }
105             return false;
106         }
107         if (!snapshot.getTopActivityComponent().equals(windowInfo.taskInfo.topActivity)) {
108             if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
109                 Slog.d(TAG, "isSnapshotCompatible obsoleted snapshot "
110                         + windowInfo.taskInfo.topActivity);
111             }
112             return false;
113         }
114 
115         final int taskRotation = windowInfo.taskInfo.configuration
116                 .windowConfiguration.getRotation();
117         final int snapshotRotation = snapshot.getRotation();
118         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
119             Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation
120                     + " snapshot " + snapshotRotation);
121         }
122         return taskRotation == snapshotRotation;
123     }
124 }
125