diff options
8 files changed, 156 insertions, 19 deletions
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 342df4f9eee3..387837d82acf 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -516,11 +516,13 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction setAdjacentTaskFragments( - @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2) { + @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2, + @Nullable TaskFragmentAdjacentOptions options) { final HierarchyOp hierarchyOp = new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS) .setContainer(fragmentToken1) .setReparentContainer(fragmentToken2) + .setLaunchOptions(options != null ? options.toBundle() : null) .build(); mHierarchyOps.add(hierarchyOp); return this; @@ -1298,4 +1300,52 @@ public final class WindowContainerTransaction implements Parcelable { } } } + + /** + * Helper class for building an options Bundle that can be used to set adjacent rules of + * TaskFragments. + * @hide + */ + public static class TaskFragmentAdjacentOptions { + private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL = + "android:transaction.adjacent.option.delay_primary_removal"; + private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL = + "android:transaction.adjacent.option.delay_secondary_removal"; + + private boolean mDelayPrimaryLastActivityRemoval; + private boolean mDelaySecondaryLastActivityRemoval; + + public TaskFragmentAdjacentOptions() { + } + + public TaskFragmentAdjacentOptions(@NonNull Bundle bundle) { + mDelayPrimaryLastActivityRemoval = bundle.getBoolean( + DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL); + mDelaySecondaryLastActivityRemoval = bundle.getBoolean( + DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL); + } + + public void setDelayPrimaryLastActivityRemoval(boolean delay) { + mDelayPrimaryLastActivityRemoval = delay; + } + + public void setDelaySecondaryLastActivityRemoval(boolean delay) { + mDelaySecondaryLastActivityRemoval = delay; + } + + public boolean isDelayPrimaryLastActivityRemoval() { + return mDelayPrimaryLastActivityRemoval; + } + + public boolean isDelaySecondaryLastActivityRemoval() { + return mDelaySecondaryLastActivityRemoval; + } + + Bundle toBundle() { + final Bundle b = new Bundle(); + b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval); + b.putBoolean(DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL, mDelaySecondaryLastActivityRemoval); + return b; + } + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java index dd00189c3bf1..4206d0375e30 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java @@ -36,6 +36,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.window.extensions.embedding.SplitRule; import java.util.Map; import java.util.concurrent.Executor; @@ -100,7 +101,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds, @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent, - @Nullable Bundle activityOptions) { + @Nullable Bundle activityOptions, @NonNull SplitRule rule) { final IBinder ownerToken = launchingActivity.getActivityToken(); // Create or resize the launching TaskFragment. @@ -117,7 +118,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { activityOptions); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments(launchingFragmentToken, secondaryFragmentToken); + setAdjacentTaskFragments(wct, launchingFragmentToken, secondaryFragmentToken, rule); } /** @@ -127,7 +128,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { */ void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) { resizeTaskFragment(wct, fragmentToken, new Rect()); - wct.setAdjacentTaskFragments(fragmentToken, null); + setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */); } /** @@ -187,6 +188,21 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions); } + void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, + @NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) { + WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = null; + final boolean finishSecondaryWithPrimary = + splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule); + final boolean finishPrimaryWithSecondary = + splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule); + if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) { + adjacentOptions = new WindowContainerTransaction.TaskFragmentAdjacentOptions(); + adjacentOptions.setDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary); + adjacentOptions.setDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary); + } + wct.setAdjacentTaskFragments(primary, secondary, adjacentOptions); + } + TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken, Rect bounds, @WindowingMode int windowingMode) { if (mFragmentInfos.containsKey(fragmentToken)) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java index 8fd710a7986d..4fd2126dfa27 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java @@ -39,16 +39,10 @@ class SplitContainer { mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; - final boolean isPlaceholderContainer = isPlaceholderContainer(); - final boolean shouldFinishPrimaryWithSecondary = (mSplitRule instanceof SplitPairRule) - && ((SplitPairRule) mSplitRule).shouldFinishPrimaryWithSecondary(); - final boolean shouldFinishSecondaryWithPrimary = (mSplitRule instanceof SplitPairRule) - && ((SplitPairRule) mSplitRule).shouldFinishSecondaryWithPrimary(); - - if (shouldFinishPrimaryWithSecondary || isPlaceholderContainer) { + if (shouldFinishPrimaryWithSecondary(splitRule)) { mSecondaryContainer.addActivityToFinishOnExit(primaryActivity); } - if (shouldFinishSecondaryWithPrimary || isPlaceholderContainer) { + if (shouldFinishSecondaryWithPrimary(splitRule)) { mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); } } @@ -71,4 +65,18 @@ class SplitContainer { boolean isPlaceholderContainer() { return (mSplitRule instanceof SplitPlaceholderRule); } + + static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) { + final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; + final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule) + && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary(); + return shouldFinishPrimaryWithSecondary || isPlaceholderContainer; + } + + static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) { + final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule; + final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule) + && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary(); + return shouldFinishSecondaryWithPrimary || isPlaceholderContainer; + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java index a7bce20cae8b..ac85ac8cbc34 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java @@ -109,8 +109,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryContainer.setLastRequestedBounds(secondaryRectBounds); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments( - primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -144,8 +144,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity, secondaryRectBounds, primaryContainer); // Set adjacent to each other so that the containers below will be invisible. - wct.setAdjacentTaskFragments( - primaryContainer.getTaskFragmentToken(), secondaryContainer.getTaskFragmentToken()); + setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), + secondaryContainer.getTaskFragmentToken(), rule); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -212,7 +212,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { rule); startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds, - activityIntent, activityOptions); + activityIntent, activityOptions, rule); applyTransaction(wct); primaryContainer.setLastRequestedBounds(primaryRectBounds); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b5fd111f6d77..60e7d109ec2e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3234,6 +3234,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere final ActivityRecord next = getDisplayArea().topRunningActivity( true /* considerKeyguardState */); + + // If the finishing activity is the last activity of a organized TaskFragment and has an + // adjacent TaskFragment, check if the activity removal should be delayed. + boolean delayRemoval = false; + final TaskFragment taskFragment = getTaskFragment(); + if (next != null && taskFragment != null && taskFragment.isEmbedded()) { + final TaskFragment organized = taskFragment.getOrganizedTaskFragment(); + final TaskFragment adjacent = + organized != null ? organized.getAdjacentTaskFragment() : null; + if (adjacent != null && organized.topRunningActivity() == null) { + delayRemoval = organized.isDelayLastActivityRemoval(); + } + } + // isNextNotYetVisible is to check if the next activity is invisible, or it has been // requested to be invisible but its windows haven't reported as invisible. If so, it // implied that the current finishing activity should be added into stopping list rather @@ -3248,7 +3262,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (isCurrentVisible) { - if (isNextNotYetVisible) { + if (isNextNotYetVisible || delayRemoval) { // Add this activity to the list of stopping activities. It will be processed and // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 70b0ccd76474..6e4792bdbd31 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -221,6 +221,13 @@ class TaskFragment extends WindowContainer<WindowContainer> { private IBinder mFragmentToken; /** + * Whether to delay the last activity of TaskFragment being immediately removed while finishing. + * This should only be set on a embedded TaskFragment, where the organizer can have the + * opportunity to perform other actions or animations. + */ + private boolean mDelayLastActivityRemoval; + + /** * The PID of the organizer that created this TaskFragment. It should be the same as the PID * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}. * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment. @@ -319,6 +326,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { mAdjacentTaskFragment.mAdjacentTaskFragment = null; } mAdjacentTaskFragment = null; + mDelayLastActivityRemoval = false; } void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) { @@ -422,6 +430,20 @@ class TaskFragment extends WindowContainer<WindowContainer> { } /** + * Returns the TaskFragment that is being organized, which could be this or the ascendant + * TaskFragment. + */ + @Nullable + TaskFragment getOrganizedTaskFragment() { + if (mTaskFragmentOrganizer != null) { + return this; + } + + TaskFragment parentTaskFragment = getParent().asTaskFragment(); + return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null; + } + + /** * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}. */ private void warnForNonLeafTaskFragment(String func) { @@ -2086,6 +2108,17 @@ class TaskFragment extends WindowContainer<WindowContainer> { }); } + void setDelayLastActivityRemoval(boolean delay) { + if (!mIsEmbedded) { + Slog.w(TAG, "Set delaying last activity removal on a non-embedded TF."); + } + mDelayLastActivityRemoval = delay; + } + + boolean isDelayLastActivityRemoval() { + return mDelayLastActivityRemoval; + } + boolean shouldDeferRemoval() { if (!hasChild()) { return false; diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index aa147c4c8712..45c47bab1175 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -741,6 +741,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } tf1.setAdjacentTaskFragment(tf2); + + final Bundle bundle = hop.getLaunchOptions(); + final WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = + bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentOptions( + bundle) : null; + if (adjacentOptions == null) { + break; + } + + tf1.setDelayLastActivityRemoval( + adjacentOptions.isDelayPrimaryLastActivityRemoval()); + if (tf2 != null) { + tf2.setDelayLastActivityRemoval( + adjacentOptions.isDelaySecondaryLastActivityRemoval()); + } break; } return effects; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index c35f31702961..5c79f5ca2c66 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -326,7 +326,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mTransaction.startActivityInTaskFragment( mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); - mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class)); + mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class), + null /* options */); // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are // testing the security check here. |