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