Notify organizer about TaskFragment parent windowing mode change
1. When the Task windowing mode changes, notify the organizer. 2. When organizer requests windowing mode change, record the last request value to avoid unnecessary IPC call. Bug: 230446204 Test: atest WmTests:TaskFragmentOrganizerControllerTest Test: atest WMJetpackUnitTests:SplitPresenterTest Change-Id: I30c5dbf7b70d085647490943508bd5d22fda3ead
This commit is contained in:
@@ -133,8 +133,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
// Create or resize the launching TaskFragment.
|
||||
if (mFragmentInfos.containsKey(launchingFragmentToken)) {
|
||||
resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds);
|
||||
wct.setWindowingMode(mFragmentInfos.get(launchingFragmentToken).getToken(),
|
||||
windowingMode);
|
||||
updateWindowingMode(wct, launchingFragmentToken, windowingMode);
|
||||
} else {
|
||||
createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken,
|
||||
launchingFragmentBounds, windowingMode, launchingActivity);
|
||||
@@ -157,7 +156,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
|
||||
resizeTaskFragment(wct, fragmentToken, new Rect());
|
||||
setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */);
|
||||
setWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED);
|
||||
updateWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +259,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
|
||||
wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds);
|
||||
}
|
||||
|
||||
private void setWindowingMode(WindowContainerTransaction wct, IBinder fragmentToken,
|
||||
void updateWindowingMode(WindowContainerTransaction wct, IBinder fragmentToken,
|
||||
@WindowingMode int windowingMode) {
|
||||
if (!mFragmentInfos.containsKey(fragmentToken)) {
|
||||
throw new IllegalArgumentException(
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package androidx.window.extensions.embedding;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
|
||||
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
|
||||
@@ -179,6 +180,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
// Enter PIP.
|
||||
// All overrides will be cleanup.
|
||||
container.setLastRequestedBounds(null /* bounds */);
|
||||
container.setLastRequestedWindowingMode(WINDOWING_MODE_UNDEFINED);
|
||||
cleanupForEnterPip(wct, container);
|
||||
} else if (wasInPip) {
|
||||
// Exit PIP.
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.util.LayoutDirection;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowMetrics;
|
||||
import android.window.TaskFragmentCreationParams;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
@@ -122,7 +121,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
|
||||
primaryActivity.getActivityToken(), secondaryRectBounds,
|
||||
windowingMode);
|
||||
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
|
||||
|
||||
// Set adjacent to each other so that the containers below will be invisible.
|
||||
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule);
|
||||
@@ -198,18 +196,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
container = mController.newContainer(activity, taskId);
|
||||
final int windowingMode = mController.getTaskContainer(taskId)
|
||||
.getWindowingModeForSplitTaskFragment(bounds);
|
||||
final TaskFragmentCreationParams fragmentOptions =
|
||||
createFragmentOptions(
|
||||
container.getTaskFragmentToken(),
|
||||
activity.getActivityToken(),
|
||||
bounds,
|
||||
windowingMode);
|
||||
wct.createTaskFragment(fragmentOptions);
|
||||
|
||||
createTaskFragment(wct, container.getTaskFragmentToken(), activity.getActivityToken(),
|
||||
bounds, windowingMode);
|
||||
wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(),
|
||||
activity.getActivityToken());
|
||||
|
||||
container.setLastRequestedBounds(bounds);
|
||||
} else {
|
||||
resizeTaskFragmentIfRegistered(wct, container, bounds);
|
||||
final int windowingMode = mController.getTaskContainer(taskId)
|
||||
@@ -262,9 +252,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
|
||||
}
|
||||
applyTransaction(wct);
|
||||
|
||||
primaryContainer.setLastRequestedBounds(primaryRectBounds);
|
||||
secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,10 +333,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
@NonNull TaskFragmentContainer container,
|
||||
@WindowingMode int windowingMode) {
|
||||
if (container.getInfo() != null) {
|
||||
wct.setWindowingMode(container.getInfo().getToken(), windowingMode);
|
||||
updateWindowingMode(wct, container.getTaskFragmentToken(), windowingMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
|
||||
@NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
|
||||
final TaskFragmentContainer container = mController.getContainer(fragmentToken);
|
||||
if (container == null) {
|
||||
throw new IllegalStateException(
|
||||
"Creating a task fragment that is not registered with controller.");
|
||||
}
|
||||
|
||||
container.setLastRequestedBounds(bounds);
|
||||
container.setLastRequestedWindowingMode(windowingMode);
|
||||
super.createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
|
||||
@Nullable Rect bounds) {
|
||||
@@ -368,6 +369,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
|
||||
super.resizeTaskFragment(wct, fragmentToken, bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateWindowingMode(@NonNull WindowContainerTransaction wct,
|
||||
@NonNull IBinder fragmentToken, @WindowingMode int windowingMode) {
|
||||
final TaskFragmentContainer container = mController.getContainer(fragmentToken);
|
||||
if (container == null) {
|
||||
throw new IllegalStateException("Setting windowing mode for a task fragment that is"
|
||||
+ " not registered with controller.");
|
||||
}
|
||||
|
||||
if (container.isLastRequestedWindowingModeEqual(windowingMode)) {
|
||||
// Return early if the windowing mode were already requested
|
||||
return;
|
||||
}
|
||||
|
||||
container.setLastRequestedWindowingMode(windowingMode);
|
||||
super.updateWindowingMode(wct, fragmentToken, windowingMode);
|
||||
}
|
||||
|
||||
boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) {
|
||||
final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer());
|
||||
return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule());
|
||||
|
||||
@@ -106,7 +106,7 @@ class TaskContainer {
|
||||
int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
|
||||
// Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
|
||||
// will be set to UNDEFINED which will then inherit the Task windowing mode.
|
||||
if (taskFragmentBounds == null || taskFragmentBounds.isEmpty()) {
|
||||
if (taskFragmentBounds == null || taskFragmentBounds.isEmpty() || isInPictureInPicture()) {
|
||||
return WINDOWING_MODE_UNDEFINED;
|
||||
}
|
||||
// We use WINDOWING_MODE_MULTI_WINDOW when the Task is fullscreen.
|
||||
|
||||
@@ -17,11 +17,13 @@
|
||||
package androidx.window.extensions.embedding;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.WindowConfiguration.WindowingMode;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
@@ -72,6 +74,12 @@ class TaskFragmentContainer {
|
||||
*/
|
||||
private final Rect mLastRequestedBounds = new Rect();
|
||||
|
||||
/**
|
||||
* Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}.
|
||||
*/
|
||||
@WindowingMode
|
||||
private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
/**
|
||||
* Creates a container with an existing activity that will be re-parented to it in a window
|
||||
* container transaction.
|
||||
@@ -300,6 +308,20 @@ class TaskFragmentContainer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if last requested windowing mode is equal to the provided value.
|
||||
*/
|
||||
boolean isLastRequestedWindowingModeEqual(@WindowingMode int windowingMode) {
|
||||
return mLastRequestedWindowingMode == windowingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the last requested windowing mode.
|
||||
*/
|
||||
void setLastRequestedWindowingMode(@WindowingMode int windowingModes) {
|
||||
mLastRequestedWindowingMode = windowingModes;
|
||||
}
|
||||
|
||||
/** Gets the parent leaf Task id. */
|
||||
int getTaskId() {
|
||||
return mTaskId;
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 androidx.window.extensions.embedding;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.window.TaskFragmentInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Test class for {@link SplitPresenter}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMJetpackUnitTests:SplitPresenterTest
|
||||
*/
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SplitPresenterTest {
|
||||
private static final int TASK_ID = 10;
|
||||
private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
|
||||
|
||||
@Mock
|
||||
private Activity mActivity;
|
||||
@Mock
|
||||
private Resources mActivityResources;
|
||||
@Mock
|
||||
private TaskFragmentInfo mTaskFragmentInfo;
|
||||
@Mock
|
||||
private WindowContainerTransaction mTransaction;
|
||||
private SplitController mController;
|
||||
private SplitPresenter mPresenter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mController = new SplitController();
|
||||
mPresenter = mController.mPresenter;
|
||||
spyOn(mController);
|
||||
spyOn(mPresenter);
|
||||
final Configuration activityConfig = new Configuration();
|
||||
activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
|
||||
activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS);
|
||||
doReturn(mActivityResources).when(mActivity).getResources();
|
||||
doReturn(activityConfig).when(mActivityResources).getConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTaskFragment() {
|
||||
final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
|
||||
mPresenter.createTaskFragment(mTransaction, container.getTaskFragmentToken(),
|
||||
mActivity.getActivityToken(), TASK_BOUNDS, WINDOWING_MODE_MULTI_WINDOW);
|
||||
|
||||
assertTrue(container.areLastRequestedBoundsEqual(TASK_BOUNDS));
|
||||
assertTrue(container.isLastRequestedWindowingModeEqual(WINDOWING_MODE_MULTI_WINDOW));
|
||||
verify(mTransaction).createTaskFragment(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResizeTaskFragment() {
|
||||
final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
|
||||
mPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), mTaskFragmentInfo);
|
||||
mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS);
|
||||
|
||||
assertTrue(container.areLastRequestedBoundsEqual(TASK_BOUNDS));
|
||||
verify(mTransaction).setBounds(any(), eq(TASK_BOUNDS));
|
||||
|
||||
// No request to set the same bounds.
|
||||
clearInvocations(mTransaction);
|
||||
mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS);
|
||||
|
||||
verify(mTransaction, never()).setBounds(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWindowingMode() {
|
||||
final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
|
||||
mPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), mTaskFragmentInfo);
|
||||
mPresenter.updateWindowingMode(mTransaction, container.getTaskFragmentToken(),
|
||||
WINDOWING_MODE_MULTI_WINDOW);
|
||||
|
||||
assertTrue(container.isLastRequestedWindowingModeEqual(WINDOWING_MODE_MULTI_WINDOW));
|
||||
verify(mTransaction).setWindowingMode(any(), eq(WINDOWING_MODE_MULTI_WINDOW));
|
||||
|
||||
// No request to set the same windowing mode.
|
||||
clearInvocations(mTransaction);
|
||||
mPresenter.updateWindowingMode(mTransaction, container.getTaskFragmentToken(),
|
||||
WINDOWING_MODE_MULTI_WINDOW);
|
||||
|
||||
verify(mTransaction, never()).setWindowingMode(any(), anyInt());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -197,7 +197,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|
||||
final Task parent = tf.getParent().asTask();
|
||||
final Configuration parentConfig = parent.getConfiguration();
|
||||
final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
|
||||
if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)) {
|
||||
if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
|
||||
&& parentConfig.windowConfiguration.getWindowingMode()
|
||||
== lastParentConfig.windowConfiguration.getWindowingMode()) {
|
||||
return;
|
||||
}
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
|
||||
|
||||
@@ -202,9 +202,17 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
|
||||
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any());
|
||||
|
||||
// Trigger callback if the info is changed.
|
||||
// Trigger callback if the size is changed.
|
||||
parentConfig.smallestScreenWidthDp = 100;
|
||||
mController.onTaskFragmentParentInfoChanged(
|
||||
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any());
|
||||
|
||||
// Trigger callback if the windowing mode is changed.
|
||||
clearInvocations(mOrganizer);
|
||||
parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||
mController.onTaskFragmentParentInfoChanged(
|
||||
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
|
||||
mController.dispatchPendingEvents();
|
||||
|
||||
Reference in New Issue
Block a user