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.car.carlauncher.taskstack; 18 19 import android.app.ActivityManager; 20 import android.app.ActivityTaskManager; 21 import android.app.TaskStackListener; 22 import android.content.ComponentName; 23 import android.os.Build; 24 import android.os.Handler; 25 import android.os.RemoteException; 26 import android.util.Log; 27 28 import com.android.wm.shell.common.HandlerExecutor; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.concurrent.Executor; 33 34 /** Organizer of many task stack listeners for the car launcher application. */ 35 public class TaskStackChangeListeners { 36 private static final String TAG = TaskStackChangeListeners.class.getSimpleName(); 37 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 38 private static final TaskStackChangeListeners INSTANCE = new TaskStackChangeListeners( 39 new HandlerExecutor(Handler.getMain())); 40 41 private final Impl mImpl; 42 TaskStackChangeListeners(Executor executor)43 private TaskStackChangeListeners(Executor executor) { 44 mImpl = new Impl(executor); 45 } 46 47 /** Returns a singleton instance of the {@link TaskStackChangeListeners}. */ getInstance()48 public static TaskStackChangeListeners getInstance() { 49 return INSTANCE; 50 } 51 52 /** 53 * Registers a task stack listener with the system. 54 * This should be called on the main thread. 55 */ registerTaskStackListener(TaskStackListener listener)56 public void registerTaskStackListener(TaskStackListener listener) { 57 synchronized (mImpl) { 58 mImpl.addListener(listener); 59 } 60 if (DEBUG) { 61 Log.d(TAG, "registerTaskStackListener: " + listener); 62 } 63 } 64 65 /** 66 * Unregisters a task stack listener with the system. 67 * This should be called on the main thread. 68 */ unregisterTaskStackListener(TaskStackListener listener)69 public void unregisterTaskStackListener(TaskStackListener listener) { 70 synchronized (mImpl) { 71 mImpl.removeListener(listener); 72 } 73 if (DEBUG) { 74 Log.d(TAG, "unregisterTaskStackListener: " + listener); 75 } 76 } 77 78 private static class Impl extends TaskStackListener { 79 80 private final List<TaskStackListener> mTaskStackListeners = new ArrayList<>(); 81 82 private final Executor mExecutor; 83 private boolean mRegistered; 84 Impl(Executor executor)85 Impl(Executor executor) { 86 mExecutor = executor; 87 } 88 addListener(TaskStackListener listener)89 public void addListener(TaskStackListener listener) { 90 synchronized (mTaskStackListeners) { 91 mTaskStackListeners.add(listener); 92 } 93 if (!mRegistered) { 94 // Register mTaskStackListener to IActivityManager only once if needed. 95 try { 96 ActivityTaskManager.getService().registerTaskStackListener(this); 97 mRegistered = true; 98 } catch (Exception e) { 99 Log.w(TAG, "Failed to call registerTaskStackListener", e); 100 } 101 } 102 } 103 removeListener(TaskStackListener listener)104 public void removeListener(TaskStackListener listener) { 105 boolean isEmpty; 106 synchronized (mTaskStackListeners) { 107 mTaskStackListeners.remove(listener); 108 isEmpty = mTaskStackListeners.isEmpty(); 109 } 110 if (isEmpty && mRegistered) { 111 // Unregister mTaskStackListener once we have no more listeners 112 try { 113 ActivityTaskManager.getService().unregisterTaskStackListener(this); 114 mRegistered = false; 115 } catch (Exception e) { 116 Log.w(TAG, "Failed to call unregisterTaskStackListener", e); 117 } 118 } 119 } 120 121 @Override onTaskStackChanged()122 public void onTaskStackChanged() throws RemoteException { 123 mExecutor.execute(() -> { 124 synchronized (mTaskStackListeners) { 125 for (int i = 0; i < mTaskStackListeners.size(); i++) { 126 try { 127 mTaskStackListeners.get(i).onTaskStackChanged(); 128 } catch (RemoteException e) { 129 Log.e(TAG, "onTaskStackChanged failed", e); 130 } 131 } 132 } 133 }); 134 } 135 136 @Override onTaskCreated(int taskId, ComponentName componentName)137 public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException { 138 mExecutor.execute(() -> { 139 synchronized (mTaskStackListeners) { 140 for (int i = 0; i < mTaskStackListeners.size(); i++) { 141 try { 142 mTaskStackListeners.get(i).onTaskCreated(taskId, componentName); 143 } catch (RemoteException e) { 144 Log.e(TAG, "onTaskCreated failed", e); 145 } 146 } 147 } 148 }); 149 } 150 151 @Override onTaskRemoved(int taskId)152 public void onTaskRemoved(int taskId) throws RemoteException { 153 mExecutor.execute(() -> { 154 synchronized (mTaskStackListeners) { 155 for (int i = 0; i < mTaskStackListeners.size(); i++) { 156 try { 157 mTaskStackListeners.get(i).onTaskRemoved(taskId); 158 } catch (RemoteException e) { 159 Log.e(TAG, "onTaskRemoved failed", e); 160 } 161 } 162 } 163 }); 164 } 165 166 @Override onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo)167 public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) 168 throws RemoteException { 169 mExecutor.execute(() -> { 170 synchronized (mTaskStackListeners) { 171 for (int i = 0; i < mTaskStackListeners.size(); i++) { 172 try { 173 mTaskStackListeners.get(i).onTaskMovedToFront(taskInfo); 174 } catch (RemoteException e) { 175 Log.e(TAG, "onTaskMovedToFront failed", e); 176 } 177 } 178 } 179 }); 180 } 181 182 @Override onTaskFocusChanged(int taskId, boolean focused)183 public void onTaskFocusChanged(int taskId, boolean focused) { 184 mExecutor.execute(() -> { 185 synchronized (mTaskStackListeners) { 186 for (int i = 0; i < mTaskStackListeners.size(); i++) { 187 mTaskStackListeners.get(i).onTaskFocusChanged(taskId, focused); 188 } 189 } 190 }); 191 } 192 193 @Override onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible)194 public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, 195 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { 196 mExecutor.execute(() -> { 197 synchronized (mTaskStackListeners) { 198 for (TaskStackListener listener : mTaskStackListeners) { 199 try { 200 listener.onActivityRestartAttempt( 201 task, homeTaskVisible, clearedTask, wasVisible); 202 } catch (RemoteException e) { 203 Log.e(TAG, "onActivityRestartAttempt failed", e); 204 } 205 } 206 } 207 }); 208 } 209 } 210 } 211