| /* |
| * 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); |
| } |
| } |