Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 android.os.Build; import android.os.Bundle; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; import androidx.annotation.NonNull; import java.util.List; /** * Helper class to back up and restore the TaskFragmentOrganizer state, in order to resume * organizing the TaskFragments if the app process is restarted. */ @SuppressWarnings("GuardedBy") class BackupHelper { private static final String TAG = "BackupHelper"; private static final boolean DEBUG = Build.isDebuggable(); private static final String KEY_TASK_CONTAINERS = "KEY_TASK_CONTAINERS"; @NonNull private final SplitController mController; @NonNull private final BackupIdler mBackupIdler = new BackupIdler(); private boolean mBackupIdlerScheduled; BackupHelper(@NonNull SplitController splitController, @NonNull Bundle savedState) { mController = splitController; if (!savedState.isEmpty()) { restoreState(savedState); } } /** * Schedules a back-up request. It is no-op if there was a request scheduled and not yet * completed. */ void scheduleBackup() { if (!mBackupIdlerScheduled) { mBackupIdlerScheduled = true; Looper.myQueue().addIdleHandler(mBackupIdler); } } final class BackupIdler implements MessageQueue.IdleHandler { @Override public boolean queueIdle() { synchronized (mController.mLock) { mBackupIdlerScheduled = false; startBackup(); } return false; } } private void startBackup() { final List<TaskContainer> taskContainers = mController.getTaskContainers(); if (taskContainers.isEmpty()) { Log.w(TAG, "No task-container to back up"); return; } if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers); final Bundle state = new Bundle(); state.setClassLoader(TaskContainer.class.getClassLoader()); state.putParcelableList(KEY_TASK_CONTAINERS, taskContainers); mController.setSavedState(state); } private void restoreState(@NonNull Bundle savedState) { if (savedState.isEmpty()) { return; } final List<TaskContainer> taskContainers = savedState.getParcelableArrayList( KEY_TASK_CONTAINERS, TaskContainer.class); for (TaskContainer taskContainer : taskContainers) { if (DEBUG) Log.d(TAG, "restore task " + taskContainer.getTaskId()); // TODO(b/289875940): implement the TaskContainer restoration. } } } libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +21 −0 Original line number Diff line number Diff line Loading @@ -2536,6 +2536,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return mTaskContainers.get(taskId); } @NonNull @GuardedBy("mLock") List<TaskContainer> getTaskContainers() { final ArrayList<TaskContainer> taskContainers = new ArrayList<>(); for (int i = mTaskContainers.size() - 1; i >= 0; i--) { taskContainers.add(mTaskContainers.valueAt(i)); } return taskContainers; } @GuardedBy("mLock") void setSavedState(@NonNull Bundle savedState) { mPresenter.setSavedState(savedState); } @GuardedBy("mLock") void addTaskContainer(int taskId, TaskContainer taskContainer) { mTaskContainers.put(taskId, taskContainer); Loading Loading @@ -2829,6 +2844,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return getActiveSplitForContainer(container) != null; } void scheduleBackup() { synchronized (mLock) { mPresenter.scheduleBackup(); } } private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter { @Override Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +14 −1 Original line number Diff line number Diff line Loading @@ -158,6 +158,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { private final WindowLayoutComponentImpl mWindowLayoutComponent; private final SplitController mController; @NonNull private final BackupHelper mBackupHelper; SplitPresenter(@NonNull Executor executor, @NonNull WindowLayoutComponentImpl windowLayoutComponent, Loading @@ -165,8 +167,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { super(executor, controller); mWindowLayoutComponent = windowLayoutComponent; mController = controller; final Bundle outSavedState = new Bundle(); if (Flags.aeBackStackRestore()) { outSavedState.setClassLoader(TaskContainer.class.getClassLoader()); registerOrganizer(false /* isSystemOrganizer */, outSavedState); } else { registerOrganizer(); } mBackupHelper = new BackupHelper(controller, outSavedState); } void scheduleBackup() { mBackupHelper.scheduleBackup(); } /** * Deletes the specified container and all other associated and dependent containers in the same Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +50 −6 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; Loading @@ -48,12 +50,14 @@ import androidx.window.extensions.embedding.SplitAttributes.SplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType; import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.List; import java.util.Set; /** Represents TaskFragments and split pairs below a Task. */ class TaskContainer { class TaskContainer implements Parcelable { private static final String TAG = TaskContainer.class.getSimpleName(); /** The unique task id. */ Loading @@ -80,6 +84,9 @@ class TaskContainer { @NonNull private TaskFragmentParentInfo mInfo; @NonNull private SplitController mSplitController; /** * TaskFragments that the organizer has requested to be closed. They should be removed when * the organizer receives Loading Loading @@ -120,8 +127,10 @@ class TaskContainer { * {@code activityInTask}. * @param activityInTask The {@link Activity} in the Task with {@code taskId}. It is used to * initialize the {@link TaskContainer} properties. * @param splitController The {@link SplitController}. */ TaskContainer(int taskId, @NonNull Activity activityInTask) { TaskContainer(int taskId, @NonNull Activity activityInTask, @Nullable SplitController splitController) { if (taskId == INVALID_TASK_ID) { throw new IllegalArgumentException("Invalid Task id"); } Loading @@ -136,6 +145,7 @@ class TaskContainer { true /* visible */, true /* hasDirectActivity */, null /* decorSurface */); mSplitController = splitController; } int getTaskId() { Loading Loading @@ -571,6 +581,12 @@ class TaskContainer { // Update overlay container after split pin container since the overlay should be on top of // pin container. updateAlwaysOnTopOverlayIfNecessary(); // TODO(b/289875940): Making backup-restore as an opt-in solution, before the flag goes // to next-food. if (Flags.aeBackStackRestore()) { mSplitController.scheduleBackup(); } } private void updateAlwaysOnTopOverlayIfNecessary() { Loading Loading @@ -664,6 +680,34 @@ class TaskContainer { return activityStacks; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTaskId); // TODO(b/289875940) } protected TaskContainer(Parcel in) { mTaskId = in.readInt(); // TODO(b/289875940) } public static final Creator<TaskContainer> CREATOR = new Creator<>() { @Override public TaskContainer createFromParcel(Parcel in) { return new TaskContainer(in); } @Override public TaskContainer[] newArray(int size) { return new TaskContainer[size]; } }; /** A wrapper class which contains the information of {@link TaskContainer} */ static final class TaskProperties { private final int mDisplayId; Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +1 −1 Original line number Diff line number Diff line Loading @@ -1203,7 +1203,7 @@ class TaskFragmentContainer { if (taskContainer == null) { // Adding a TaskContainer if no existed one. taskContainer = new TaskContainer(mTaskId, mActivityInTask); taskContainer = new TaskContainer(mTaskId, mActivityInTask, mSplitController); mSplitController.addTaskContainer(mTaskId, taskContainer); } Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 android.os.Build; import android.os.Bundle; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; import androidx.annotation.NonNull; import java.util.List; /** * Helper class to back up and restore the TaskFragmentOrganizer state, in order to resume * organizing the TaskFragments if the app process is restarted. */ @SuppressWarnings("GuardedBy") class BackupHelper { private static final String TAG = "BackupHelper"; private static final boolean DEBUG = Build.isDebuggable(); private static final String KEY_TASK_CONTAINERS = "KEY_TASK_CONTAINERS"; @NonNull private final SplitController mController; @NonNull private final BackupIdler mBackupIdler = new BackupIdler(); private boolean mBackupIdlerScheduled; BackupHelper(@NonNull SplitController splitController, @NonNull Bundle savedState) { mController = splitController; if (!savedState.isEmpty()) { restoreState(savedState); } } /** * Schedules a back-up request. It is no-op if there was a request scheduled and not yet * completed. */ void scheduleBackup() { if (!mBackupIdlerScheduled) { mBackupIdlerScheduled = true; Looper.myQueue().addIdleHandler(mBackupIdler); } } final class BackupIdler implements MessageQueue.IdleHandler { @Override public boolean queueIdle() { synchronized (mController.mLock) { mBackupIdlerScheduled = false; startBackup(); } return false; } } private void startBackup() { final List<TaskContainer> taskContainers = mController.getTaskContainers(); if (taskContainers.isEmpty()) { Log.w(TAG, "No task-container to back up"); return; } if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers); final Bundle state = new Bundle(); state.setClassLoader(TaskContainer.class.getClassLoader()); state.putParcelableList(KEY_TASK_CONTAINERS, taskContainers); mController.setSavedState(state); } private void restoreState(@NonNull Bundle savedState) { if (savedState.isEmpty()) { return; } final List<TaskContainer> taskContainers = savedState.getParcelableArrayList( KEY_TASK_CONTAINERS, TaskContainer.class); for (TaskContainer taskContainer : taskContainers) { if (DEBUG) Log.d(TAG, "restore task " + taskContainer.getTaskId()); // TODO(b/289875940): implement the TaskContainer restoration. } } }
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +21 −0 Original line number Diff line number Diff line Loading @@ -2536,6 +2536,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return mTaskContainers.get(taskId); } @NonNull @GuardedBy("mLock") List<TaskContainer> getTaskContainers() { final ArrayList<TaskContainer> taskContainers = new ArrayList<>(); for (int i = mTaskContainers.size() - 1; i >= 0; i--) { taskContainers.add(mTaskContainers.valueAt(i)); } return taskContainers; } @GuardedBy("mLock") void setSavedState(@NonNull Bundle savedState) { mPresenter.setSavedState(savedState); } @GuardedBy("mLock") void addTaskContainer(int taskId, TaskContainer taskContainer) { mTaskContainers.put(taskId, taskContainer); Loading Loading @@ -2829,6 +2844,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return getActiveSplitForContainer(container) != null; } void scheduleBackup() { synchronized (mLock) { mPresenter.scheduleBackup(); } } private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter { @Override Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +14 −1 Original line number Diff line number Diff line Loading @@ -158,6 +158,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { private final WindowLayoutComponentImpl mWindowLayoutComponent; private final SplitController mController; @NonNull private final BackupHelper mBackupHelper; SplitPresenter(@NonNull Executor executor, @NonNull WindowLayoutComponentImpl windowLayoutComponent, Loading @@ -165,8 +167,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { super(executor, controller); mWindowLayoutComponent = windowLayoutComponent; mController = controller; final Bundle outSavedState = new Bundle(); if (Flags.aeBackStackRestore()) { outSavedState.setClassLoader(TaskContainer.class.getClassLoader()); registerOrganizer(false /* isSystemOrganizer */, outSavedState); } else { registerOrganizer(); } mBackupHelper = new BackupHelper(controller, outSavedState); } void scheduleBackup() { mBackupHelper.scheduleBackup(); } /** * Deletes the specified container and all other associated and dependent containers in the same Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +50 −6 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; Loading @@ -48,12 +50,14 @@ import androidx.window.extensions.embedding.SplitAttributes.SplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType; import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.List; import java.util.Set; /** Represents TaskFragments and split pairs below a Task. */ class TaskContainer { class TaskContainer implements Parcelable { private static final String TAG = TaskContainer.class.getSimpleName(); /** The unique task id. */ Loading @@ -80,6 +84,9 @@ class TaskContainer { @NonNull private TaskFragmentParentInfo mInfo; @NonNull private SplitController mSplitController; /** * TaskFragments that the organizer has requested to be closed. They should be removed when * the organizer receives Loading Loading @@ -120,8 +127,10 @@ class TaskContainer { * {@code activityInTask}. * @param activityInTask The {@link Activity} in the Task with {@code taskId}. It is used to * initialize the {@link TaskContainer} properties. * @param splitController The {@link SplitController}. */ TaskContainer(int taskId, @NonNull Activity activityInTask) { TaskContainer(int taskId, @NonNull Activity activityInTask, @Nullable SplitController splitController) { if (taskId == INVALID_TASK_ID) { throw new IllegalArgumentException("Invalid Task id"); } Loading @@ -136,6 +145,7 @@ class TaskContainer { true /* visible */, true /* hasDirectActivity */, null /* decorSurface */); mSplitController = splitController; } int getTaskId() { Loading Loading @@ -571,6 +581,12 @@ class TaskContainer { // Update overlay container after split pin container since the overlay should be on top of // pin container. updateAlwaysOnTopOverlayIfNecessary(); // TODO(b/289875940): Making backup-restore as an opt-in solution, before the flag goes // to next-food. if (Flags.aeBackStackRestore()) { mSplitController.scheduleBackup(); } } private void updateAlwaysOnTopOverlayIfNecessary() { Loading Loading @@ -664,6 +680,34 @@ class TaskContainer { return activityStacks; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTaskId); // TODO(b/289875940) } protected TaskContainer(Parcel in) { mTaskId = in.readInt(); // TODO(b/289875940) } public static final Creator<TaskContainer> CREATOR = new Creator<>() { @Override public TaskContainer createFromParcel(Parcel in) { return new TaskContainer(in); } @Override public TaskContainer[] newArray(int size) { return new TaskContainer[size]; } }; /** A wrapper class which contains the information of {@link TaskContainer} */ static final class TaskProperties { private final int mDisplayId; Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +1 −1 Original line number Diff line number Diff line Loading @@ -1203,7 +1203,7 @@ class TaskFragmentContainer { if (taskContainer == null) { // Adding a TaskContainer if no existed one. taskContainer = new TaskContainer(mTaskId, mActivityInTask); taskContainer = new TaskContainer(mTaskId, mActivityInTask, mSplitController); mSplitController.addTaskContainer(mTaskId, taskContainer); } Loading