Merge "Notify organizer about TaskFragment parent windowing mode change" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-05-03 01:32:14 +00:00
committed by Android (Google) Code Review
8 changed files with 207 additions and 23 deletions

View File

@@ -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(

View File

@@ -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.

View File

@@ -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());

View File

@@ -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.

View File

@@ -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;

View File

@@ -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());
}
}

View File

@@ -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,

View File

@@ -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();