Pass Task configuration changed for all TaskFragments at once
Before, we passed Task configuration changed for every embedded TaskFragment. Now, we only passed one Task configuration changed for the whole Task. The organizer will then update TaskFragments in the Task accordingly. Bug: 240519866 Test: atest WmTests:TaskFragmentOrganizerControllerTest Change-Id: I755c11a1bcee0dd0305a090b229e867b79eee46d
This commit is contained in:
@@ -32,8 +32,10 @@ import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.SparseArray;
|
||||
import android.view.RemoteAnimationDefinition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -72,6 +74,12 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
||||
*/
|
||||
private final Executor mExecutor;
|
||||
|
||||
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
|
||||
/** Map from Task id to client tokens of TaskFragments in the Task. */
|
||||
private final SparseArray<List<IBinder>> mTaskIdToFragmentTokens = new SparseArray<>();
|
||||
/** Map from Task id to Task configuration. */
|
||||
private final SparseArray<Configuration> mTaskIdToConfigurations = new SparseArray<>();
|
||||
|
||||
public TaskFragmentOrganizer(@NonNull Executor executor) {
|
||||
mExecutor = executor;
|
||||
}
|
||||
@@ -160,6 +168,27 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
||||
public void onTaskFragmentParentInfoChanged(
|
||||
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
|
||||
|
||||
/**
|
||||
* Called when the parent leaf Task of organized TaskFragments is changed.
|
||||
* When the leaf Task is changed, the organizer may want to update the TaskFragments in one
|
||||
* transaction.
|
||||
*
|
||||
* For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
|
||||
* Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
|
||||
* bounds.
|
||||
* @hide
|
||||
*/
|
||||
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
|
||||
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
|
||||
final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
|
||||
if (tokens == null || tokens.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (int i = tokens.size() - 1; i >= 0; i--) {
|
||||
onTaskFragmentParentInfoChanged(tokens.get(i), parentConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link WindowContainerTransaction} created with
|
||||
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
|
||||
@@ -221,34 +250,43 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
||||
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
|
||||
for (TaskFragmentTransaction.Change change : changes) {
|
||||
// TODO(b/240519866): apply all changes in one WCT.
|
||||
final int taskId = change.getTaskId();
|
||||
switch (change.getType()) {
|
||||
case TYPE_TASK_FRAGMENT_APPEARED:
|
||||
onTaskFragmentAppeared(change.getTaskFragmentInfo());
|
||||
if (change.getTaskConfiguration() != null) {
|
||||
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
|
||||
// same Task
|
||||
onTaskFragmentParentInfoChanged(
|
||||
change.getTaskFragmentToken(),
|
||||
change.getTaskConfiguration());
|
||||
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
|
||||
// release.
|
||||
if (!mTaskIdToFragmentTokens.contains(taskId)) {
|
||||
mTaskIdToFragmentTokens.put(taskId, new ArrayList<>());
|
||||
}
|
||||
mTaskIdToFragmentTokens.get(taskId).add(change.getTaskFragmentToken());
|
||||
onTaskFragmentParentInfoChanged(change.getTaskFragmentToken(),
|
||||
mTaskIdToConfigurations.get(taskId));
|
||||
|
||||
onTaskFragmentAppeared(change.getTaskFragmentInfo());
|
||||
break;
|
||||
case TYPE_TASK_FRAGMENT_INFO_CHANGED:
|
||||
if (change.getTaskConfiguration() != null) {
|
||||
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
|
||||
// same Task
|
||||
onTaskFragmentParentInfoChanged(
|
||||
change.getTaskFragmentToken(),
|
||||
change.getTaskConfiguration());
|
||||
}
|
||||
onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
|
||||
break;
|
||||
case TYPE_TASK_FRAGMENT_VANISHED:
|
||||
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
|
||||
// release.
|
||||
if (mTaskIdToFragmentTokens.contains(taskId)) {
|
||||
final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
|
||||
tokens.remove(change.getTaskFragmentToken());
|
||||
if (tokens.isEmpty()) {
|
||||
mTaskIdToFragmentTokens.remove(taskId);
|
||||
mTaskIdToConfigurations.remove(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
onTaskFragmentVanished(change.getTaskFragmentInfo());
|
||||
break;
|
||||
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
|
||||
onTaskFragmentParentInfoChanged(
|
||||
change.getTaskFragmentToken(),
|
||||
change.getTaskConfiguration());
|
||||
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
|
||||
// release.
|
||||
mTaskIdToConfigurations.put(taskId, change.getTaskConfiguration());
|
||||
|
||||
onTaskFragmentParentInfoChanged(taskId, change.getTaskConfiguration());
|
||||
break;
|
||||
case TYPE_TASK_FRAGMENT_ERROR:
|
||||
final Bundle errorBundle = change.getErrorBundle();
|
||||
|
||||
@@ -51,12 +51,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
@VisibleForTesting
|
||||
final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
|
||||
|
||||
/**
|
||||
* Mapping from the client assigned unique token to the TaskFragment parent
|
||||
* {@link Configuration}.
|
||||
*/
|
||||
final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
|
||||
|
||||
private final TaskFragmentCallback mCallback;
|
||||
@VisibleForTesting
|
||||
TaskFragmentAnimationController mAnimationController;
|
||||
@@ -68,8 +62,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
|
||||
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
|
||||
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
|
||||
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
||||
@NonNull Configuration parentConfig);
|
||||
void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
|
||||
void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
|
||||
@NonNull IBinder activityToken);
|
||||
void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
|
||||
@@ -300,7 +293,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
@Override
|
||||
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
|
||||
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
|
||||
mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
|
||||
|
||||
if (mCallback != null) {
|
||||
mCallback.onTaskFragmentVanished(taskFragmentInfo);
|
||||
@@ -308,12 +300,9 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskFragmentParentInfoChanged(
|
||||
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
|
||||
mFragmentParentConfigs.put(fragmentToken, parentConfig);
|
||||
|
||||
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
|
||||
if (mCallback != null) {
|
||||
mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig);
|
||||
mCallback.onTaskFragmentParentInfoChanged(taskId, parentConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
container.setInfo(taskFragmentInfo);
|
||||
if (container.isFinished()) {
|
||||
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
|
||||
} else {
|
||||
// Update with the latest Task configuration.
|
||||
mPresenter.updateContainer(container);
|
||||
}
|
||||
updateCallbackIfNecessary();
|
||||
}
|
||||
@@ -233,19 +236,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
|
||||
@NonNull Configuration parentConfig) {
|
||||
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
|
||||
synchronized (mLock) {
|
||||
final TaskFragmentContainer container = getContainer(fragmentToken);
|
||||
if (container != null) {
|
||||
onTaskConfigurationChanged(container.getTaskId(), parentConfig);
|
||||
if (isInPictureInPicture(parentConfig)) {
|
||||
// No need to update presentation in PIP until the Task exit PIP.
|
||||
return;
|
||||
}
|
||||
mPresenter.updateContainer(container);
|
||||
updateCallbackIfNecessary();
|
||||
onTaskConfigurationChanged(taskId, parentConfig);
|
||||
if (isInPictureInPicture(parentConfig)) {
|
||||
// No need to update presentation in PIP until the Task exit PIP.
|
||||
return;
|
||||
}
|
||||
final TaskContainer taskContainer = getTaskContainer(taskId);
|
||||
if (taskContainer == null || taskContainer.isEmpty()) {
|
||||
Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
|
||||
return;
|
||||
}
|
||||
// Update all TaskFragments in the Task. Make a copy of the list since some may be
|
||||
// removed on updating.
|
||||
final List<TaskFragmentContainer> containers =
|
||||
new ArrayList<>(taskContainer.mContainers);
|
||||
for (int i = containers.size() - 1; i >= 0; i--) {
|
||||
final TaskFragmentContainer container = containers.get(i);
|
||||
// Wait until onTaskFragmentAppeared to update new container.
|
||||
if (!container.isFinished() && !container.isWaitingActivityAppear()) {
|
||||
mPresenter.updateContainer(container);
|
||||
}
|
||||
}
|
||||
updateCallbackIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -887,11 +887,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
|
||||
|
||||
logIfTransactionTooLarge(r.intent, r.getSavedState());
|
||||
|
||||
if (r.isEmbedded()) {
|
||||
final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
|
||||
if (organizedTaskFragment != null) {
|
||||
// Sending TaskFragmentInfo to client to ensure the info is updated before
|
||||
// the activity creation.
|
||||
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
|
||||
r.getOrganizedTaskFragment());
|
||||
organizedTaskFragment);
|
||||
}
|
||||
|
||||
// Create activity launch transaction.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
|
||||
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
|
||||
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
|
||||
@@ -28,6 +29,8 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI
|
||||
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
|
||||
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -38,6 +41,7 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.RemoteAnimationDefinition;
|
||||
@@ -51,6 +55,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
@@ -70,15 +75,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
|
||||
new ArrayMap<>();
|
||||
/**
|
||||
* A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent}
|
||||
* Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
|
||||
*/
|
||||
private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
|
||||
new ArrayList<>();
|
||||
|
||||
/** Map from {@link ITaskFragmentOrganizer} to {@link TaskFragmentTransaction}. */
|
||||
private final ArrayMap<IBinder, TaskFragmentTransaction> mTmpOrganizerToTransactionMap =
|
||||
private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
|
||||
new ArrayMap<>();
|
||||
private final ArrayList<ITaskFragmentOrganizer> mTmpOrganizerList = new ArrayList<>();
|
||||
|
||||
private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
|
||||
|
||||
TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
|
||||
mAtmService = atm;
|
||||
@@ -94,10 +96,30 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
private final ITaskFragmentOrganizer mOrganizer;
|
||||
private final int mOrganizerPid;
|
||||
private final int mOrganizerUid;
|
||||
|
||||
/**
|
||||
* Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
|
||||
* organizer.
|
||||
*/
|
||||
private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
|
||||
new WeakHashMap<>();
|
||||
private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
|
||||
new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Map from {@link TaskFragment} to its leaf {@link Task#mTaskId}. Embedded
|
||||
* {@link TaskFragment} will not be reparented until it is removed.
|
||||
*/
|
||||
private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
|
||||
* organizer.
|
||||
*/
|
||||
private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
|
||||
new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Map from temporary activity token to the corresponding {@link ActivityRecord}.
|
||||
*/
|
||||
private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens =
|
||||
new WeakHashMap<>();
|
||||
|
||||
@@ -161,21 +183,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
|
||||
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
|
||||
final int taskId = tf.getTask().mTaskId;
|
||||
tf.mTaskFragmentAppearedSent = true;
|
||||
mLastSentTaskFragmentInfos.put(tf, info);
|
||||
final TaskFragmentTransaction.Change change =
|
||||
new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_APPEARED)
|
||||
.setTaskFragmentToken(tf.getFragmentToken())
|
||||
.setTaskFragmentInfo(info);
|
||||
if (shouldSendTaskFragmentParentInfoChanged(tf)) {
|
||||
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
|
||||
final Task task = tf.getTask();
|
||||
mLastSentTaskFragmentParentConfigs
|
||||
.put(tf, new Configuration(task.getConfiguration()));
|
||||
change.setTaskId(task.mTaskId)
|
||||
.setTaskConfiguration(task.getConfiguration());
|
||||
}
|
||||
return change;
|
||||
mTaskFragmentTaskIds.put(tf, taskId);
|
||||
return new TaskFragmentTransaction.Change(
|
||||
TYPE_TASK_FRAGMENT_APPEARED)
|
||||
.setTaskFragmentToken(tf.getFragmentToken())
|
||||
.setTaskFragmentInfo(info)
|
||||
.setTaskId(taskId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -183,10 +199,24 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
|
||||
tf.mTaskFragmentAppearedSent = false;
|
||||
mLastSentTaskFragmentInfos.remove(tf);
|
||||
mLastSentTaskFragmentParentConfigs.remove(tf);
|
||||
|
||||
// Cleanup TaskFragmentParentConfig if this is the last TaskFragment in the Task.
|
||||
final int taskId;
|
||||
if (mTaskFragmentTaskIds.containsKey(tf)) {
|
||||
taskId = mTaskFragmentTaskIds.remove(tf);
|
||||
if (!mTaskFragmentTaskIds.containsValue(taskId)) {
|
||||
// No more TaskFragment in the Task.
|
||||
mLastSentTaskFragmentParentConfigs.remove(taskId);
|
||||
}
|
||||
} else {
|
||||
// This can happen if the appeared wasn't sent before remove.
|
||||
taskId = INVALID_TASK_ID;
|
||||
}
|
||||
|
||||
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
|
||||
.setTaskFragmentToken(tf.getFragmentToken())
|
||||
.setTaskFragmentInfo(tf.getTaskFragmentInfo());
|
||||
.setTaskFragmentInfo(tf.getTaskFragmentInfo())
|
||||
.setTaskId(taskId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -197,65 +227,39 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
|
||||
if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
|
||||
info.getConfiguration(), lastInfo.getConfiguration())) {
|
||||
// Parent config may have changed. The controller will check if there is any
|
||||
// important config change for the organizer.
|
||||
return prepareTaskFragmentParentInfoChanged(tf);
|
||||
return null;
|
||||
}
|
||||
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
|
||||
tf.getName());
|
||||
mLastSentTaskFragmentInfos.put(tf, info);
|
||||
final TaskFragmentTransaction.Change change =
|
||||
new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_INFO_CHANGED)
|
||||
.setTaskFragmentToken(tf.getFragmentToken())
|
||||
.setTaskFragmentInfo(info);
|
||||
if (shouldSendTaskFragmentParentInfoChanged(tf)) {
|
||||
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
|
||||
// at once.
|
||||
// Parent config may have changed. The controller will check if there is any
|
||||
// important config change for the organizer.
|
||||
final Task task = tf.getTask();
|
||||
mLastSentTaskFragmentParentConfigs
|
||||
.put(tf, new Configuration(task.getConfiguration()));
|
||||
change.setTaskId(task.mTaskId)
|
||||
.setTaskConfiguration(task.getConfiguration());
|
||||
}
|
||||
return change;
|
||||
return new TaskFragmentTransaction.Change(
|
||||
TYPE_TASK_FRAGMENT_INFO_CHANGED)
|
||||
.setTaskFragmentToken(tf.getFragmentToken())
|
||||
.setTaskFragmentInfo(info)
|
||||
.setTaskId(tf.getTask().mTaskId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
|
||||
@NonNull TaskFragment tf) {
|
||||
if (!shouldSendTaskFragmentParentInfoChanged(tf)) {
|
||||
@NonNull Task task) {
|
||||
final int taskId = task.mTaskId;
|
||||
// Check if the parent info is different from the last reported parent info.
|
||||
final Configuration taskConfig = task.getConfiguration();
|
||||
final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(taskId);
|
||||
if (configurationsAreEqualForOrganizer(taskConfig, lastParentConfig)
|
||||
&& taskConfig.windowConfiguration.getWindowingMode()
|
||||
== lastParentConfig.windowConfiguration.getWindowingMode()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Task parent = tf.getTask();
|
||||
final Configuration parentConfig = parent.getConfiguration();
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
|
||||
"TaskFragment parent info changed name=%s parentTaskId=%d",
|
||||
tf.getName(), parent.mTaskId);
|
||||
mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
|
||||
task.getName(), taskId);
|
||||
mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
|
||||
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
|
||||
.setTaskFragmentToken(tf.getFragmentToken())
|
||||
.setTaskId(parent.mTaskId)
|
||||
.setTaskConfiguration(parent.getConfiguration());
|
||||
}
|
||||
|
||||
/** Whether the system should report TaskFragment parent info changed to the organizer. */
|
||||
private boolean shouldSendTaskFragmentParentInfoChanged(@NonNull TaskFragment tf) {
|
||||
final Task parent = tf.getTask();
|
||||
if (parent == null) {
|
||||
// The TaskFragment is not attached.
|
||||
mLastSentTaskFragmentParentConfigs.remove(tf);
|
||||
return false;
|
||||
}
|
||||
// Check if the parent info is different from the last reported parent info.
|
||||
final Configuration parentConfig = parent.getConfiguration();
|
||||
final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
|
||||
return !configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
|
||||
|| parentConfig.windowConfiguration.getWindowingMode()
|
||||
!= lastParentConfig.windowConfiguration.getWindowingMode();
|
||||
.setTaskId(taskId)
|
||||
.setTaskConfiguration(taskConfig);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -345,6 +349,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
}
|
||||
mTaskFragmentOrganizerState.put(organizer.asBinder(),
|
||||
new TaskFragmentOrganizerState(organizer, pid, uid));
|
||||
mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +439,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
|
||||
void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@NonNull TaskFragment taskFragment) {
|
||||
if (taskFragment.getTask() == null) {
|
||||
Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf="
|
||||
+ taskFragment);
|
||||
return;
|
||||
}
|
||||
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
|
||||
if (!state.addTaskFragment(taskFragment)) {
|
||||
return;
|
||||
@@ -441,28 +451,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
|
||||
PendingTaskFragmentEvent.EVENT_APPEARED);
|
||||
if (pendingEvent == null) {
|
||||
pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||
addPendingEvent(new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_APPEARED, organizer)
|
||||
.setTaskFragment(taskFragment)
|
||||
.build();
|
||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@NonNull TaskFragment taskFragment) {
|
||||
handleTaskFragmentInfoChanged(organizer, taskFragment,
|
||||
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
|
||||
}
|
||||
|
||||
void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@NonNull TaskFragment taskFragment) {
|
||||
handleTaskFragmentInfoChanged(organizer, taskFragment,
|
||||
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
|
||||
}
|
||||
|
||||
private void handleTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@NonNull TaskFragment taskFragment, int eventType) {
|
||||
validateAndGetState(organizer);
|
||||
if (!taskFragment.mTaskFragmentAppearedSent) {
|
||||
// Skip if TaskFragment still not appeared.
|
||||
@@ -470,45 +467,41 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
}
|
||||
PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
|
||||
if (pendingEvent == null) {
|
||||
pendingEvent = new PendingTaskFragmentEvent.Builder(eventType, organizer)
|
||||
.setTaskFragment(taskFragment)
|
||||
.build();
|
||||
pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_INFO_CHANGED, organizer)
|
||||
.setTaskFragment(taskFragment)
|
||||
.build();
|
||||
} else {
|
||||
if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
|
||||
// Skipped the info changed event if vanished event is pending.
|
||||
return;
|
||||
}
|
||||
// Remove and add for re-ordering.
|
||||
mPendingTaskFragmentEvents.remove(pendingEvent);
|
||||
removePendingEvent(pendingEvent);
|
||||
// Reset the defer time when TaskFragment is changed, so that it can check again if
|
||||
// the event should be sent to the organizer, for example the TaskFragment may become
|
||||
// empty.
|
||||
pendingEvent.mDeferTime = 0;
|
||||
}
|
||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||
addPendingEvent(pendingEvent);
|
||||
}
|
||||
|
||||
void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@NonNull TaskFragment taskFragment) {
|
||||
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
|
||||
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
|
||||
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
|
||||
if (taskFragment == entry.mTaskFragment) {
|
||||
mPendingTaskFragmentEvents.remove(i);
|
||||
if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) {
|
||||
// If taskFragment appeared callback is pending, ignore the vanished request.
|
||||
return;
|
||||
}
|
||||
final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents
|
||||
.get(organizer.asBinder());
|
||||
// Remove any pending events since this TaskFragment is being removed.
|
||||
for (int i = pendingEvents.size() - 1; i >= 0; i--) {
|
||||
final PendingTaskFragmentEvent event = pendingEvents.get(i);
|
||||
if (taskFragment == event.mTaskFragment) {
|
||||
pendingEvents.remove(i);
|
||||
}
|
||||
}
|
||||
if (!taskFragment.mTaskFragmentAppearedSent) {
|
||||
return;
|
||||
}
|
||||
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||
addPendingEvent(new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_VANISHED, organizer)
|
||||
.setTaskFragment(taskFragment)
|
||||
.build();
|
||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||
.build());
|
||||
state.removeTaskFragment(taskFragment);
|
||||
}
|
||||
|
||||
@@ -517,14 +510,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
int opType, @NonNull Throwable exception) {
|
||||
validateAndGetState(organizer);
|
||||
Slog.w(TAG, "onTaskFragmentError ", exception);
|
||||
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||
addPendingEvent(new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_ERROR, organizer)
|
||||
.setErrorCallbackToken(errorCallbackToken)
|
||||
.setTaskFragment(taskFragment)
|
||||
.setException(exception)
|
||||
.setOpType(opType)
|
||||
.build();
|
||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||
.build());
|
||||
// Make sure the error event will be dispatched if there are no other changes.
|
||||
mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
|
||||
}
|
||||
@@ -554,11 +546,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
|
||||
return;
|
||||
}
|
||||
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
|
||||
addPendingEvent(new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer)
|
||||
.setActivity(activity)
|
||||
.build();
|
||||
mPendingTaskFragmentEvents.add(pendingEvent);
|
||||
.build());
|
||||
}
|
||||
|
||||
private void addPendingEvent(@NonNull PendingTaskFragmentEvent event) {
|
||||
mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).add(event);
|
||||
}
|
||||
|
||||
private void removePendingEvent(@NonNull PendingTaskFragmentEvent event) {
|
||||
mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).remove(event);
|
||||
}
|
||||
|
||||
boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
|
||||
@@ -570,12 +569,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
// remove all of the children of the organized TaskFragment
|
||||
state.dispose();
|
||||
// Remove any pending event of this organizer.
|
||||
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
|
||||
final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
|
||||
if (event.mTaskFragmentOrg.asBinder().equals(organizer.asBinder())) {
|
||||
mPendingTaskFragmentEvents.remove(i);
|
||||
}
|
||||
}
|
||||
mPendingTaskFragmentEvents.remove(organizer.asBinder());
|
||||
mTaskFragmentOrganizerState.remove(organizer.asBinder());
|
||||
}
|
||||
|
||||
@@ -631,6 +625,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
private final Throwable mException;
|
||||
@Nullable
|
||||
private final ActivityRecord mActivity;
|
||||
@Nullable
|
||||
private final Task mTask;
|
||||
// Set when the event is deferred due to the host task is invisible. The defer time will
|
||||
// be the last active time of the host task.
|
||||
private long mDeferTime;
|
||||
@@ -642,6 +638,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
@Nullable IBinder errorCallbackToken,
|
||||
@Nullable Throwable exception,
|
||||
@Nullable ActivityRecord activity,
|
||||
@Nullable Task task,
|
||||
int opType) {
|
||||
mEventType = eventType;
|
||||
mTaskFragmentOrg = taskFragmentOrg;
|
||||
@@ -649,6 +646,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
mErrorCallbackToken = errorCallbackToken;
|
||||
mException = exception;
|
||||
mActivity = activity;
|
||||
mTask = task;
|
||||
mOpType = opType;
|
||||
}
|
||||
|
||||
@@ -680,11 +678,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
private Throwable mException;
|
||||
@Nullable
|
||||
private ActivityRecord mActivity;
|
||||
@Nullable
|
||||
private Task mTask;
|
||||
private int mOpType;
|
||||
|
||||
Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) {
|
||||
Builder(@EventType int eventType, @NonNull ITaskFragmentOrganizer taskFragmentOrg) {
|
||||
mEventType = eventType;
|
||||
mTaskFragmentOrg = taskFragmentOrg;
|
||||
mTaskFragmentOrg = requireNonNull(taskFragmentOrg);
|
||||
}
|
||||
|
||||
Builder setTaskFragment(@Nullable TaskFragment taskFragment) {
|
||||
@@ -697,13 +697,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setException(@Nullable Throwable exception) {
|
||||
mException = exception;
|
||||
Builder setException(@NonNull Throwable exception) {
|
||||
mException = requireNonNull(exception);
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setActivity(@Nullable ActivityRecord activity) {
|
||||
mActivity = activity;
|
||||
Builder setActivity(@NonNull ActivityRecord activity) {
|
||||
mActivity = requireNonNull(activity);
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder setTask(@NonNull Task task) {
|
||||
mTask = requireNonNull(task);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -714,17 +719,20 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
|
||||
PendingTaskFragmentEvent build() {
|
||||
return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
|
||||
mErrorCallbackToken, mException, mActivity, mOpType);
|
||||
mErrorCallbackToken, mException, mActivity, mTask, mOpType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
|
||||
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
|
||||
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
|
||||
if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
|
||||
return entry;
|
||||
final ITaskFragmentOrganizer organizer = tf.getTaskFragmentOrganizer();
|
||||
final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
|
||||
.get(organizer.asBinder());
|
||||
for (int i = events.size() - 1; i >= 0; i--) {
|
||||
final PendingTaskFragmentEvent event = events.get(i);
|
||||
if (tf == event.mTaskFragment && event.isLifecycleEvent()) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -733,10 +741,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
@Nullable
|
||||
private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
|
||||
int type) {
|
||||
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
|
||||
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
|
||||
if (taskFragment == entry.mTaskFragment && type == entry.mEventType) {
|
||||
return entry;
|
||||
final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
|
||||
final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
|
||||
.get(organizer.asBinder());
|
||||
for (int i = events.size() - 1; i >= 0; i--) {
|
||||
final PendingTaskFragmentEvent event = events.get(i);
|
||||
if (taskFragment == event.mTaskFragment && type == event.mEventType) {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -762,12 +773,25 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
|| mPendingTaskFragmentEvents.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final int organizerNum = mPendingTaskFragmentEvents.size();
|
||||
for (int i = 0; i < organizerNum; i++) {
|
||||
final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
|
||||
mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
|
||||
dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@NonNull List<PendingTaskFragmentEvent> pendingEvents) {
|
||||
if (pendingEvents.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ArrayList<Task> visibleTasks = new ArrayList<>();
|
||||
final ArrayList<Task> invisibleTasks = new ArrayList<>();
|
||||
final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>();
|
||||
for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
|
||||
final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
|
||||
for (int i = 0, n = pendingEvents.size(); i < n; i++) {
|
||||
final PendingTaskFragmentEvent event = pendingEvents.get(i);
|
||||
final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
|
||||
if (task != null && (task.lastActiveTime <= event.mDeferTime
|
||||
|| !(isTaskVisible(task, visibleTasks, invisibleTasks)
|
||||
@@ -783,27 +807,26 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
return;
|
||||
}
|
||||
|
||||
mTmpOrganizerToTransactionMap.clear();
|
||||
mTmpOrganizerList.clear();
|
||||
mTmpTaskSet.clear();
|
||||
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
|
||||
for (int i = 0; i < numEvents; i++) {
|
||||
final PendingTaskFragmentEvent event = candidateEvents.get(i);
|
||||
if (!mTmpOrganizerToTransactionMap.containsKey(event.mTaskFragmentOrg.asBinder())) {
|
||||
mTmpOrganizerToTransactionMap.put(event.mTaskFragmentOrg.asBinder(),
|
||||
new TaskFragmentTransaction());
|
||||
mTmpOrganizerList.add(event.mTaskFragmentOrg);
|
||||
if (event.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED
|
||||
|| event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED) {
|
||||
final Task task = event.mTaskFragment.getTask();
|
||||
if (mTmpTaskSet.add(task)) {
|
||||
// Make sure the organizer know about the Task config.
|
||||
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
|
||||
.setTask(task)
|
||||
.build()));
|
||||
}
|
||||
}
|
||||
mTmpOrganizerToTransactionMap.get(event.mTaskFragmentOrg.asBinder())
|
||||
.addChange(prepareChange(event));
|
||||
transaction.addChange(prepareChange(event));
|
||||
}
|
||||
final int numOrganizers = mTmpOrganizerList.size();
|
||||
for (int i = 0; i < numOrganizers; i++) {
|
||||
final ITaskFragmentOrganizer organizer = mTmpOrganizerList.get(i);
|
||||
dispatchTransactionInfo(organizer,
|
||||
mTmpOrganizerToTransactionMap.get(organizer.asBinder()));
|
||||
}
|
||||
mPendingTaskFragmentEvents.removeAll(candidateEvents);
|
||||
mTmpOrganizerToTransactionMap.clear();
|
||||
mTmpOrganizerList.clear();
|
||||
mTmpTaskSet.clear();
|
||||
dispatchTransactionInfo(organizer, transaction);
|
||||
pendingEvents.removeAll(candidateEvents);
|
||||
}
|
||||
|
||||
private static boolean isTaskVisible(@NonNull Task task,
|
||||
@@ -831,10 +854,16 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
return;
|
||||
}
|
||||
|
||||
final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
|
||||
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
|
||||
// Make sure the organizer know about the Task config.
|
||||
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
|
||||
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
|
||||
.setTask(taskFragment.getTask())
|
||||
.build()));
|
||||
transaction.addChange(prepareChange(event));
|
||||
dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
|
||||
mPendingTaskFragmentEvents.remove(event);
|
||||
mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
|
||||
}
|
||||
|
||||
private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
|
||||
@@ -867,7 +896,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
|
||||
return state.prepareTaskFragmentInfoChanged(taskFragment);
|
||||
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
|
||||
return state.prepareTaskFragmentParentInfoChanged(taskFragment);
|
||||
return state.prepareTaskFragmentParentInfoChanged(event.mTask);
|
||||
case PendingTaskFragmentEvent.EVENT_ERROR:
|
||||
return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
|
||||
event.mOpType, event.mException);
|
||||
|
||||
@@ -75,6 +75,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Build/Install/Run:
|
||||
@@ -92,7 +94,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
private TaskFragmentOrganizerToken mOrganizerToken;
|
||||
private ITaskFragmentOrganizer mIOrganizer;
|
||||
private TaskFragment mTaskFragment;
|
||||
private TaskFragmentInfo mTaskFragmentInfo;
|
||||
private IBinder mFragmentToken;
|
||||
private WindowContainerTransaction mTransaction;
|
||||
private WindowContainerToken mFragmentWindowToken;
|
||||
@@ -100,14 +101,19 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
private IBinder mErrorToken;
|
||||
private Rect mTaskFragBounds;
|
||||
|
||||
@Mock
|
||||
private TaskFragmentInfo mTaskFragmentInfo;
|
||||
@Mock
|
||||
private Task mTask;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mWindowOrganizerController = mAtm.mWindowOrganizerController;
|
||||
mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
|
||||
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
|
||||
mOrganizerToken = mOrganizer.getOrganizerToken();
|
||||
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
|
||||
mTaskFragmentInfo = mock(TaskFragmentInfo.class);
|
||||
mFragmentToken = new Binder();
|
||||
mTaskFragment =
|
||||
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
|
||||
@@ -131,6 +137,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
|
||||
@Test
|
||||
public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
|
||||
doReturn(mTask).when(mTaskFragment).getTask();
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> mController
|
||||
.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
|
||||
|
||||
@@ -140,16 +148,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> mController
|
||||
.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> mController
|
||||
.onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
|
||||
mTaskFragment));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnTaskFragmentAppeared() {
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
|
||||
// No-op when the TaskFragment is not attached.
|
||||
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
|
||||
|
||||
// Send callback when the TaskFragment is attached.
|
||||
setupMockParent(mTaskFragment, mTask);
|
||||
|
||||
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
@@ -159,9 +172,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
@Test
|
||||
public void testOnTaskFragmentInfoChanged() {
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
setupMockParent(mTaskFragment, mTask);
|
||||
|
||||
// No-op if onTaskFragmentAppeared is not called yet.
|
||||
mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
|
||||
mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
|
||||
|
||||
// Call onTaskFragmentAppeared first.
|
||||
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer).onTaskFragmentAppeared(any());
|
||||
|
||||
// No callback if the info is not changed.
|
||||
doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
|
||||
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
|
||||
@@ -193,50 +218,75 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
verify(mOrganizer).onTaskFragmentVanished(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnTaskFragmentVanished_clearUpRemaining() {
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
setupMockParent(mTaskFragment, mTask);
|
||||
|
||||
// Not trigger onTaskFragmentAppeared.
|
||||
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
|
||||
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
|
||||
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
|
||||
verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
|
||||
|
||||
// Not trigger onTaskFragmentInfoChanged.
|
||||
// Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
|
||||
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
clearInvocations(mOrganizer);
|
||||
doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
|
||||
mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
|
||||
mTaskFragment);
|
||||
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
|
||||
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
|
||||
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
|
||||
verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnTaskFragmentParentInfoChanged() {
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
final Task parent = mock(Task.class);
|
||||
final Configuration parentConfig = new Configuration();
|
||||
parentConfig.smallestScreenWidthDp = 10;
|
||||
doReturn(parent).when(mTaskFragment).getTask();
|
||||
doReturn(parentConfig).when(parent).getConfiguration();
|
||||
// Task needs to be visible
|
||||
parent.lastActiveTime = 100;
|
||||
doReturn(true).when(parent).shouldBeVisible(any());
|
||||
setupMockParent(mTaskFragment, mTask);
|
||||
mTask.getConfiguration().smallestScreenWidthDp = 10;
|
||||
|
||||
mTaskFragment.mTaskFragmentAppearedSent = true;
|
||||
mController.onTaskFragmentParentInfoChanged(
|
||||
mController.onTaskFragmentAppeared(
|
||||
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
|
||||
|
||||
// No extra callback if the info is not changed.
|
||||
clearInvocations(mOrganizer);
|
||||
|
||||
mController.onTaskFragmentParentInfoChanged(
|
||||
mController.onTaskFragmentInfoChanged(
|
||||
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
|
||||
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
|
||||
|
||||
// Trigger callback if the size is changed.
|
||||
parentConfig.smallestScreenWidthDp = 100;
|
||||
mController.onTaskFragmentParentInfoChanged(
|
||||
mTask.getConfiguration().smallestScreenWidthDp = 100;
|
||||
mController.onTaskFragmentInfoChanged(
|
||||
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
|
||||
|
||||
// Trigger callback if the windowing mode is changed.
|
||||
clearInvocations(mOrganizer);
|
||||
parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
mController.onTaskFragmentParentInfoChanged(
|
||||
mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
mController.onTaskFragmentInfoChanged(
|
||||
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1091,4 +1141,15 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
.put(mFragmentToken, mTaskFragment);
|
||||
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
}
|
||||
|
||||
/** Setups the mock Task as the parent of the given TaskFragment. */
|
||||
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
|
||||
doReturn(mockParent).when(taskFragment).getTask();
|
||||
final Configuration taskConfig = new Configuration();
|
||||
doReturn(taskConfig).when(mockParent).getConfiguration();
|
||||
|
||||
// Task needs to be visible
|
||||
mockParent.lastActiveTime = 100;
|
||||
doReturn(true).when(mockParent).shouldBeVisible(any());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user