Merge "Prevent activity being destroyed immediately if embedded" into sc-v2-dev am: eec6c21ef8 am: 20579de6c2
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15537319 Change-Id: I5544d58402b8c44536a67a8077bd33e60573ff5a
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
@@ -220,6 +220,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
@Nullable
|
||||
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()}.
|
||||
@@ -319,6 +326,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
|
||||
}
|
||||
mAdjacentTaskFragment = null;
|
||||
mDelayLastActivityRemoval = false;
|
||||
}
|
||||
|
||||
void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
|
||||
@@ -421,6 +429,20 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user