Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +28 −7 Original line number Diff line number Diff line Loading @@ -819,14 +819,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId); return; } // Checks if container should be updated before apply new parentInfo. final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo); taskContainer.updateTaskFragmentParentInfo(parentInfo); if (!taskContainer.isVisible()) { // Don't update containers if the task is not visible. We only update containers when // parentInfo#isVisibleRequested is true. return; } if (isInPictureInPicture(parentInfo.getConfiguration())) { // No need to update presentation in PIP until the Task exit PIP. // If the last direct activity of the host task is dismissed and the overlay container is // the only taskFragment, the overlay container should also be dismissed. dismissOverlayContainerIfNeeded(wct, taskContainer); if (!shouldUpdateContainer) { return; } updateContainersInTask(wct, taskContainer); Loading Loading @@ -1947,11 +1953,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void updateOverlayContainer(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container) { final TaskContainer taskContainer = container.getTaskContainer(); // Dismiss the overlay container if it's the only container in the task and there's no // direct activity in the parent task. if (taskContainer.getTaskFragmentContainers().size() == 1 && !taskContainer.hasDirectActivity()) { container.finish(false /* shouldFinishDependent */, mPresenter, wct, this); if (dismissOverlayContainerIfNeeded(wct, taskContainer)) { return; } Loading @@ -1968,6 +1971,24 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } /** Dismisses the overlay container in the {@code taskContainer} if needed. */ @GuardedBy("mLock") private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct, @NonNull TaskContainer taskContainer) { final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer(); if (overlayContainer == null) { return false; } // Dismiss the overlay container if it's the only container in the task and there's no // direct activity in the parent task. if (taskContainer.getTaskFragmentContainers().size() == 1 && !taskContainer.hasDirectActivity()) { mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); return true; } return false; } /** * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes} Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +20 −1 Original line number Diff line number Diff line Loading @@ -137,6 +137,21 @@ class TaskContainer { mHasDirectActivity = info.hasDirectActivity(); } /** * Returns {@code true} if the container should be updated with {@code info}. */ boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) { final Configuration configuration = info.getConfiguration(); return info.isVisible() // No need to update presentation in PIP until the Task exit PIP. && !isInPictureInPicture(configuration) // If the task properties equals regardless of starting position, don't need to // update the container. && (mConfiguration.diffPublicOnly(configuration) != 0 || mDisplayId != info.getDisplayId()); } /** * Returns the windowing mode for the TaskFragments below this Task, which should be split with * other TaskFragments. Loading @@ -161,7 +176,11 @@ class TaskContainer { } boolean isInPictureInPicture() { return getWindowingMode() == WINDOWING_MODE_PINNED; return isInPictureInPicture(mConfiguration); } private static boolean isInPictureInPicture(@NonNull Configuration configuration) { return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; } boolean isInMultiWindow() { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +3 −0 Original line number Diff line number Diff line Loading @@ -616,6 +616,9 @@ class TaskFragmentContainer { * Removes all activities that belong to this process and finishes other containers/activities * configured to finish together. */ // Suppress GuardedBy warning because lint ask to mark this method as // @GuardedBy(container.mController.mLock), which is mLock itself @SuppressWarnings("GuardedBy") @GuardedBy("mController.mLock") void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter, @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) { Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java +13 −1 Original line number Diff line number Diff line Loading @@ -77,9 +77,11 @@ class TransactionManager { @NonNull TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) { if (mCurrentTransaction != null) { final TransactionRecord lastTransaction = mCurrentTransaction; mCurrentTransaction = null; throw new IllegalStateException( "The previous transaction has not been applied or aborted,"); "The previous transaction:" + lastTransaction + " has not been applied or " + "aborted."); } mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken); return mCurrentTransaction; Loading Loading @@ -199,5 +201,15 @@ class TransactionManager { ? mOriginType : TASK_FRAGMENT_TRANSIT_CHANGE; } @Override @NonNull public String toString() { return TransactionRecord.class.getSimpleName() + "{" + "token=" + mTaskFragmentTransactionToken + ", type=" + getTransactionTransitionType() + ", transaction=" + mTransaction + "}"; } } } libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +23 −0 Original line number Diff line number Diff line Loading @@ -472,6 +472,29 @@ public class OverlayPresentationTest { verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs)); } @Test public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() { final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); final TaskContainer taskContainer = overlayContainer.getTaskContainer(); spyOn(taskContainer); final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(), true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10); mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo); // The parent info must be applied to the task container verify(taskContainer).updateTaskFragmentParentInfo(parentInfo); verify(mSplitController, never()).updateContainer(any(), any()); assertWithMessage("The overlay container must still be dismissed even if " + "#updateContainer is not called") .that(taskContainer.getOverlayContainer()).isNull(); } /** * A simplified version of {@link SplitController.ActivityStartMonitor * #createOrUpdateOverlayTaskFragmentIfNeeded} Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +28 −7 Original line number Diff line number Diff line Loading @@ -819,14 +819,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId); return; } // Checks if container should be updated before apply new parentInfo. final boolean shouldUpdateContainer = taskContainer.shouldUpdateContainer(parentInfo); taskContainer.updateTaskFragmentParentInfo(parentInfo); if (!taskContainer.isVisible()) { // Don't update containers if the task is not visible. We only update containers when // parentInfo#isVisibleRequested is true. return; } if (isInPictureInPicture(parentInfo.getConfiguration())) { // No need to update presentation in PIP until the Task exit PIP. // If the last direct activity of the host task is dismissed and the overlay container is // the only taskFragment, the overlay container should also be dismissed. dismissOverlayContainerIfNeeded(wct, taskContainer); if (!shouldUpdateContainer) { return; } updateContainersInTask(wct, taskContainer); Loading Loading @@ -1947,11 +1953,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void updateOverlayContainer(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container) { final TaskContainer taskContainer = container.getTaskContainer(); // Dismiss the overlay container if it's the only container in the task and there's no // direct activity in the parent task. if (taskContainer.getTaskFragmentContainers().size() == 1 && !taskContainer.hasDirectActivity()) { container.finish(false /* shouldFinishDependent */, mPresenter, wct, this); if (dismissOverlayContainerIfNeeded(wct, taskContainer)) { return; } Loading @@ -1968,6 +1971,24 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } /** Dismisses the overlay container in the {@code taskContainer} if needed. */ @GuardedBy("mLock") private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct, @NonNull TaskContainer taskContainer) { final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer(); if (overlayContainer == null) { return false; } // Dismiss the overlay container if it's the only container in the task and there's no // direct activity in the parent task. if (taskContainer.getTaskFragmentContainers().size() == 1 && !taskContainer.hasDirectActivity()) { mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); return true; } return false; } /** * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes} Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +20 −1 Original line number Diff line number Diff line Loading @@ -137,6 +137,21 @@ class TaskContainer { mHasDirectActivity = info.hasDirectActivity(); } /** * Returns {@code true} if the container should be updated with {@code info}. */ boolean shouldUpdateContainer(@NonNull TaskFragmentParentInfo info) { final Configuration configuration = info.getConfiguration(); return info.isVisible() // No need to update presentation in PIP until the Task exit PIP. && !isInPictureInPicture(configuration) // If the task properties equals regardless of starting position, don't need to // update the container. && (mConfiguration.diffPublicOnly(configuration) != 0 || mDisplayId != info.getDisplayId()); } /** * Returns the windowing mode for the TaskFragments below this Task, which should be split with * other TaskFragments. Loading @@ -161,7 +176,11 @@ class TaskContainer { } boolean isInPictureInPicture() { return getWindowingMode() == WINDOWING_MODE_PINNED; return isInPictureInPicture(mConfiguration); } private static boolean isInPictureInPicture(@NonNull Configuration configuration) { return configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; } boolean isInMultiWindow() { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +3 −0 Original line number Diff line number Diff line Loading @@ -616,6 +616,9 @@ class TaskFragmentContainer { * Removes all activities that belong to this process and finishes other containers/activities * configured to finish together. */ // Suppress GuardedBy warning because lint ask to mark this method as // @GuardedBy(container.mController.mLock), which is mLock itself @SuppressWarnings("GuardedBy") @GuardedBy("mController.mLock") void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter, @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) { Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TransactionManager.java +13 −1 Original line number Diff line number Diff line Loading @@ -77,9 +77,11 @@ class TransactionManager { @NonNull TransactionRecord startNewTransaction(@Nullable IBinder taskFragmentTransactionToken) { if (mCurrentTransaction != null) { final TransactionRecord lastTransaction = mCurrentTransaction; mCurrentTransaction = null; throw new IllegalStateException( "The previous transaction has not been applied or aborted,"); "The previous transaction:" + lastTransaction + " has not been applied or " + "aborted."); } mCurrentTransaction = new TransactionRecord(taskFragmentTransactionToken); return mCurrentTransaction; Loading Loading @@ -199,5 +201,15 @@ class TransactionManager { ? mOriginType : TASK_FRAGMENT_TRANSIT_CHANGE; } @Override @NonNull public String toString() { return TransactionRecord.class.getSimpleName() + "{" + "token=" + mTaskFragmentTransactionToken + ", type=" + getTransactionTransitionType() + ", transaction=" + mTransaction + "}"; } } }
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +23 −0 Original line number Diff line number Diff line Loading @@ -472,6 +472,29 @@ public class OverlayPresentationTest { verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs)); } @Test public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() { final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); final TaskContainer taskContainer = overlayContainer.getTaskContainer(); spyOn(taskContainer); final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(), true /* visible */, false /* hasDirectActivity */, null /* decorSurface */); parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10); mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo); // The parent info must be applied to the task container verify(taskContainer).updateTaskFragmentParentInfo(parentInfo); verify(mSplitController, never()).updateContainer(any(), any()); assertWithMessage("The overlay container must still be dismissed even if " + "#updateContainer is not called") .that(taskContainer.getOverlayContainer()).isNull(); } /** * A simplified version of {@link SplitController.ActivityStartMonitor * #createOrUpdateOverlayTaskFragmentIfNeeded} Loading