blob: 6e38e421d4b611f315c73e0ae9e630e1432b7b86 [file] [log] [blame]
/*
* Copyright (C) 2020 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.wm.shell.fullscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.graphics.Point;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.Optional;
/**
* Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
*/
public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener";
private final SyncTransactionQueue mSyncQueue;
private final FullscreenUnfoldController mFullscreenUnfoldController;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final SparseArray<TaskData> mDataByTaskId = new SparseArray<>();
private final AnimatableTasksListener mAnimatableTasksListener = new AnimatableTasksListener();
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
Optional<FullscreenUnfoldController> unfoldController) {
this(syncQueue, unfoldController, Optional.empty());
}
public FullscreenTaskListener(SyncTransactionQueue syncQueue,
Optional<FullscreenUnfoldController> unfoldController,
Optional<RecentTasksController> recentTasks) {
mSyncQueue = syncQueue;
mFullscreenUnfoldController = unfoldController.orElse(null);
mRecentTasksOptional = recentTasks;
}
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
if (mDataByTaskId.get(taskInfo.taskId) != null) {
throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent));
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
t.setWindowCrop(leash, null);
t.setPosition(leash, positionInParent.x, positionInParent.y);
t.setAlpha(leash, 1f);
t.setMatrix(leash, 1, 0, 0, 1);
t.show(leash);
});
mAnimatableTasksListener.onTaskAppeared(taskInfo);
updateRecentsForVisibleFullscreenTask(taskInfo);
}
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
mAnimatableTasksListener.onTaskInfoChanged(taskInfo);
updateRecentsForVisibleFullscreenTask(taskInfo);
final TaskData data = mDataByTaskId.get(taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
if (!positionInParent.equals(data.positionInParent)) {
data.positionInParent.set(positionInParent.x, positionInParent.y);
mSyncQueue.runInSync(t -> {
t.setPosition(data.surface, positionInParent.x, positionInParent.y);
});
}
}
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
if (mDataByTaskId.get(taskInfo.taskId) == null) {
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
return;
}
mAnimatableTasksListener.onTaskVanished(taskInfo);
mDataByTaskId.remove(taskInfo.taskId);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
taskInfo.taskId);
}
private void updateRecentsForVisibleFullscreenTask(RunningTaskInfo taskInfo) {
mRecentTasksOptional.ifPresent(recentTasks -> {
if (taskInfo.isVisible) {
// Remove any persisted splits if either tasks are now made fullscreen and visible
recentTasks.removeSplitPair(taskInfo.taskId);
}
});
}
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
if (!mDataByTaskId.contains(taskId)) {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
b.setParent(mDataByTaskId.get(taskId).surface);
}
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + this);
pw.println(innerPrefix + mDataByTaskId.size() + " Tasks");
}
@Override
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
/**
* Per-task data for each managed task.
*/
private static class TaskData {
public final SurfaceControl surface;
public final Point positionInParent;
public TaskData(SurfaceControl surface, Point positionInParent) {
this.surface = surface;
this.positionInParent = positionInParent;
}
}
class AnimatableTasksListener {
private final SparseBooleanArray mTaskIds = new SparseBooleanArray();
public void onTaskAppeared(RunningTaskInfo taskInfo) {
final boolean isApplicable = isAnimatable(taskInfo);
if (isApplicable) {
mTaskIds.put(taskInfo.taskId, true);
if (mFullscreenUnfoldController != null) {
SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
}
}
}
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
final boolean isApplicable = isAnimatable(taskInfo);
if (isCurrentlyApplicable) {
if (isApplicable) {
// Still applicable, send update
if (mFullscreenUnfoldController != null) {
mFullscreenUnfoldController.onTaskInfoChanged(taskInfo);
}
} else {
// Became inapplicable
if (mFullscreenUnfoldController != null) {
mFullscreenUnfoldController.onTaskVanished(taskInfo);
}
mTaskIds.put(taskInfo.taskId, false);
}
} else {
if (isApplicable) {
// Became applicable
mTaskIds.put(taskInfo.taskId, true);
if (mFullscreenUnfoldController != null) {
SurfaceControl leash = mDataByTaskId.get(taskInfo.taskId).surface;
mFullscreenUnfoldController.onTaskAppeared(taskInfo, leash);
}
}
}
}
public void onTaskVanished(RunningTaskInfo taskInfo) {
final boolean isCurrentlyApplicable = mTaskIds.get(taskInfo.taskId);
if (isCurrentlyApplicable && mFullscreenUnfoldController != null) {
mFullscreenUnfoldController.onTaskVanished(taskInfo);
}
mTaskIds.put(taskInfo.taskId, false);
}
private boolean isAnimatable(TaskInfo taskInfo) {
// Filter all visible tasks that are not launcher tasks
// We do not animate launcher as it handles the animation by itself
return taskInfo != null && taskInfo.isVisible && taskInfo.getConfiguration()
.windowConfiguration.getActivityType() != ACTIVITY_TYPE_HOME;
}
}
}