Add TaskFragmentParentInfo

This CL adds a TaskFragmentParentInfo class and extends
onTaskFragmentParentInfoChanged to dispatch display ID and
visibility of the parent Task.
It makes SplitController able to track visibility and display
changes and used to update SplitContainers if there's a folding
state change.

Test: atest SplitControllerTest TaskFragmentOrganizerControllerTest
Bug: 243609832
Bug: 207494880

Change-Id: If3d5db621b20d4005ce0b60e46ac98e1d1e962e3
This commit is contained in:
Charles Chen
2022-09-13 22:39:10 +08:00
parent 894d516094
commit 3589fb907f
9 changed files with 231 additions and 32 deletions

View File

@@ -188,6 +188,10 @@ public final class TaskFragmentInfo implements Parcelable {
/**
* Returns {@code true} if the parameters that are important for task fragment organizers are
* equal between this {@link TaskFragmentInfo} and {@param that}.
* Note that this method is usually called with
* {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer(
* Configuration, Configuration)} to determine if this {@link TaskFragmentInfo} should
* be dispatched to the client.
*/
public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentInfo that) {
if (that == null) {

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.window;
/**
* The information about the parent Task of a particular TaskFragment
* @hide
*/
parcelable TaskFragmentParentInfo;

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.window;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
/**
* The information about the parent Task of a particular TaskFragment
* @hide
*/
public class TaskFragmentParentInfo implements Parcelable {
@NonNull
private final Configuration mConfiguration = new Configuration();
private final int mDisplayId;
private final boolean mVisibleRequested;
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
boolean visibleRequested) {
mConfiguration.setTo(configuration);
mDisplayId = displayId;
mVisibleRequested = visibleRequested;
}
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
mConfiguration.setTo(info.getConfiguration());
mDisplayId = info.mDisplayId;
mVisibleRequested = info.mVisibleRequested;
}
/** The {@link Configuration} of the parent Task */
@NonNull
public Configuration getConfiguration() {
return mConfiguration;
}
/**
* The display ID of the parent Task. {@link android.view.Display#INVALID_DISPLAY} means the
* Task is detached from previously associated display.
*/
public int getDisplayId() {
return mDisplayId;
}
/** Whether the parent Task is requested to be visible or not */
public boolean isVisibleRequested() {
return mVisibleRequested;
}
/**
* Returns {@code true} if the parameters which are important for task fragment
* organizers are equal between this {@link TaskFragmentParentInfo} and {@code that}.
* Note that this method is usually called with
* {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer(
* Configuration, Configuration)} to determine if this {@link TaskFragmentParentInfo} should
* be dispatched to the client.
*/
public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentParentInfo that) {
if (that == null) {
return false;
}
return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
&& mVisibleRequested == that.mVisibleRequested;
}
@WindowConfiguration.WindowingMode
private int getWindowingMode() {
return mConfiguration.windowConfiguration.getWindowingMode();
}
@Override
public String toString() {
return TaskFragmentParentInfo.class.getSimpleName() + ":{"
+ "config=" + mConfiguration
+ ", displayId=" + mDisplayId
+ ", visibleRequested=" + mVisibleRequested
+ "}";
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
mConfiguration.writeToParcel(dest, flags);
dest.writeInt(mDisplayId);
dest.writeBoolean(mVisibleRequested);
}
private TaskFragmentParentInfo(Parcel in) {
mConfiguration.readFromParcel(in);
mDisplayId = in.readInt();
mVisibleRequested = in.readBoolean();
}
public static final Creator<TaskFragmentParentInfo> CREATOR =
new Creator<TaskFragmentParentInfo>() {
@Override
public TaskFragmentParentInfo createFromParcel(Parcel in) {
return new TaskFragmentParentInfo(in);
}
@Override
public TaskFragmentParentInfo[] newArray(int size) {
return new TaskFragmentParentInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
}

View File

@@ -171,10 +171,6 @@ public final class TaskFragmentTransaction implements Parcelable {
/** @see #setTaskId(int) */
private int mTaskId;
/** @see #setTaskConfiguration(Configuration) */
@Nullable
private Configuration mTaskConfiguration;
/** @see #setErrorCallbackToken(IBinder) */
@Nullable
private IBinder mErrorCallbackToken;
@@ -191,6 +187,9 @@ public final class TaskFragmentTransaction implements Parcelable {
@Nullable
private IBinder mActivityToken;
@Nullable
private TaskFragmentParentInfo mTaskFragmentParentInfo;
public Change(@ChangeType int type) {
mType = type;
}
@@ -200,11 +199,11 @@ public final class TaskFragmentTransaction implements Parcelable {
mTaskFragmentToken = in.readStrongBinder();
mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
mTaskId = in.readInt();
mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
mErrorCallbackToken = in.readStrongBinder();
mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mActivityToken = in.readStrongBinder();
mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
}
@Override
@@ -213,11 +212,11 @@ public final class TaskFragmentTransaction implements Parcelable {
dest.writeStrongBinder(mTaskFragmentToken);
dest.writeTypedObject(mTaskFragmentInfo, flags);
dest.writeInt(mTaskId);
dest.writeTypedObject(mTaskConfiguration, flags);
dest.writeStrongBinder(mErrorCallbackToken);
dest.writeBundle(mErrorBundle);
dest.writeTypedObject(mActivityIntent, flags);
dest.writeStrongBinder(mActivityToken);
dest.writeTypedObject(mTaskFragmentParentInfo, flags);
}
/** The change is related to the TaskFragment created with this unique token. */
@@ -241,10 +240,10 @@ public final class TaskFragmentTransaction implements Parcelable {
return this;
}
// TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
/** Configuration of the parent Task. */
@NonNull
public Change setTaskConfiguration(@NonNull Configuration configuration) {
mTaskConfiguration = requireNonNull(configuration);
return this;
}
@@ -292,6 +291,19 @@ public final class TaskFragmentTransaction implements Parcelable {
return this;
}
// TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
/**
* Sets info of the parent Task of the embedded TaskFragment.
* @see TaskFragmentParentInfo
*
* @hide pending unhide
*/
@NonNull
public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
mTaskFragmentParentInfo = requireNonNull(info);
return this;
}
@ChangeType
public int getType() {
return mType;
@@ -311,9 +323,10 @@ public final class TaskFragmentTransaction implements Parcelable {
return mTaskId;
}
// TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
@Nullable
public Configuration getTaskConfiguration() {
return mTaskConfiguration;
return mTaskFragmentParentInfo.getConfiguration();
}
@Nullable
@@ -337,6 +350,13 @@ public final class TaskFragmentTransaction implements Parcelable {
return mActivityToken;
}
// TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
/** @hide pending unhide */
@Nullable
public TaskFragmentParentInfo getTaskFragmentParentInfo() {
return mTaskFragmentParentInfo;
}
@Override
public String toString() {
return "Change{ type=" + mType + " }";

View File

@@ -63,6 +63,7 @@ import android.util.Pair;
import android.util.Size;
import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -191,7 +192,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
onTaskFragmentVanished(wct, info);
break;
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
onTaskFragmentParentInfoChanged(wct, taskId, change.getTaskConfiguration());
onTaskFragmentParentInfoChanged(wct, taskId,
change.getTaskFragmentParentInfo());
break;
case TYPE_TASK_FRAGMENT_ERROR:
final Bundle errorBundle = change.getErrorBundle();
@@ -346,12 +348,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*
* @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
* @param taskId Id of the parent Task that is changed.
* @param parentConfig Config of the parent Task.
* @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
*/
@VisibleForTesting
@GuardedBy("mLock")
void onTaskFragmentParentInfoChanged(@NonNull WindowContainerTransaction wct,
int taskId, @NonNull Configuration parentConfig) {
int taskId, @NonNull TaskFragmentParentInfo parentInfo) {
// TODO(b/241043111): handles displayId and visibility here.
final Configuration parentConfig = parentInfo.getConfiguration();
onTaskConfigurationChanged(taskId, parentConfig);
if (isInPictureInPicture(parentConfig)) {
// No need to update presentation in PIP until the Task exit PIP.

View File

@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.ActivityManager.START_CANCELED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
@@ -75,6 +76,7 @@ import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -1038,15 +1040,16 @@ public class SplitControllerTest {
@Test
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
final Configuration taskConfig = new Configuration();
final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
DEFAULT_DISPLAY, true);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(TASK_ID)
.setTaskConfiguration(taskConfig));
.setTaskFragmentParentInfo(parentInfo));
mSplitController.onTransactionReady(transaction);
verify(mSplitController).onTaskFragmentParentInfoChanged(any(), eq(TASK_ID),
eq(taskConfig));
eq(parentInfo));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
}

View File

@@ -185,6 +185,7 @@ import android.view.WindowManager.TransitionOldType;
import android.window.ITaskOrganizer;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.StartingWindowInfo;
import android.window.TaskFragmentParentInfo;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -3518,6 +3519,14 @@ class Task extends TaskFragment {
return info;
}
/**
* Returns the {@link TaskFragmentParentInfo} which will send to the client
* {@link android.window.TaskFragmentOrganizer}
*/
TaskFragmentParentInfo getTaskFragmentParentInfo() {
return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), isVisibleRequested());
}
boolean isTaskId(int taskId) {
return mTaskId == taskId;
}

View File

@@ -49,6 +49,7 @@ import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -118,10 +119,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
/**
* Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
* Map from {@link Task#mTaskId} to the last {@link TaskFragmentParentInfo} sent to the
* organizer.
*/
private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
private final SparseArray<TaskFragmentParentInfo> mLastSentTaskFragmentParentInfos =
new SparseArray<>();
/**
@@ -225,7 +226,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
taskId = mTaskFragmentTaskIds.remove(tf);
if (!mTaskFragmentTaskIds.containsValue(taskId)) {
// No more TaskFragment in the Task.
mLastSentTaskFragmentParentConfigs.remove(taskId);
mLastSentTaskFragmentParentInfos.remove(taskId);
}
} else {
// This can happen if the appeared wasn't sent before remove.
@@ -260,25 +261,27 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Nullable
TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
@NonNull Task task) {
TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(@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()) {
final TaskFragmentParentInfo parentInfo = task.getTaskFragmentParentInfo();
final TaskFragmentParentInfo lastParentInfo = mLastSentTaskFragmentParentInfos
.get(taskId);
final Configuration lastParentConfig = lastParentInfo != null
? lastParentInfo.getConfiguration() : null;
if (parentInfo.equalsForTaskFragmentOrganizer(lastParentInfo)
&& configurationsAreEqualForOrganizer(parentInfo.getConfiguration(),
lastParentConfig)) {
return null;
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d",
task.getName(), taskId);
mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
mLastSentTaskFragmentParentInfos.put(taskId, new TaskFragmentParentInfo(parentInfo));
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(taskId)
.setTaskConfiguration(taskConfig);
.setTaskFragmentParentInfo(parentInfo);
}
@NonNull

View File

@@ -18,6 +18,7 @@ package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
import static android.window.TaskFragmentOrganizer.getTransitionType;
@@ -76,6 +77,7 @@ import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -271,7 +273,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test
public void testOnTaskFragmentParentInfoChanged() {
setupMockParent(mTaskFragment, mTask);
mTask.getConfiguration().smallestScreenWidthDp = 10;
mTask.getTaskFragmentParentInfo().getConfiguration().smallestScreenWidthDp = 10;
mController.onTaskFragmentAppeared(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
@@ -295,7 +297,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Trigger callback if the size is changed.
clearInvocations(mOrganizer);
mTask.getConfiguration().smallestScreenWidthDp = 100;
mTask.getTaskFragmentParentInfo().getConfiguration().smallestScreenWidthDp = 100;
mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
@@ -304,7 +306,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Trigger callback if the windowing mode is changed.
clearInvocations(mOrganizer);
mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
mTask.getTaskFragmentParentInfo().getConfiguration().windowConfiguration
.setWindowingMode(WINDOWING_MODE_PINNED);
mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
@@ -1268,7 +1271,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
final TaskFragmentTransaction.Change change = changes.get(0);
assertEquals(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, change.getType());
assertEquals(task.mTaskId, change.getTaskId());
assertEquals(task.getConfiguration(), change.getTaskConfiguration());
assertEquals(task.getTaskFragmentParentInfo(), change.getTaskFragmentParentInfo());
}
/** Asserts that there will be a transaction for TaskFragment error. */
@@ -1316,8 +1319,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
/** 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();
doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true))
.when(mockParent).getTaskFragmentParentInfo();
// Task needs to be visible
mockParent.lastActiveTime = 100;