1 /* 2 * Copyright (C) 2022 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.car.carlauncher; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 23 import static android.view.Display.DEFAULT_DISPLAY; 24 25 import static com.android.car.carlauncher.TaskViewManager.DBG; 26 27 import android.app.Activity; 28 import android.app.ActivityManager; 29 import android.util.Log; 30 import android.view.SurfaceControl; 31 import android.window.WindowContainerTransaction; 32 33 import com.android.wm.shell.ShellTaskOrganizer; 34 import com.android.wm.shell.common.SyncTransactionQueue; 35 36 import java.util.concurrent.Executor; 37 38 /** 39 * A {@link CarTaskView} that can act as a default app container. A default app container is the 40 * container where all apps open by default. 41 */ 42 final class LaunchRootCarTaskView extends CarTaskView { 43 private static final String TAG = LaunchRootCarTaskView.class.getSimpleName(); 44 45 private final Executor mCallbackExecutor; 46 private final LaunchRootCarTaskViewCallbacks mCallbacks; 47 private final ShellTaskOrganizer mShellTaskOrganizer; 48 private final SyncTransactionQueue mSyncQueue; 49 private final ShellTaskOrganizer.TaskListener mRootTaskListener; 50 51 private ActivityManager.RunningTaskInfo mLaunchRootTask; 52 53 private final ShellTaskOrganizer.TaskListener mRootTaskListenerWrapper = 54 new ShellTaskOrganizer.TaskListener() { 55 @Override 56 public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, 57 SurfaceControl leash) { 58 // The first call to onTaskAppeared() is always for the root-task. 59 if (mLaunchRootTask == null && !taskInfo.hasParentTask()) { 60 setRootTaskAsLaunchRoot(taskInfo); 61 LaunchRootCarTaskView.this.onTaskAppeared(taskInfo, leash); 62 mCallbackExecutor.execute(() -> mCallbacks.onTaskViewReady()); 63 return; 64 } 65 66 if (DBG) { 67 Log.d(TAG, "onTaskAppeared " + taskInfo.taskId + " - " 68 + taskInfo.baseActivity); 69 } 70 71 72 mRootTaskListener.onTaskAppeared(taskInfo, leash); 73 } 74 75 @Override 76 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { 77 if (mLaunchRootTask != null 78 && mLaunchRootTask.taskId == taskInfo.taskId) { 79 LaunchRootCarTaskView.this.onTaskInfoChanged(taskInfo); 80 if (DBG) { 81 Log.d(TAG, "got onTaskInfoChanged for the launch root task. Not " 82 + "forwarding this to root task listener"); 83 } 84 return; 85 } 86 if (DBG) { 87 Log.d(TAG, "onTaskInfoChanged " + taskInfo.taskId + " - " 88 + taskInfo.baseActivity); 89 } 90 91 mRootTaskListener.onTaskInfoChanged(taskInfo); 92 } 93 94 @Override 95 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 96 if (DBG) { 97 Log.d(TAG, "onTaskVanished " + taskInfo.taskId + " - " 98 + taskInfo.baseActivity); 99 } 100 if (mLaunchRootTask != null 101 && mLaunchRootTask.taskId == taskInfo.taskId) { 102 LaunchRootCarTaskView.this.onTaskVanished(taskInfo); 103 if (DBG) { 104 Log.d(TAG, "got onTaskVanished for the launch root task. Not " 105 + "forwarding this to root task listener"); 106 } 107 return; 108 } 109 110 mRootTaskListener.onTaskVanished(taskInfo); 111 } 112 113 @Override 114 public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { 115 mRootTaskListener.onBackPressedOnTaskRoot(taskInfo); 116 } 117 }; 118 LaunchRootCarTaskView(Activity context, ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, Executor callbackExecutor, LaunchRootCarTaskViewCallbacks callbacks, ShellTaskOrganizer.TaskListener rootTaskListener)119 public LaunchRootCarTaskView(Activity context, 120 ShellTaskOrganizer organizer, 121 SyncTransactionQueue syncQueue, 122 Executor callbackExecutor, 123 LaunchRootCarTaskViewCallbacks callbacks, 124 ShellTaskOrganizer.TaskListener rootTaskListener) { 125 super(context, organizer, syncQueue); 126 mCallbacks = callbacks; 127 mCallbackExecutor = callbackExecutor; 128 mShellTaskOrganizer = organizer; 129 mSyncQueue = syncQueue; 130 mRootTaskListener = rootTaskListener; 131 132 mCallbackExecutor.execute(() -> mCallbacks.onTaskViewCreated(this)); 133 } 134 135 @Override onCarTaskViewInitialized()136 protected void onCarTaskViewInitialized() { 137 super.onCarTaskViewInitialized(); 138 mShellTaskOrganizer.getExecutor().execute(() -> { 139 // Should run on shell's executor 140 mShellTaskOrganizer.createRootTask(DEFAULT_DISPLAY, 141 WINDOWING_MODE_MULTI_WINDOW, 142 mRootTaskListenerWrapper); 143 }); 144 } 145 146 @Override notifyReleased()147 protected void notifyReleased() { 148 super.notifyReleased(); 149 clearLaunchRootTask(); 150 } 151 clearLaunchRootTask()152 private void clearLaunchRootTask() { 153 if (mLaunchRootTask == null) { 154 Log.w(TAG, "Unable to clear launch root task because it is not created."); 155 return; 156 } 157 WindowContainerTransaction wct = new WindowContainerTransaction(); 158 wct.setLaunchRoot(mLaunchRootTask.token, null, null); 159 mSyncQueue.queue(wct); 160 // Should run on shell's executor 161 mShellTaskOrganizer.deleteRootTask(mLaunchRootTask.token); 162 mLaunchRootTask = null; 163 } 164 setRootTaskAsLaunchRoot(ActivityManager.RunningTaskInfo taskInfo)165 private void setRootTaskAsLaunchRoot(ActivityManager.RunningTaskInfo taskInfo) { 166 mLaunchRootTask = taskInfo; 167 WindowContainerTransaction wct = new WindowContainerTransaction(); 168 wct.setLaunchRoot(taskInfo.token, 169 new int[]{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}, 170 new int[]{ACTIVITY_TYPE_STANDARD}) 171 .reorder(taskInfo.token, true); 172 mSyncQueue.queue(wct); 173 } 174 } 175