| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.carlauncher; |
| |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| |
| import static com.android.car.carlauncher.TaskViewManager.DBG; |
| |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.util.Log; |
| import android.view.SurfaceControl; |
| import android.window.WindowContainerTransaction; |
| |
| import com.android.wm.shell.ShellTaskOrganizer; |
| import com.android.wm.shell.common.SyncTransactionQueue; |
| |
| import java.util.concurrent.Executor; |
| |
| /** |
| * A {@link CarTaskView} that can act as a default app container. A default app container is the |
| * container where all apps open by default. |
| */ |
| final class LaunchRootCarTaskView extends CarTaskView { |
| private static final String TAG = LaunchRootCarTaskView.class.getSimpleName(); |
| |
| private final Executor mCallbackExecutor; |
| private final LaunchRootCarTaskViewCallbacks mCallbacks; |
| private final ShellTaskOrganizer mShellTaskOrganizer; |
| private final SyncTransactionQueue mSyncQueue; |
| private final ShellTaskOrganizer.TaskListener mRootTaskListener; |
| |
| private ActivityManager.RunningTaskInfo mLaunchRootTask; |
| |
| private final ShellTaskOrganizer.TaskListener mRootTaskListenerWrapper = |
| new ShellTaskOrganizer.TaskListener() { |
| @Override |
| public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, |
| SurfaceControl leash) { |
| // The first call to onTaskAppeared() is always for the root-task. |
| if (mLaunchRootTask == null && !taskInfo.hasParentTask()) { |
| setRootTaskAsLaunchRoot(taskInfo); |
| LaunchRootCarTaskView.this.onTaskAppeared(taskInfo, leash); |
| mCallbackExecutor.execute(() -> mCallbacks.onTaskViewReady()); |
| return; |
| } |
| |
| if (DBG) { |
| Log.d(TAG, "onTaskAppeared " + taskInfo.taskId + " - " |
| + taskInfo.baseActivity); |
| } |
| |
| |
| mRootTaskListener.onTaskAppeared(taskInfo, leash); |
| } |
| |
| @Override |
| public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { |
| if (mLaunchRootTask != null |
| && mLaunchRootTask.taskId == taskInfo.taskId) { |
| LaunchRootCarTaskView.this.onTaskInfoChanged(taskInfo); |
| if (DBG) { |
| Log.d(TAG, "got onTaskInfoChanged for the launch root task. Not " |
| + "forwarding this to root task listener"); |
| } |
| return; |
| } |
| if (DBG) { |
| Log.d(TAG, "onTaskInfoChanged " + taskInfo.taskId + " - " |
| + taskInfo.baseActivity); |
| } |
| |
| mRootTaskListener.onTaskInfoChanged(taskInfo); |
| } |
| |
| @Override |
| public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { |
| if (DBG) { |
| Log.d(TAG, "onTaskVanished " + taskInfo.taskId + " - " |
| + taskInfo.baseActivity); |
| } |
| if (mLaunchRootTask != null |
| && mLaunchRootTask.taskId == taskInfo.taskId) { |
| LaunchRootCarTaskView.this.onTaskVanished(taskInfo); |
| if (DBG) { |
| Log.d(TAG, "got onTaskVanished for the launch root task. Not " |
| + "forwarding this to root task listener"); |
| } |
| return; |
| } |
| |
| mRootTaskListener.onTaskVanished(taskInfo); |
| } |
| }; |
| |
| public LaunchRootCarTaskView(Activity context, |
| ShellTaskOrganizer organizer, |
| SyncTransactionQueue syncQueue, |
| Executor callbackExecutor, |
| LaunchRootCarTaskViewCallbacks callbacks, |
| ShellTaskOrganizer.TaskListener rootTaskListener) { |
| super(context, organizer, syncQueue); |
| mCallbacks = callbacks; |
| mCallbackExecutor = callbackExecutor; |
| mShellTaskOrganizer = organizer; |
| mSyncQueue = syncQueue; |
| mRootTaskListener = rootTaskListener; |
| |
| mCallbackExecutor.execute(() -> mCallbacks.onTaskViewCreated(this)); |
| } |
| |
| @Override |
| protected void onCarTaskViewInitialized() { |
| super.onCarTaskViewInitialized(); |
| mShellTaskOrganizer.getExecutor().execute(() -> { |
| // Should run on shell's executor |
| mShellTaskOrganizer.createRootTask(DEFAULT_DISPLAY, |
| WINDOWING_MODE_MULTI_WINDOW, |
| mRootTaskListenerWrapper); |
| }); |
| } |
| |
| @Override |
| protected void notifyReleased() { |
| super.notifyReleased(); |
| clearLaunchRootTask(); |
| } |
| |
| private void clearLaunchRootTask() { |
| if (mLaunchRootTask == null) { |
| Log.w(TAG, "Unable to clear launch root task because it is not created."); |
| return; |
| } |
| WindowContainerTransaction wct = new WindowContainerTransaction(); |
| wct.setLaunchRoot(mLaunchRootTask.token, null, null); |
| mSyncQueue.queue(wct); |
| // Should run on shell's executor |
| mShellTaskOrganizer.deleteRootTask(mLaunchRootTask.token); |
| mLaunchRootTask = null; |
| } |
| |
| private void setRootTaskAsLaunchRoot(ActivityManager.RunningTaskInfo taskInfo) { |
| mLaunchRootTask = taskInfo; |
| WindowContainerTransaction wct = new WindowContainerTransaction(); |
| wct.setLaunchRoot(taskInfo.token, |
| new int[]{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}, |
| new int[]{ACTIVITY_TYPE_STANDARD}) |
| .reorder(taskInfo.token, true); |
| mSyncQueue.queue(wct); |
| } |
| } |