Merge "Associate starting window in task fragment with base task" into sc-v2-dev am: 25f50a91e8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15371455 Change-Id: I813776a9f0e4b0ebf6fcb6a0cf5c515cf0e8cf37
This commit is contained in:
@@ -330,8 +330,6 @@ import com.android.internal.policy.AttributeCache;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.ToBooleanFunction;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.internal.util.function.pooled.PooledFunction;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.am.AppTimeTracker;
|
||||
import com.android.server.am.PendingIntentRecord;
|
||||
@@ -746,6 +744,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
boolean startingDisplayed;
|
||||
boolean startingMoved;
|
||||
|
||||
/**
|
||||
* If it is non-null, it requires all activities who have the same starting data to be drawn
|
||||
* to remove the starting window.
|
||||
* TODO(b/189385912): Remove starting window related fields after migrating them to task.
|
||||
*/
|
||||
private StartingData mSharedStartingData;
|
||||
|
||||
boolean mHandleExitSplashScreen;
|
||||
@TransferSplashScreenState
|
||||
int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
|
||||
@@ -1085,6 +1090,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
|
||||
pw.print(" mIsExiting="); pw.println(mIsExiting);
|
||||
}
|
||||
if (mSharedStartingData != null) {
|
||||
pw.println(prefix + "mSharedStartingData=" + mSharedStartingData);
|
||||
}
|
||||
if (mStartingWindow != null || mStartingSurface != null
|
||||
|| startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
|
||||
pw.print(prefix); pw.print("startingWindow="); pw.print(mStartingWindow);
|
||||
@@ -1379,9 +1387,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
|
||||
@Override
|
||||
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
|
||||
final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null;
|
||||
final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null;
|
||||
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
|
||||
final TaskFragment oldParent = (TaskFragment) rawOldParent;
|
||||
final TaskFragment newParent = (TaskFragment) rawNewParent;
|
||||
final Task oldTask = oldParent != null ? oldParent.getTask() : null;
|
||||
final Task newTask = newParent != null ? newParent.getTask() : null;
|
||||
this.task = newTask;
|
||||
|
||||
super.onParentChanged(newParent, oldParent);
|
||||
@@ -1440,11 +1450,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
updateColorTransform();
|
||||
|
||||
if (oldParent != null) {
|
||||
((TaskFragment) oldParent).cleanUpActivityReferences(this);
|
||||
oldParent.cleanUpActivityReferences(this);
|
||||
}
|
||||
|
||||
if (newParent != null && isState(RESUMED)) {
|
||||
((TaskFragment) newParent).setResumedActivity(this, "onParentChanged");
|
||||
newParent.setResumedActivity(this, "onParentChanged");
|
||||
if (mStartingWindow != null && mStartingData != null
|
||||
&& mStartingData.mAssociatedTask == null && newParent.isEmbedded()) {
|
||||
// The starting window should keep covering its task when the activity is
|
||||
// reparented to a task fragment that may not fill the task bounds.
|
||||
associateStartingDataWithTask();
|
||||
overrideConfigurationPropagation(mStartingWindow, task);
|
||||
}
|
||||
}
|
||||
|
||||
if (rootTask != null && rootTask.topRunningActivity() == this) {
|
||||
@@ -2005,7 +2022,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
@VisibleForTesting
|
||||
boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
|
||||
CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
|
||||
IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
|
||||
ActivityRecord from, boolean newTask, boolean taskSwitch, boolean processRunning,
|
||||
boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty) {
|
||||
// If the display is frozen, we won't do anything until the actual window is
|
||||
// displayed so there is no reason to put in the starting window.
|
||||
@@ -2064,7 +2081,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
applyStartingWindowTheme(pkg, resolvedTheme);
|
||||
|
||||
if (transferStartingWindow(transferFrom)) {
|
||||
if (from != null && transferStartingWindow(from)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2089,6 +2106,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
|
||||
mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
|
||||
if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
|
||||
// Associate with the task so if this activity is resized by task fragment later, the
|
||||
// starting window can keep the same bounds as the task.
|
||||
associateStartingDataWithTask();
|
||||
}
|
||||
scheduleAddStartingWindow();
|
||||
return true;
|
||||
}
|
||||
@@ -2337,6 +2359,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
void attachStartingWindow(@NonNull WindowState startingWindow) {
|
||||
mStartingWindow = startingWindow;
|
||||
if (mStartingData != null && mStartingData.mAssociatedTask != null) {
|
||||
// Associate the configuration of starting window with the task.
|
||||
overrideConfigurationPropagation(startingWindow, mStartingData.mAssociatedTask);
|
||||
}
|
||||
}
|
||||
|
||||
private void associateStartingDataWithTask() {
|
||||
mStartingData.mAssociatedTask = task;
|
||||
task.forAllActivities(r -> {
|
||||
if (r.mVisibleRequested && !r.firstWindowDrawn) {
|
||||
r.mSharedStartingData = mStartingData;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void removeStartingWindow() {
|
||||
if (transferSplashScreenIfNeeded()) {
|
||||
return;
|
||||
@@ -2346,6 +2385,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
void removeStartingWindowAnimation(boolean prepareAnimation) {
|
||||
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
|
||||
if (mSharedStartingData != null) {
|
||||
mSharedStartingData.mAssociatedTask.forAllActivities(r -> {
|
||||
r.mSharedStartingData = null;
|
||||
});
|
||||
}
|
||||
if (mStartingWindow == null) {
|
||||
if (mStartingData != null) {
|
||||
// Starting window has not been added yet, but it is scheduled to be added.
|
||||
@@ -3864,12 +3908,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
boolean transferStartingWindow(IBinder transferFrom) {
|
||||
final ActivityRecord fromActivity = getDisplayContent().getActivityRecord(transferFrom);
|
||||
if (fromActivity == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) {
|
||||
final WindowState tStartingWindow = fromActivity.mStartingWindow;
|
||||
if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
|
||||
// In this case, the starting icon has already been displayed, so start
|
||||
@@ -3890,6 +3929,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
// Transfer the starting window over to the new token.
|
||||
mStartingData = fromActivity.mStartingData;
|
||||
mSharedStartingData = fromActivity.mSharedStartingData;
|
||||
mStartingSurface = fromActivity.mStartingSurface;
|
||||
startingDisplayed = fromActivity.startingDisplayed;
|
||||
fromActivity.startingDisplayed = false;
|
||||
@@ -3952,6 +3992,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
|
||||
"Moving pending starting from %s to %s", fromActivity, this);
|
||||
mStartingData = fromActivity.mStartingData;
|
||||
mSharedStartingData = fromActivity.mSharedStartingData;
|
||||
fromActivity.mStartingData = null;
|
||||
fromActivity.startingMoved = true;
|
||||
scheduleAddStartingWindow();
|
||||
@@ -3970,16 +4011,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
* immediately finishes after, so we have to transfer T to M.
|
||||
*/
|
||||
void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
|
||||
final PooledFunction p = PooledLambda.obtainFunction(ActivityRecord::transferStartingWindow,
|
||||
this, PooledLambda.__(ActivityRecord.class));
|
||||
task.forAllActivities(p);
|
||||
p.recycle();
|
||||
}
|
||||
|
||||
private boolean transferStartingWindow(ActivityRecord fromActivity) {
|
||||
if (fromActivity == this) return true;
|
||||
|
||||
return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token);
|
||||
task.forAllActivities(fromActivity -> {
|
||||
if (fromActivity == this) return true;
|
||||
return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity);
|
||||
});
|
||||
}
|
||||
|
||||
void checkKeyguardFlagsChanged() {
|
||||
@@ -5160,6 +5195,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
void notifyAppStopped() {
|
||||
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
|
||||
mAppStopped = true;
|
||||
firstWindowDrawn = false;
|
||||
// This is to fix the edge case that auto-enter-pip is finished in Launcher but app calls
|
||||
// setAutoEnterEnabled(false) and transitions to STOPPED state, see b/191930787.
|
||||
// Clear any surface transactions and content overlay in this case.
|
||||
@@ -5923,7 +5959,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
|
||||
void onFirstWindowDrawn(WindowState win) {
|
||||
firstWindowDrawn = true;
|
||||
// stop tracking
|
||||
mSplashScreenStyleEmpty = true;
|
||||
@@ -5939,7 +5975,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// own stuff.
|
||||
win.cancelAnimation();
|
||||
}
|
||||
removeStartingWindow();
|
||||
|
||||
// Remove starting window directly if is in a pure task. Otherwise if it is associated with
|
||||
// a task (e.g. nested task fragment), then remove only if all visible windows in the task
|
||||
// are drawn.
|
||||
final Task associatedTask =
|
||||
mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
|
||||
if (associatedTask == null) {
|
||||
removeStartingWindow();
|
||||
} else if (associatedTask.getActivity(
|
||||
r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
|
||||
// The last drawn activity may not be the one that owns the starting window.
|
||||
final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
|
||||
if (r != null) {
|
||||
r.removeStartingWindow();
|
||||
}
|
||||
}
|
||||
updateReportedVisibilityLocked();
|
||||
}
|
||||
|
||||
@@ -6510,8 +6561,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
|
||||
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
|
||||
prev != null ? prev.appToken : null,
|
||||
newTask || newSingleActivity, taskSwitch, isProcessRunning(),
|
||||
prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
|
||||
allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
|
||||
if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
|
||||
Slog.d(TAG, "Scheduled starting window for " + this);
|
||||
|
||||
@@ -32,6 +32,12 @@ public abstract class StartingData {
|
||||
*/
|
||||
boolean mIsTransitionForward;
|
||||
|
||||
/**
|
||||
* Non-null if the starting window should cover the bounds of associated task. It is assigned
|
||||
* when the parent activity of starting window may be put in a partial area of the task.
|
||||
*/
|
||||
Task mAssociatedTask;
|
||||
|
||||
protected StartingData(WindowManagerService service, int typeParams) {
|
||||
mService = service;
|
||||
mTypeParams = typeParams;
|
||||
|
||||
@@ -628,7 +628,7 @@ class Task extends TaskFragment {
|
||||
IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
|
||||
boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
|
||||
boolean _removeWithTaskOrganizer) {
|
||||
super(atmService, null /* fragmentToken */, _createdByOrganizer);
|
||||
super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
|
||||
|
||||
mTaskId = _taskId;
|
||||
mUserId = _userId;
|
||||
@@ -707,13 +707,13 @@ class Task extends TaskFragment {
|
||||
return this;
|
||||
}
|
||||
|
||||
private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
|
||||
private void cleanUpResourcesForDestroy(WindowContainer<?> oldParent) {
|
||||
if (hasChild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This task is going away, so save the last state if necessary.
|
||||
saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
|
||||
saveLaunchingStateIfNeeded(oldParent.getDisplayContent());
|
||||
|
||||
// TODO: VI what about activity?
|
||||
final boolean isVoiceSession = voiceSession != null;
|
||||
@@ -1144,11 +1144,11 @@ class Task extends TaskFragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
|
||||
final DisplayContent display = newParent != null
|
||||
? ((WindowContainer) newParent).getDisplayContent() : null;
|
||||
final DisplayContent oldDisplay = oldParent != null
|
||||
? ((WindowContainer) oldParent).getDisplayContent() : null;
|
||||
void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
|
||||
final WindowContainer<?> newParent = (WindowContainer<?>) rawNewParent;
|
||||
final WindowContainer<?> oldParent = (WindowContainer<?>) rawOldParent;
|
||||
final DisplayContent display = newParent != null ? newParent.getDisplayContent() : null;
|
||||
final DisplayContent oldDisplay = oldParent != null ? oldParent.getDisplayContent() : null;
|
||||
|
||||
mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
|
||||
|
||||
@@ -1189,7 +1189,7 @@ class Task extends TaskFragment {
|
||||
}
|
||||
|
||||
if (oldParent != null) {
|
||||
final Task oldParentTask = ((WindowContainer) oldParent).asTask();
|
||||
final Task oldParentTask = oldParent.asTask();
|
||||
if (oldParentTask != null) {
|
||||
final PooledConsumer c = PooledLambda.obtainConsumer(
|
||||
Task::cleanUpActivityReferences, oldParentTask,
|
||||
@@ -5164,21 +5164,17 @@ class Task extends TaskFragment {
|
||||
// "has the same starting icon" as the next one. This allows the
|
||||
// window manager to keep the previous window it had previously
|
||||
// created, if it still had one.
|
||||
Task prevTask = r.getTask();
|
||||
ActivityRecord prev = prevTask.getActivity(
|
||||
a -> a.mStartingData != null && a.okToShowLocked());
|
||||
if (prev != null) {
|
||||
// We don't want to reuse the previous starting preview if:
|
||||
// (1) The current activity is in a different task.
|
||||
if (prev.getTask() != prevTask) {
|
||||
prev = null;
|
||||
}
|
||||
// (2) The current activity is already displayed.
|
||||
else if (prev.nowVisible) {
|
||||
prev = null;
|
||||
}
|
||||
Task baseTask = r.getTask();
|
||||
if (baseTask.isEmbedded()) {
|
||||
// If the task is embedded in a task fragment, there may have an existing
|
||||
// starting window in the parent task. This allows the embedded activities
|
||||
// to share the starting window and make sure that the window can have top
|
||||
// z-order by transferring to the top activity.
|
||||
baseTask = baseTask.getParent().asTaskFragment().getTask();
|
||||
}
|
||||
|
||||
final ActivityRecord prev = baseTask.getActivity(
|
||||
a -> a.mStartingData != null && a.okToShowLocked());
|
||||
r.showStartingWindow(prev, newTask, isTaskSwitch,
|
||||
true /* startActivity */, sourceRecord);
|
||||
}
|
||||
|
||||
@@ -207,6 +207,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
@VisibleForTesting
|
||||
boolean mCreatedByOrganizer;
|
||||
|
||||
/** Whether this TaskFragment is embedded in a task. */
|
||||
private final boolean mIsEmbedded;
|
||||
|
||||
/** Organizer that organizing this TaskFragment. */
|
||||
@Nullable
|
||||
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
|
||||
@@ -269,14 +272,21 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Creates an embedded task fragment. */
|
||||
TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
|
||||
boolean createdByOrganizer) {
|
||||
this(atmService, fragmentToken, createdByOrganizer, true /* isEmbedded */);
|
||||
}
|
||||
|
||||
TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
|
||||
boolean createdByOrganizer, boolean isEmbedded) {
|
||||
super(atmService.mWindowManager);
|
||||
|
||||
mAtmService = atmService;
|
||||
mTaskSupervisor = mAtmService.mTaskSupervisor;
|
||||
mRootWindowContainer = mAtmService.mRootWindowContainer;
|
||||
mCreatedByOrganizer = createdByOrganizer;
|
||||
mIsEmbedded = isEmbedded;
|
||||
mTaskFragmentOrganizerController =
|
||||
mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
|
||||
mFragmentToken = fragmentToken;
|
||||
@@ -389,6 +399,18 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns {@code true} if this is a container for embedded activities or tasks. */
|
||||
boolean isEmbedded() {
|
||||
if (mIsEmbedded) {
|
||||
return true;
|
||||
}
|
||||
final WindowContainer<?> parent = getParent();
|
||||
if (parent != null) {
|
||||
final TaskFragment taskFragment = parent.asTaskFragment();
|
||||
return taskFragment != null && taskFragment.isEmbedded();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
|
||||
|
||||
@@ -3399,6 +3399,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
||||
unregisterConfigurationChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the receiver container to always use the configuration of the supplier container as
|
||||
* its requested override configuration. It allows to propagate configuration without changing
|
||||
* the relationship between child and parent.
|
||||
*/
|
||||
static void overrideConfigurationPropagation(WindowContainer<?> receiver,
|
||||
WindowContainer<?> supplier) {
|
||||
final ConfigurationContainerListener listener = new ConfigurationContainerListener() {
|
||||
@Override
|
||||
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
|
||||
receiver.onRequestedOverrideConfigurationChanged(supplier.getConfiguration());
|
||||
}
|
||||
};
|
||||
supplier.registerConfigurationChangeListener(listener);
|
||||
receiver.registerWindowContainerListener(new WindowContainerListener() {
|
||||
@Override
|
||||
public void onRemoved() {
|
||||
receiver.unregisterWindowContainerListener(this);
|
||||
supplier.unregisterConfigurationChangeListener(listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link WindowManager.LayoutParams.WindowType}.
|
||||
*/
|
||||
|
||||
@@ -1769,9 +1769,8 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
|
||||
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
|
||||
|
||||
final ActivityRecord tokenActivity = token.asActivityRecord();
|
||||
if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
|
||||
tokenActivity.mStartingWindow = win;
|
||||
if (type == TYPE_APPLICATION_STARTING && activity != null) {
|
||||
activity.attachStartingWindow(win);
|
||||
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
|
||||
activity, win);
|
||||
}
|
||||
|
||||
@@ -4736,7 +4736,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
final int drawState = mWinAnimator.mDrawState;
|
||||
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
|
||||
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
|
||||
mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);
|
||||
mActivityRecord.onFirstWindowDrawn(this);
|
||||
} else {
|
||||
mActivityRecord.onStartingWindowDrawn();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.wm;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
|
||||
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
|
||||
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
|
||||
@@ -145,6 +146,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
|
||||
/**
|
||||
@@ -2506,7 +2508,7 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
false, false);
|
||||
waitUntilHandlersIdle();
|
||||
activity2.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
|
||||
true, true, false, true, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertNoStartingWindow(activity1);
|
||||
@@ -2523,7 +2525,7 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
// Surprise, ...! Transfer window in the middle of the creation flow.
|
||||
activity2.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0,
|
||||
activity1.appToken.asBinder(), true, true, false,
|
||||
activity1, true, true, false,
|
||||
true, false, false);
|
||||
});
|
||||
activity1.addStartingWindow(mPackageName,
|
||||
@@ -2544,7 +2546,7 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
false, false);
|
||||
waitUntilHandlersIdle();
|
||||
activity2.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
|
||||
true, true, false, true, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertNoStartingWindow(activity1);
|
||||
@@ -2650,7 +2652,7 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
// Make sure the fixed rotation transform linked to activity2 when adding starting window
|
||||
// on activity2.
|
||||
topActivity.addStartingWindow(mPackageName,
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
|
||||
android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity,
|
||||
false, false, false, true, false, false);
|
||||
waitUntilHandlersIdle();
|
||||
assertTrue(topActivity.hasFixedRotationTransform());
|
||||
@@ -2681,6 +2683,52 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
assertHasStartingWindow(activityBottom);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartingWindowInTaskFragment() {
|
||||
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
final WindowState startingWindow = createWindowState(
|
||||
new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING), activity1);
|
||||
activity1.addWindow(startingWindow);
|
||||
activity1.attachStartingWindow(startingWindow);
|
||||
activity1.mStartingData = mock(StartingData.class);
|
||||
final Task task = activity1.getTask();
|
||||
final Rect taskBounds = task.getBounds();
|
||||
final int width = taskBounds.width();
|
||||
final int height = taskBounds.height();
|
||||
final BiConsumer<TaskFragment, Rect> fragmentSetup = (fragment, bounds) -> {
|
||||
final Configuration config = fragment.getRequestedOverrideConfiguration();
|
||||
config.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
|
||||
config.windowConfiguration.setBounds(bounds);
|
||||
fragment.onRequestedOverrideConfigurationChanged(config);
|
||||
};
|
||||
|
||||
final TaskFragment taskFragment1 = new TaskFragment(
|
||||
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
|
||||
fragmentSetup.accept(taskFragment1, new Rect(0, 0, width / 2, height));
|
||||
task.addChild(taskFragment1, POSITION_TOP);
|
||||
|
||||
final TaskFragment taskFragment2 = new TaskFragment(
|
||||
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
|
||||
fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
|
||||
task.addChild(taskFragment2, POSITION_TOP);
|
||||
final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
|
||||
activity2.mVisibleRequested = true;
|
||||
taskFragment2.addChild(activity2);
|
||||
activity1.reparent(taskFragment1, POSITION_TOP);
|
||||
|
||||
assertEquals(task, activity1.mStartingData.mAssociatedTask);
|
||||
assertEquals(taskFragment1.getBounds(), activity1.getBounds());
|
||||
// The activity was resized by task fragment, but starting window must still cover the task.
|
||||
assertEquals(taskBounds, activity1.mStartingWindow.getBounds());
|
||||
|
||||
// The starting window is only removed when all embedded activities are drawn.
|
||||
final WindowState activityWindow = mock(WindowState.class);
|
||||
activity1.onFirstWindowDrawn(activityWindow);
|
||||
assertNotNull(activity1.mStartingWindow);
|
||||
activity2.onFirstWindowDrawn(activityWindow);
|
||||
assertNull(activity1.mStartingWindow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransitionAnimationBounds() {
|
||||
removeGlobalMinSizeRestriction();
|
||||
|
||||
Reference in New Issue
Block a user