Consolidate mTaskBounds logic to mConfiguration

After the horizontal layout feature, the split layout is not just
determined by task bounds, but the whole task Configuration should
be take account.

This CL removes the mTaskBounds logic and consolidate to use
TaskContainer#mConfiguration instead. This CL also introduces
ActivityClient#getTaskConfiguration to obtain task config at
TaskContainer initialization.

Bug: 240219484
Test: atest WMJetpackUnitTests

Change-Id: I5725ac92d841c53675c78b9e50e0d3fee3973f37
This commit is contained in:
Charles Chen
2022-09-29 17:19:21 +08:00
parent 7cf9a10a4e
commit e9d7d941ee
9 changed files with 74 additions and 101 deletions

View File

@@ -227,12 +227,13 @@ public class ActivityClient {
}
/**
* Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not
* found.
* Returns the {@link Configuration} of the task which hosts the Activity, or {@code null} if
* the task {@link Configuration} cannot be obtained.
*/
public int getTaskWindowingMode(IBinder activityToken) {
@Nullable
public Configuration getTaskConfiguration(IBinder activityToken) {
try {
return getActivityClientController().getTaskWindowingMode(activityToken);
return getActivityClientController().getTaskConfiguration(activityToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -78,7 +78,11 @@ interface IActivityClientController {
boolean willActivityBeVisible(in IBinder token);
int getDisplayId(in IBinder activityToken);
int getTaskForActivity(in IBinder token, in boolean onlyRoot);
int getTaskWindowingMode(in IBinder activityToken);
/**
* Returns the {@link Configuration} of the task which hosts the Activity, or {@code null} if
* the task {@link Configuration} cannot be obtained.
*/
Configuration getTaskConfiguration(in IBinder activityToken);
IBinder getActivityTokenBelow(IBinder token);
ComponentName getCallingActivity(in IBinder token);
String getCallingPackage(in IBinder token);

View File

@@ -41,7 +41,6 @@ import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAs
import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
import static androidx.window.extensions.embedding.SplitPresenter.getNonEmbeddedActivityBounds;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
import android.app.Activity;
@@ -464,7 +463,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// parentInfo#isVisibleRequested is true.
return;
}
onTaskContainerInfoChanged(taskContainer, parentInfo.getConfiguration());
if (isInPictureInPicture(parentInfo.getConfiguration())) {
// No need to update presentation in PIP until the Task exit PIP.
return;
@@ -614,12 +612,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
@GuardedBy("mLock")
private void onTaskContainerInfoChanged(@NonNull TaskContainer taskContainer,
@NonNull Configuration config) {
taskContainer.setTaskBounds(config.windowConfiguration.getBounds());
}
/** Returns whether the given {@link TaskContainer} may show in split. */
// Suppress GuardedBy warning because lint asks to mark this method as
// @GuardedBy(mPresenter.mController.mLock), which is mLock itself
@@ -1235,13 +1227,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
pendingAppearedIntent, taskContainer, this);
if (!taskContainer.isTaskBoundsInitialized()) {
// Get the initial bounds before the TaskFragment has appeared.
final Rect taskBounds = getNonEmbeddedActivityBounds(activityInTask);
if (!taskContainer.setTaskBounds(taskBounds)) {
Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
}
}
return container;
}

View File

@@ -932,11 +932,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
if (taskContainer != null) {
return taskContainer.getTaskProperties();
}
// Use a copy of configuration because activity's configuration may be updated later,
// or we may get unexpected TaskContainer's configuration if Activity's configuration is
// updated. An example is Activity is going to be in split.
return new TaskProperties(activity.getDisplayId(),
new Configuration(activity.getResources().getConfiguration()));
return TaskProperties.getTaskPropertiesFromActivity(activity);
}
@NonNull
@@ -950,16 +946,4 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// TODO(b/190433398): Supply correct insets.
return new WindowMetrics(taskBounds, WindowInsets.CONSUMED);
}
/** Obtains the bounds from a non-embedded Activity. */
@NonNull
static Rect getNonEmbeddedActivityBounds(@NonNull Activity activity) {
final WindowConfiguration windowConfiguration =
activity.getResources().getConfiguration().windowConfiguration;
if (!activity.isInMultiWindowMode()) {
// In fullscreen mode the max bounds should correspond to the task bounds.
return windowConfiguration.getMaxBounds();
}
return windowConfiguration.getBounds();
}
}

View File

@@ -20,14 +20,17 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.inMultiWindowMode;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
import android.util.Log;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.WindowContainerTransaction;
@@ -41,14 +44,11 @@ import java.util.Set;
/** Represents TaskFragments and split pairs below a Task. */
class TaskContainer {
private static final String TAG = TaskContainer.class.getSimpleName();
/** The unique task id. */
private final int mTaskId;
// TODO(b/240219484): consolidate to mConfiguration
/** Available window bounds of this Task. */
private final Rect mTaskBounds = new Rect();
/** Active TaskFragments in this Task. */
@NonNull
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
@@ -86,10 +86,10 @@ class TaskContainer {
throw new IllegalArgumentException("Invalid Task id");
}
mTaskId = taskId;
// Make a copy in case the activity's config is updated, and updates the TaskContainer's
// config unexpectedly.
mConfiguration = new Configuration(activityInTask.getResources().getConfiguration());
mDisplayId = activityInTask.getDisplayId();
final TaskProperties taskProperties = TaskProperties
.getTaskPropertiesFromActivity(activityInTask);
mConfiguration = taskProperties.getConfiguration();
mDisplayId = taskProperties.getDisplayId();
// Note that it is always called when there's a new Activity is started, which implies
// the host task is visible.
mIsVisible = true;
@@ -107,25 +107,6 @@ class TaskContainer {
return mIsVisible;
}
@NonNull
Rect getTaskBounds() {
return mTaskBounds;
}
/** Returns {@code true} if the bounds is changed. */
boolean setTaskBounds(@NonNull Rect taskBounds) {
if (!taskBounds.isEmpty() && !mTaskBounds.equals(taskBounds)) {
mTaskBounds.set(taskBounds);
return true;
}
return false;
}
/** Whether the Task bounds has been initialized. */
boolean isTaskBoundsInitialized() {
return !mTaskBounds.isEmpty();
}
@NonNull
Configuration getConfiguration() {
// Make a copy in case the config is updated unexpectedly.
@@ -261,5 +242,45 @@ class TaskContainer {
Configuration getConfiguration() {
return mConfiguration;
}
/**
* Obtains the {@link TaskProperties} for the task that the provided {@link Activity} is
* associated with.
* <p>
* Note that for most case, caller should use
* {@link SplitPresenter#getTaskProperties(Activity)} instead. This method is used before
* the {@code activity} goes into split.
* </p><p>
* If the {@link Activity} is in fullscreen, override
* {@link WindowConfiguration#getBounds()} with {@link WindowConfiguration#getMaxBounds()}
* in case the {@link Activity} is letterboxed. Otherwise, get the Task
* {@link Configuration} from the server side or use {@link Activity}'s
* {@link Configuration} as a fallback if the Task {@link Configuration} cannot be obtained.
*/
@NonNull
static TaskProperties getTaskPropertiesFromActivity(@NonNull Activity activity) {
final int displayId = activity.getDisplayId();
// Use a copy of configuration because activity's configuration may be updated later,
// or we may get unexpected TaskContainer's configuration if Activity's configuration is
// updated. An example is Activity is going to be in split.
final Configuration activityConfig = new Configuration(
activity.getResources().getConfiguration());
final WindowConfiguration windowConfiguration = activityConfig.windowConfiguration;
final int windowingMode = windowConfiguration.getWindowingMode();
if (!inMultiWindowMode(windowingMode)) {
// Use the max bounds in fullscreen in case the Activity is letterboxed.
windowConfiguration.setBounds(windowConfiguration.getMaxBounds());
return new TaskProperties(displayId, activityConfig);
}
final Configuration taskConfig = ActivityClient.getInstance()
.getTaskConfiguration(activity.getActivityToken());
if (taskConfig == null) {
Log.w(TAG, "Could not obtain task configuration for activity:" + activity);
// Still report activity config if task config cannot be obtained from the server
// side.
return new TaskProperties(displayId, activityConfig);
}
return new TaskProperties(displayId, taskConfig);
}
}
}

View File

@@ -35,7 +35,6 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
import android.window.WindowContext;
import android.window.WindowProvider;
import androidx.annotation.NonNull;
@@ -310,20 +309,21 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
final int windowingMode;
if (context instanceof Activity) {
windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
final Configuration taskConfig = ActivityClient.getInstance().getTaskConfiguration(
context.getActivityToken());
if (taskConfig == null) {
// If we cannot determine the task configuration for any reason, it is likely that
// we won't be able to determine its position correctly as well. DisplayFeatures'
// bounds in this case can't be computed correctly, so we should skip.
return false;
}
windowingMode = taskConfig.windowConfiguration.getWindowingMode();
} else {
// TODO(b/242674941): use task windowing mode for window context that associates with
// activity.
windowingMode = context.getResources().getConfiguration().windowConfiguration
.getWindowingMode();
}
if (windowingMode == -1) {
// If we cannot determine the task windowing mode for any reason, it is likely that we
// won't be able to determine its position correctly as well. DisplayFeatures' bounds
// in this case can't be computed correctly, so we should skip.
return false;
}
// It is recommended not to report any display features in multi-window mode, since it
// won't be possible to synchronize the display feature positions with window movement.
return !WindowConfiguration.inMultiWindowMode(windowingMode);

View File

@@ -261,7 +261,7 @@ public class SplitControllerTest {
assertNotNull(tf);
assertNotNull(taskContainer);
assertEquals(TASK_BOUNDS, taskContainer.getTaskBounds());
assertEquals(TASK_BOUNDS, taskContainer.getConfiguration().windowConfiguration.getBounds());
}
@Test

View File

@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static org.junit.Assert.assertEquals;
@@ -67,28 +66,6 @@ public class TaskContainerTest {
MockitoAnnotations.initMocks(this);
}
@Test
public void testIsTaskBoundsInitialized() {
final TaskContainer taskContainer = createTestTaskContainer();
assertFalse(taskContainer.isTaskBoundsInitialized());
taskContainer.setTaskBounds(TASK_BOUNDS);
assertTrue(taskContainer.isTaskBoundsInitialized());
}
@Test
public void testSetTaskBounds() {
final TaskContainer taskContainer = createTestTaskContainer();
assertFalse(taskContainer.setTaskBounds(new Rect()));
assertTrue(taskContainer.setTaskBounds(TASK_BOUNDS));
assertFalse(taskContainer.setTaskBounds(TASK_BOUNDS));
}
@Test
public void testGetWindowingModeForSplitTaskFragment() {
final TaskContainer taskContainer = createTestTaskContainer();

View File

@@ -580,17 +580,18 @@ class ActivityClientController extends IActivityClientController.Stub {
}
/**
* Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not
* found.
* Returns the {@link Configuration} of the task which hosts the Activity, or {@code null} if
* the task {@link Configuration} cannot be obtained.
*/
@Override
public int getTaskWindowingMode(IBinder activityToken) {
@Nullable
public Configuration getTaskConfiguration(IBinder activityToken) {
synchronized (mGlobalLock) {
final ActivityRecord ar = ActivityRecord.isInAnyTask(activityToken);
if (ar == null) {
return -1;
return null;
}
return ar.getTask().getWindowingMode();
return ar.getTask().getConfiguration();
}
}