From 35b2942bced5a6ebd295f8b39cd1fbde137aca09 Mon Sep 17 00:00:00 2001 From: Chris Li Date: Mon, 16 Jan 2023 15:19:27 +0800 Subject: Fix ActivityEmbedding flicker when dragging freeform window Before, when dragging freeform window without resizing, TaskFragmentOrganizer would update the TaskFragment bounds to abosulte bounds, which can have a racing condition when the freeform Task is also repositioning. Now, TaskFragmentOrganizer only set relative bounds, so that it won't trigger unnecessary change transition. Also, don't start change transition when the Task isDragResizing. Bug: 256967281 Test: atest WMJetpackUnitTests:SplitPresenterTest Test: atest WmTests:TaskFragmentTest Test: atest WmTests:TaskFragmentOrganizerControllerTest Test: manually verify dragging freeform with ActivityEmbedding Change-Id: I87535036c9f69a7b50b2fe6bdf3e996ae73a8a64 --- .../embedding/JetpackTaskFragmentOrganizer.java | 35 ++-- .../extensions/embedding/SplitPresenter.java | 81 +++++---- .../window/extensions/embedding/TaskContainer.java | 9 + .../embedding/TaskFragmentContainer.java | 14 +- .../extensions/embedding/SplitPresenterTest.java | 187 ++++++++++++++++----- 5 files changed, 224 insertions(+), 102 deletions(-) (limited to 'libs/WindowManager/Jetpack') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java index ee8ec1dd1008..67963a36713c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java @@ -109,38 +109,38 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { * be resized based on {@param launchingFragmentBounds}. * Otherwise, we will create a new TaskFragment with the given * token for the {@param launchingActivity}. - * @param launchingFragmentBounds the initial bounds for the launching TaskFragment. + * @param launchingRelBounds the initial relative bounds for the launching TaskFragment. * @param launchingActivity the Activity to put on the left hand side of the split as the * primary. * @param secondaryFragmentToken token to create the secondary TaskFragment with. - * @param secondaryFragmentBounds the initial bounds for the secondary TaskFragment + * @param secondaryRelBounds the initial relative bounds for the secondary TaskFragment * @param activityIntent Intent to start the secondary Activity with. * @param activityOptions ActivityOptions to start the secondary Activity with. * @param windowingMode the windowing mode to set for the TaskFragments. * @param splitAttributes the {@link SplitAttributes} to represent the split. */ void startActivityToSide(@NonNull WindowContainerTransaction wct, - @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds, + @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingRelBounds, @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken, - @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent, + @NonNull Rect secondaryRelBounds, @NonNull Intent activityIntent, @Nullable Bundle activityOptions, @NonNull SplitRule rule, @WindowingMode int windowingMode, @NonNull SplitAttributes splitAttributes) { final IBinder ownerToken = launchingActivity.getActivityToken(); // Create or resize the launching TaskFragment. if (mFragmentInfos.containsKey(launchingFragmentToken)) { - resizeTaskFragment(wct, launchingFragmentToken, launchingFragmentBounds); + resizeTaskFragment(wct, launchingFragmentToken, launchingRelBounds); updateWindowingMode(wct, launchingFragmentToken, windowingMode); } else { createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken, - launchingFragmentBounds, windowingMode, launchingActivity); + launchingRelBounds, windowingMode, launchingActivity); } updateAnimationParams(wct, launchingFragmentToken, splitAttributes); // Create a TaskFragment for the secondary activity. final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( getOrganizerToken(), secondaryFragmentToken, ownerToken) - .setInitialBounds(secondaryFragmentBounds) + .setInitialRelativeBounds(secondaryRelBounds) .setWindowingMode(windowingMode) // Make sure to set the paired fragment token so that the new TaskFragment will be // positioned right above the paired TaskFragment. @@ -190,8 +190,9 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { * have to be a child of this task fragment, but must belong to the same task. */ void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, - @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) { - createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode, + @NonNull IBinder ownerToken, @NonNull Rect relBounds, + @WindowingMode int windowingMode) { + createTaskFragment(wct, fragmentToken, ownerToken, relBounds, windowingMode, null /* pairedActivityToken */); } @@ -203,11 +204,11 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { * positioned right above it. */ void createTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, - @NonNull IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode, + @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode, @Nullable IBinder pairedActivityToken) { final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder( getOrganizerToken(), fragmentToken, ownerToken) - .setInitialBounds(bounds) + .setInitialRelativeBounds(relBounds) .setWindowingMode(windowingMode) .setPairedActivityToken(pairedActivityToken) .build(); @@ -229,10 +230,10 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { * have to be a child of this task fragment, but must belong to the same task. */ private void createTaskFragmentAndReparentActivity(@NonNull WindowContainerTransaction wct, - @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect bounds, + @NonNull IBinder fragmentToken, @NonNull IBinder ownerToken, @NonNull Rect relBounds, @WindowingMode int windowingMode, @NonNull Activity activity) { final IBinder reparentActivityToken = activity.getActivityToken(); - createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode, + createTaskFragment(wct, fragmentToken, ownerToken, relBounds, windowingMode, reparentActivityToken); wct.reparentActivityToTaskFragment(fragmentToken, reparentActivityToken); } @@ -280,15 +281,15 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer { } void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, - @Nullable Rect bounds) { + @Nullable Rect relBounds) { if (!mFragmentInfos.containsKey(fragmentToken)) { throw new IllegalArgumentException( "Can't find an existing TaskFragment with fragmentToken=" + fragmentToken); } - if (bounds == null) { - bounds = new Rect(); + if (relBounds == null) { + relBounds = new Rect(); } - wct.setBounds(mFragmentInfos.get(fragmentToken).getToken(), bounds); + wct.setRelativeBounds(mFragmentInfos.get(fragmentToken).getToken(), relBounds); } void updateWindowingMode(@NonNull WindowContainerTransaction wct, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 85a00dfc010c..2b93682d791f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -180,22 +180,21 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { primaryActivity, secondaryIntent); final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule, minDimensionsPair); - final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties, + final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties, splitAttributes); final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct, - primaryActivity, primaryRectBounds, splitAttributes, null /* containerToAvoid */); + primaryActivity, primaryRelBounds, splitAttributes, null /* containerToAvoid */); // Create new empty task fragment final int taskId = primaryContainer.getTaskId(); final TaskFragmentContainer secondaryContainer = mController.newContainer( secondaryIntent, primaryActivity, taskId); - final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties, + final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes); final int windowingMode = mController.getTaskContainer(taskId) - .getWindowingModeForSplitTaskFragment(secondaryRectBounds); + .getWindowingModeForSplitTaskFragment(secondaryRelBounds); createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(), - primaryActivity.getActivityToken(), secondaryRectBounds, - windowingMode); + primaryActivity.getActivityToken(), secondaryRelBounds, windowingMode); updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes); // Set adjacent to each other so that the containers below will be invisible. @@ -227,12 +226,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity); final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule, minDimensionsPair); - final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties, + final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties, splitAttributes); final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct, - primaryActivity, primaryRectBounds, splitAttributes, null /* containerToAvoid */); + primaryActivity, primaryRelBounds, splitAttributes, null /* containerToAvoid */); - final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties, + final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes); final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity( secondaryActivity); @@ -244,7 +243,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { containerToAvoid = curSecondaryContainer; } final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct, - secondaryActivity, secondaryRectBounds, splitAttributes, containerToAvoid); + secondaryActivity, secondaryRelBounds, splitAttributes, containerToAvoid); // Set adjacent to each other so that the containers below will be invisible. setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule, @@ -261,23 +260,23 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { */ private TaskFragmentContainer prepareContainerForActivity( @NonNull WindowContainerTransaction wct, @NonNull Activity activity, - @NonNull Rect bounds, @NonNull SplitAttributes splitAttributes, + @NonNull Rect relBounds, @NonNull SplitAttributes splitAttributes, @Nullable TaskFragmentContainer containerToAvoid) { TaskFragmentContainer container = mController.getContainerWithActivity(activity); final int taskId = container != null ? container.getTaskId() : activity.getTaskId(); if (container == null || container == containerToAvoid) { container = mController.newContainer(activity, taskId); final int windowingMode = mController.getTaskContainer(taskId) - .getWindowingModeForSplitTaskFragment(bounds); + .getWindowingModeForSplitTaskFragment(relBounds); final IBinder reparentActivityToken = activity.getActivityToken(); createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken, - bounds, windowingMode, reparentActivityToken); + relBounds, windowingMode, reparentActivityToken); wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(), reparentActivityToken); } else { - resizeTaskFragmentIfRegistered(wct, container, bounds); + resizeTaskFragmentIfRegistered(wct, container, relBounds); final int windowingMode = mController.getTaskContainer(taskId) - .getWindowingModeForSplitTaskFragment(bounds); + .getWindowingModeForSplitTaskFragment(relBounds); updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode); } updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes); @@ -301,9 +300,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @Nullable Bundle activityOptions, @NonNull SplitRule rule, @NonNull SplitAttributes splitAttributes, boolean isPlaceholder) { final TaskProperties taskProperties = getTaskProperties(launchingActivity); - final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties, + final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties, splitAttributes); - final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties, + final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes); TaskFragmentContainer primaryContainer = mController.getContainerWithActivity( @@ -320,11 +319,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { primaryContainer); final TaskContainer taskContainer = mController.getTaskContainer(taskId); final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment( - primaryRectBounds); + primaryRelBounds); mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer, rule, splitAttributes); - startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds, - launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds, + startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRelBounds, + launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRelBounds, activityIntent, activityOptions, rule, windowingMode, splitAttributes); if (isPlaceholder) { // When placeholder is launched in split, we should keep the focus on the primary. @@ -351,20 +350,20 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } final TaskProperties taskProperties = getTaskProperties(updatedContainer); final SplitAttributes splitAttributes = splitContainer.getSplitAttributes(); - final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties, + final Rect primaryRelBounds = getRelBoundsForPosition(POSITION_START, taskProperties, splitAttributes); - final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties, + final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes); final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); // Whether the placeholder is becoming side-by-side with the primary from fullscreen. final boolean isPlaceholderBecomingSplit = splitContainer.isPlaceholderContainer() && secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */) - && !secondaryRectBounds.isEmpty(); + && !secondaryRelBounds.isEmpty(); // If the task fragments are not registered yet, the positions will be updated after they // are created again. - resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds); - resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds); + resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRelBounds); + resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRelBounds); setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule, splitAttributes); if (isPlaceholderBecomingSplit) { @@ -373,7 +372,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } final TaskContainer taskContainer = updatedContainer.getTaskContainer(); final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment( - primaryRectBounds); + primaryRelBounds); updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode); updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode); updateAnimationParams(wct, primaryContainer.getTaskFragmentToken(), splitAttributes); @@ -405,11 +404,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // TODO(b/190433398): Handle resize if the fragment hasn't appeared yet. private void resizeTaskFragmentIfRegistered(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container, - @Nullable Rect bounds) { + @Nullable Rect relBounds) { if (container.getInfo() == null) { return; } - resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds); + resizeTaskFragment(wct, container.getTaskFragmentToken(), relBounds); } private void updateTaskFragmentWindowingModeIfRegistered( @@ -431,27 +430,27 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { "Creating a task fragment that is not registered with controller."); } - container.setLastRequestedBounds(fragmentOptions.getInitialBounds()); + container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds()); container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode()); super.createTaskFragment(wct, fragmentOptions); } @Override void resizeTaskFragment(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken, - @Nullable Rect bounds) { + @Nullable Rect relBounds) { TaskFragmentContainer container = mController.getContainer(fragmentToken); if (container == null) { throw new IllegalStateException( "Resizing a task fragment that is not registered with controller."); } - if (container.areLastRequestedBoundsEqual(bounds)) { + if (container.areLastRequestedBoundsEqual(relBounds)) { // Return early if the provided bounds were already requested return; } - container.setLastRequestedBounds(bounds); - super.resizeTaskFragment(wct, fragmentToken, bounds); + container.setLastRequestedBounds(relBounds); + super.resizeTaskFragment(wct, fragmentToken, relBounds); } @Override @@ -658,7 +657,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @VisibleForTesting @NonNull - Rect getBoundsForPosition(@Position int position, @NonNull TaskProperties taskProperties, + Rect getRelBoundsForPosition(@Position int position, @NonNull TaskProperties taskProperties, @NonNull SplitAttributes splitAttributes) { final Configuration taskConfiguration = taskProperties.getConfiguration(); final FoldingFeature foldingFeature = getFoldingFeature(taskProperties); @@ -671,16 +670,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { if (!shouldShowSplit(computedSplitAttributes)) { return new Rect(); } + final Rect bounds; switch (position) { case POSITION_START: - return getPrimaryBounds(taskConfiguration, computedSplitAttributes, foldingFeature); + bounds = getPrimaryBounds(taskConfiguration, computedSplitAttributes, + foldingFeature); + break; case POSITION_END: - return getSecondaryBounds(taskConfiguration, computedSplitAttributes, + bounds = getSecondaryBounds(taskConfiguration, computedSplitAttributes, foldingFeature); + break; case POSITION_FILL: default: - return new Rect(); + bounds = new Rect(); } + // Convert to relative bounds in parent coordinate. This is to avoid flicker when the Task + // resized before organizer requests have been applied. + taskProperties.translateAbsoluteBoundsToRelativeBounds(bounds); + return bounds; } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 03f4dc9c1167..f41295b77a0d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -243,6 +243,15 @@ class TaskContainer { return mConfiguration; } + /** Translates the given absolute bounds to relative bounds in this Task coordinate. */ + void translateAbsoluteBoundsToRelativeBounds(@NonNull Rect inOutBounds) { + if (inOutBounds.isEmpty()) { + return; + } + final Rect taskBounds = mConfiguration.windowConfiguration.getBounds(); + inOutBounds.offset(-taskBounds.left, -taskBounds.top); + } + /** * Obtains the {@link TaskProperties} for the task that the provided {@link Activity} is * associated with. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 17814c65e791..861cb49bf18f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -570,20 +570,22 @@ class TaskFragmentContainer { /** * Checks if last requested bounds are equal to the provided value. + * The requested bounds are relative bounds in parent coordinate. */ - boolean areLastRequestedBoundsEqual(@Nullable Rect bounds) { - return (bounds == null && mLastRequestedBounds.isEmpty()) - || mLastRequestedBounds.equals(bounds); + boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) { + return (relBounds == null && mLastRequestedBounds.isEmpty()) + || mLastRequestedBounds.equals(relBounds); } /** * Updates the last requested bounds. + * The requested bounds are relative bounds in parent coordinate. */ - void setLastRequestedBounds(@Nullable Rect bounds) { - if (bounds == null) { + void setLastRequestedBounds(@Nullable Rect relBounds) { + if (relBounds == null) { mLastRequestedBounds.setEmpty(); } else { - mLastRequestedBounds.set(bounds); + mLastRequestedBounds.set(relBounds); } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java index c5d932e4e0a6..a41e63f46f55 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java @@ -75,6 +75,7 @@ import android.window.TaskFragmentInfo; import android.window.TaskFragmentOperation; import android.window.WindowContainerTransaction; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -148,13 +149,13 @@ public class SplitPresenterTest { mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS); assertTrue(container.areLastRequestedBoundsEqual(TASK_BOUNDS)); - verify(mTransaction).setBounds(any(), eq(TASK_BOUNDS)); + verify(mTransaction).setRelativeBounds(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()); + verify(mTransaction, never()).setRelativeBounds(any(), any()); } @Test @@ -226,7 +227,7 @@ public class SplitPresenterTest { } @Test - public void testGetBoundsForPosition_expandContainers() { + public void testGetRelBoundsForPosition_expandContainers() { final TaskContainer.TaskProperties taskProperties = getTaskProperty(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType()) @@ -234,18 +235,40 @@ public class SplitPresenterTest { assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); } @Test - public void testGetBoundsForPosition_splitVertically() { + public void testGetRelBoundsForPosition_expandContainers_isRelativeToParent() { + final TaskContainer.TaskProperties taskProperties = getTaskProperty( + new Rect(100, 100, 500, 1000)); + final SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType()) + .build(); + + assertEquals("Task bounds must be reported.", + new Rect(), + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); + + assertEquals("Task bounds must be reported.", + new Rect(), + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + assertEquals("Task bounds must be reported.", + new Rect(), + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + } + + @Test + public void testGetRelBoundsForPosition_splitVertically() { final Rect primaryBounds = getSplitBounds(true /* isPrimary */, false /* splitHorizontally */); final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, @@ -258,14 +281,81 @@ public class SplitPresenterTest { assertEquals("Primary bounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); + + assertEquals("Secondary bounds must be reported.", + secondaryBounds, + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + assertEquals("Task bounds must be reported.", + new Rect(), + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + + splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) + .setLayoutDirection(SplitAttributes.LayoutDirection.RIGHT_TO_LEFT) + .build(); + + assertEquals("Secondary bounds must be reported.", + secondaryBounds, + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); + + assertEquals("Primary bounds must be reported.", + primaryBounds, + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + assertEquals("Task bounds must be reported.", + new Rect(), + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + + splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) + .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) + .build(); + // Layout direction should follow screen layout for SplitAttributes.LayoutDirection.LOCALE. + taskProperties.getConfiguration().screenLayout |= Configuration.SCREENLAYOUT_LAYOUTDIR_RTL; + + assertEquals("Secondary bounds must be reported.", + secondaryBounds, + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); + + assertEquals("Primary bounds must be reported.", + primaryBounds, + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + assertEquals("Task bounds must be reported.", + new Rect(), + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + } + + @Test + public void testGetRelBoundsForPosition_splitVertically_isRelativeToParent() { + // Calculate based on TASK_BOUNDS. + final Rect primaryBounds = getSplitBounds(true /* isPrimary */, + false /* splitHorizontally */); + final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, + false /* splitHorizontally */); + + // Offset TaskBounds to 100, 100. The returned rel bounds shouldn't be affected. + final Rect taskBounds = new Rect(TASK_BOUNDS); + taskBounds.offset(100, 100); + final TaskContainer.TaskProperties taskProperties = getTaskProperty(taskBounds); + SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) + .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) + .build(); + + assertEquals("Primary bounds must be reported.", + primaryBounds, + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Secondary bounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) @@ -274,14 +364,15 @@ public class SplitPresenterTest { assertEquals("Secondary bounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Primary bounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) @@ -292,18 +383,19 @@ public class SplitPresenterTest { assertEquals("Secondary bounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Primary bounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); } @Test - public void testGetBoundsForPosition_splitHorizontally() { + public void testGetRelBoundsForPosition_splitHorizontally() { final Rect primaryBounds = getSplitBounds(true /* isPrimary */, true /* splitHorizontally */); final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, @@ -316,14 +408,15 @@ public class SplitPresenterTest { assertEquals("Primary bounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Secondary bounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually()) @@ -332,18 +425,19 @@ public class SplitPresenterTest { assertEquals("Secondary bounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Primary bounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); } @Test - public void testGetBoundsForPosition_useHingeFallback() { + public void testGetRelBoundsForPosition_useHingeFallback() { final Rect primaryBounds = getSplitBounds(true /* isPrimary */, false /* splitHorizontally */); final Rect secondaryBounds = getSplitBounds(false /* isPrimary */, @@ -361,14 +455,15 @@ public class SplitPresenterTest { assertEquals("PrimaryBounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("SecondaryBounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); // Hinge is reported, but the host task is in multi-window mode. Still use fallback // splitType. @@ -379,14 +474,15 @@ public class SplitPresenterTest { assertEquals("PrimaryBounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("SecondaryBounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); // Hinge is reported, and the host task is in fullscreen, but layout direction doesn't match // folding area orientation. Still use fallback splitType. @@ -397,18 +493,19 @@ public class SplitPresenterTest { assertEquals("PrimaryBounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("SecondaryBounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); } @Test - public void testGetBoundsForPosition_fallbackToExpandContainers() { + public void testGetRelBoundsForPosition_fallbackToExpandContainers() { final TaskContainer.TaskProperties taskProperties = getTaskProperty(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.HingeSplitType( @@ -418,18 +515,19 @@ public class SplitPresenterTest { assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); } @Test - public void testGetBoundsForPosition_useHingeSplitType() { + public void testGetRelBoundsForPosition_useHingeSplitType() { final TaskContainer.TaskProperties taskProperties = getTaskProperty(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(new SplitAttributes.SplitType.HingeSplitType( @@ -455,14 +553,15 @@ public class SplitPresenterTest { assertEquals("PrimaryBounds must be reported.", primaryBounds, - mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_START, taskProperties, + splitAttributes)); assertEquals("SecondaryBounds must be reported.", secondaryBounds, - mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_END, taskProperties, splitAttributes)); assertEquals("Task bounds must be reported.", new Rect(), - mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); + mPresenter.getRelBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes)); } @Test @@ -601,8 +700,12 @@ public class SplitPresenterTest { } private static TaskContainer.TaskProperties getTaskProperty() { + return getTaskProperty(TASK_BOUNDS); + } + + private static TaskContainer.TaskProperties getTaskProperty(@NonNull Rect taskBounds) { final Configuration configuration = new Configuration(); - configuration.windowConfiguration.setBounds(TASK_BOUNDS); + configuration.windowConfiguration.setBounds(taskBounds); return new TaskContainer.TaskProperties(DEFAULT_DISPLAY, configuration); } } -- cgit v1.2.3-59-g8ed1b