blob: b5636777568eca3bc943f7680c724b2e4a6783c1 [file] [log] [blame]
/*
* 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 androidx.window.extensions.embedding;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.Activity;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
import android.window.TaskFragmentInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/** Represents TaskFragments and split pairs below a Task. */
class TaskContainer {
/** The unique task id. */
private final int mTaskId;
/** Available window bounds of this Task. */
private final Rect mTaskBounds = new Rect();
/** Windowing mode of this Task. */
@WindowingMode
private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
/** Active TaskFragments in this Task. */
@NonNull
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
/** Active split pairs in this Task. */
@NonNull
final List<SplitContainer> mSplitContainers = new ArrayList<>();
/**
* TaskFragments that the organizer has requested to be closed. They should be removed when
* the organizer receives {@link SplitController#onTaskFragmentVanished(TaskFragmentInfo)} event
* for them.
*/
final Set<IBinder> mFinishedContainer = new ArraySet<>();
TaskContainer(int taskId) {
if (taskId == INVALID_TASK_ID) {
throw new IllegalArgumentException("Invalid Task id");
}
mTaskId = taskId;
}
int getTaskId() {
return mTaskId;
}
@NonNull
Rect getTaskBounds() {
return mTaskBounds;
}
/** Returns {@code true} if the bounds is changed. */
boolean setTaskBounds(@NonNull Rect taskBounds) {
if (!taskBounds.isEmpty() && !mTaskBounds.equals(taskBounds)) {
mTaskBounds.set(taskBounds);
return true;
}
return false;
}
/** Whether the Task bounds has been initialized. */
boolean isTaskBoundsInitialized() {
return !mTaskBounds.isEmpty();
}
void setWindowingMode(int windowingMode) {
mWindowingMode = windowingMode;
}
/** Whether the Task windowing mode has been initialized. */
boolean isWindowingModeInitialized() {
return mWindowingMode != WINDOWING_MODE_UNDEFINED;
}
/**
* Returns the windowing mode for the TaskFragments below this Task, which should be split with
* other TaskFragments.
*
* @param taskFragmentBounds Requested bounds for the TaskFragment. It will be empty when
* the pair of TaskFragments are stacked due to the limited space.
*/
@WindowingMode
int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
// Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
// will be set to UNDEFINED which will then inherit the Task windowing mode.
if (taskFragmentBounds == null || taskFragmentBounds.isEmpty() || isInPictureInPicture()) {
return WINDOWING_MODE_UNDEFINED;
}
// We use WINDOWING_MODE_MULTI_WINDOW when the Task is fullscreen.
// However, when the Task is in other multi windowing mode, such as Freeform, we need to
// have the activity windowing mode to match the Task, otherwise things like
// DecorCaptionView won't work correctly. As a result, have the TaskFragment to be in the
// Task windowing mode if the Task is in multi window.
// TODO we won't need this anymore after we migrate Freeform caption to WM Shell.
return WindowConfiguration.inMultiWindowMode(mWindowingMode)
? mWindowingMode
: WINDOWING_MODE_MULTI_WINDOW;
}
boolean isInPictureInPicture() {
return mWindowingMode == WINDOWING_MODE_PINNED;
}
/** Whether there is any {@link TaskFragmentContainer} below this Task. */
boolean isEmpty() {
return mContainers.isEmpty() && mFinishedContainer.isEmpty();
}
/** Called when the activity is destroyed. */
void onActivityDestroyed(@NonNull Activity activity) {
for (TaskFragmentContainer container : mContainers) {
container.onActivityDestroyed(activity);
}
}
/** Removes the pending appeared activity from all TaskFragments in this Task. */
void cleanupPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
for (TaskFragmentContainer container : mContainers) {
container.removePendingAppearedActivity(pendingAppearedActivity);
}
}
@Nullable
TaskFragmentContainer getTopTaskFragmentContainer() {
if (mContainers.isEmpty()) {
return null;
}
return mContainers.get(mContainers.size() - 1);
}
@Nullable
Activity getTopNonFinishingActivity() {
for (int i = mContainers.size() - 1; i >= 0; i--) {
final Activity activity = mContainers.get(i).getTopNonFinishingActivity();
if (activity != null) {
return activity;
}
}
return null;
}
int indexOf(@NonNull TaskFragmentContainer child) {
return mContainers.indexOf(child);
}
}