1 /* 2 * Copyright (C) 2020 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.fullscreen; 18 19 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; 20 import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; 21 22 import android.app.ActivityManager; 23 import android.app.ActivityManager.RunningTaskInfo; 24 import android.graphics.Point; 25 import android.util.SparseArray; 26 import android.view.SurfaceControl; 27 28 import androidx.annotation.NonNull; 29 30 import com.android.internal.protolog.ProtoLog; 31 import com.android.wm.shell.ShellTaskOrganizer; 32 import com.android.wm.shell.common.SyncTransactionQueue; 33 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; 34 import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider; 35 import com.android.wm.shell.protolog.ShellProtoLogGroup; 36 import com.android.wm.shell.recents.RecentTasksController; 37 import com.android.wm.shell.sysui.ShellInit; 38 import com.android.wm.shell.transition.Transitions; 39 import com.android.wm.shell.windowdecor.WindowDecorViewModel; 40 41 import java.io.PrintWriter; 42 import java.util.Optional; 43 44 /** 45 * Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}. 46 * @param <T> the type of window decoration instance 47 */ 48 public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { 49 private static final String TAG = "FullscreenTaskListener"; 50 51 private final ShellTaskOrganizer mShellTaskOrganizer; 52 53 private final SparseArray<State> mTasks = new SparseArray<>(); 54 55 private static class State { 56 RunningTaskInfo mTaskInfo; 57 SurfaceControl mLeash; 58 } 59 private final SyncTransactionQueue mSyncQueue; 60 private final Optional<RecentTasksController> mRecentTasksOptional; 61 private final Optional<WindowDecorViewModel> mWindowDecorViewModelOptional; 62 private final Optional<DesktopWallpaperActivityTokenProvider> 63 mDesktopWallpaperActivityTokenProviderOptional; 64 65 /** 66 * This constructor is used by downstream products. 67 */ FullscreenTaskListener(SyncTransactionQueue syncQueue)68 public FullscreenTaskListener(SyncTransactionQueue syncQueue) { 69 this(null /* shellInit */, null /* shellTaskOrganizer */, syncQueue, Optional.empty(), 70 Optional.empty(), Optional.empty()); 71 } 72 FullscreenTaskListener(ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Optional<RecentTasksController> recentTasksOptional, Optional<WindowDecorViewModel> windowDecorViewModelOptional, Optional<DesktopWallpaperActivityTokenProvider> desktopWallpaperActivityTokenProviderOptional)73 public FullscreenTaskListener(ShellInit shellInit, 74 ShellTaskOrganizer shellTaskOrganizer, 75 SyncTransactionQueue syncQueue, 76 Optional<RecentTasksController> recentTasksOptional, 77 Optional<WindowDecorViewModel> windowDecorViewModelOptional, 78 Optional<DesktopWallpaperActivityTokenProvider> 79 desktopWallpaperActivityTokenProviderOptional) { 80 mShellTaskOrganizer = shellTaskOrganizer; 81 mSyncQueue = syncQueue; 82 mRecentTasksOptional = recentTasksOptional; 83 mWindowDecorViewModelOptional = windowDecorViewModelOptional; 84 mDesktopWallpaperActivityTokenProviderOptional = 85 desktopWallpaperActivityTokenProviderOptional; 86 // Note: Some derivative FullscreenTaskListener implementations do not use ShellInit 87 if (shellInit != null) { 88 shellInit.addInitCallback(this::onInit, this); 89 } 90 } 91 onInit()92 private void onInit() { 93 mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FULLSCREEN); 94 } 95 96 @Override onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash)97 public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { 98 if (mTasks.get(taskInfo.taskId) != null) { 99 throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); 100 } 101 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", 102 taskInfo.taskId); 103 final Point positionInParent = taskInfo.positionInParent; 104 final State state = new State(); 105 state.mLeash = leash; 106 state.mTaskInfo = taskInfo; 107 mTasks.put(taskInfo.taskId, state); 108 109 if (Transitions.ENABLE_SHELL_TRANSITIONS) return; 110 updateRecentsForVisibleFullscreenTask(taskInfo); 111 boolean createdWindowDecor = false; 112 if (mWindowDecorViewModelOptional.isPresent()) { 113 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 114 createdWindowDecor = mWindowDecorViewModelOptional.get() 115 .onTaskOpening(taskInfo, leash, t, t); 116 t.apply(); 117 } 118 if (!createdWindowDecor) { 119 mSyncQueue.runInSync(t -> { 120 if (!leash.isValid()) { 121 // Task vanished before sync completion 122 return; 123 } 124 // Reset several properties back to fullscreen (PiP, for example, leaves all these 125 // properties in a bad state). 126 t.setWindowCrop(leash, null); 127 t.setPosition(leash, positionInParent.x, positionInParent.y); 128 t.setAlpha(leash, 1f); 129 t.setMatrix(leash, 1, 0, 0, 1); 130 if (taskInfo.isVisible) { 131 t.show(leash); 132 } 133 }); 134 } 135 } 136 137 @Override onTaskInfoChanged(RunningTaskInfo taskInfo)138 public void onTaskInfoChanged(RunningTaskInfo taskInfo) { 139 final State state = mTasks.get(taskInfo.taskId); 140 final Point oldPositionInParent = state.mTaskInfo.positionInParent; 141 boolean oldVisible = state.mTaskInfo.isVisible; 142 143 if (mWindowDecorViewModelOptional.isPresent()) { 144 mWindowDecorViewModelOptional.get().onTaskInfoChanged(taskInfo); 145 } 146 state.mTaskInfo = taskInfo; 147 if (Transitions.ENABLE_SHELL_TRANSITIONS) return; 148 updateRecentsForVisibleFullscreenTask(taskInfo); 149 150 final Point positionInParent = state.mTaskInfo.positionInParent; 151 boolean positionInParentChanged = !oldPositionInParent.equals(positionInParent); 152 boolean becameVisible = !oldVisible && state.mTaskInfo.isVisible; 153 154 if (becameVisible || positionInParentChanged) { 155 mSyncQueue.runInSync(t -> { 156 if (!state.mLeash.isValid()) { 157 // Task vanished before sync completion 158 return; 159 } 160 if (becameVisible) { 161 t.show(state.mLeash); 162 } 163 t.setPosition(state.mLeash, positionInParent.x, positionInParent.y); 164 }); 165 } 166 } 167 168 @Override onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)169 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 170 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", 171 taskInfo.taskId); 172 mTasks.remove(taskInfo.taskId); 173 mWindowDecorViewModelOptional.ifPresent(v -> v.onTaskVanished(taskInfo)); 174 mDesktopWallpaperActivityTokenProviderOptional.ifPresent( 175 provider -> { 176 if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) { 177 provider.removeToken(taskInfo.getToken()); 178 } 179 }); 180 if (Transitions.ENABLE_SHELL_TRANSITIONS) return; 181 if (mWindowDecorViewModelOptional.isPresent()) { 182 mWindowDecorViewModelOptional.get().destroyWindowDecoration(taskInfo); 183 } 184 } 185 updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo)186 private void updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo) { 187 mRecentTasksOptional.ifPresent(recentTasks -> { 188 if (taskInfo.isVisible) { 189 // Remove any persisted splits if either tasks are now made fullscreen and visible 190 recentTasks.removeSplitPair(taskInfo.taskId); 191 } 192 }); 193 } 194 195 @Override attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b)196 public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { 197 b.setParent(findTaskSurface(taskId)); 198 } 199 200 @Override reparentChildSurfaceToTask(int taskId, SurfaceControl sc, SurfaceControl.Transaction t)201 public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, 202 SurfaceControl.Transaction t) { 203 t.reparent(sc, findTaskSurface(taskId)); 204 } 205 findTaskSurface(int taskId)206 private SurfaceControl findTaskSurface(int taskId) { 207 if (!mTasks.contains(taskId)) { 208 throw new IllegalArgumentException("There is no surface for taskId=" + taskId); 209 } 210 return mTasks.get(taskId).mLeash; 211 } 212 213 @Override dump(@onNull PrintWriter pw, String prefix)214 public void dump(@NonNull PrintWriter pw, String prefix) { 215 final String innerPrefix = prefix + " "; 216 pw.println(prefix + this); 217 pw.println(innerPrefix + mTasks.size() + " Tasks"); 218 } 219 220 @Override toString()221 public String toString() { 222 return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN); 223 } 224 } 225