Merge "Pass Task configuration changed for all TaskFragments at once" into tm-qpr-dev
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