Merge "Notify organizer about TaskFragment parent windowing mode change" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6725f73a88
@@ -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