From 4f6fc192155c78bee2ee60589073deb0061507b8 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Mon, 8 Aug 2022 16:06:15 +0800 Subject: [PATCH] Apply SplitAttributesCalculator to SplitController This CL implements SplitController#set(clear)SplitAttributesCalculator, and also invokes #computeSplitAttributesForParams in #computeSplitAttributes to compute the split attributes based on the current device state. fixes: 241043111 fixes: 241043028 Bug: 207494880 Test: atest WMJetpackUnitTests Change-Id: I0e3aadba451f2e585b048bfe2417239ed2f3ba3c --- .../extensions/embedding/SplitController.java | 46 ++++++++++++++++-- .../extensions/embedding/SplitPresenter.java | 31 ++++++++---- .../embedding/SplitControllerTest.java | 46 ++++++++++++++++++ .../embedding/SplitPresenterTest.java | 47 +++++++++++++++++++ 4 files changed, 158 insertions(+), 12 deletions(-) diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 6468fe87f2001..203ece091e46d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -103,6 +103,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Currently applied split configuration. @GuardedBy("mLock") private final List mSplitRules = new ArrayList<>(); + /** + * A developer-defined {@link SplitAttributes} calculator to compute the current + * {@link SplitAttributes} with the current device and window states. + * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)} + * and unregistered via {@link #clearSplitAttributesCalculator()}. + * This is called when: + * + */ + @GuardedBy("mLock") + @Nullable + private SplitAttributesCalculator mSplitAttributesCalculator; /** * Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info * below it. @@ -188,12 +205,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) { - // TODO: Implement this method + synchronized (mLock) { + mSplitAttributesCalculator = calculator; + } } @Override public void clearSplitAttributesCalculator() { - // TODO: Implement this method + synchronized (mLock) { + mSplitAttributesCalculator = null; + } + } + + @GuardedBy("mLock") + @Nullable + SplitAttributesCalculator getSplitAttributesCalculator() { + return mSplitAttributesCalculator; } @NonNull @@ -613,6 +640,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer.isInPictureInPicture()) { return false; } + // Always assume the TaskContainer if SplitAttributesCalculator is set + if (mSplitAttributesCalculator != null) { + return true; + } // Check if the parent container bounds can support any split rule. for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof SplitRule)) { @@ -2005,8 +2036,17 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** Whether the two rules have the same presentation. */ - private static boolean haveSamePresentation(@NonNull SplitPairRule rule1, + @VisibleForTesting + static boolean haveSamePresentation(@NonNull SplitPairRule rule1, @NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) { + if (rule1.getTag() != null || rule2.getTag() != null) { + // Tag must be unique if it is set. We don't want to reuse the container if the rules + // have different tags because they can have different SplitAttributes later through + // SplitAttributesCalculator. + return Objects.equals(rule1.getTag(), rule2.getTag()); + } + // If both rules don't have tag, compare all SplitRules' properties that may affect their + // SplitAttributes. // TODO(b/231655482): add util method to do the comparison in SplitPairRule. return rule1.getDefaultSplitAttributes().equals(rule2.getDefaultSplitAttributes()) && rule1.checkParentMetrics(parentWindowMetrics) 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 c9895239029ed..79603233ae141 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -46,6 +46,7 @@ import androidx.window.extensions.embedding.SplitAttributes.SplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType; import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType; +import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams; import androidx.window.extensions.embedding.TaskContainer.TaskProperties; import androidx.window.extensions.layout.DisplayFeature; import androidx.window.extensions.layout.FoldingFeature; @@ -126,7 +127,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { }) private @interface ResultCode {} - private static final SplitAttributes EXPAND_CONTAINERS_ATTRIBUTES = + @VisibleForTesting + static final SplitAttributes EXPAND_CONTAINERS_ATTRIBUTES = new SplitAttributes.Builder() .setSplitType(new ExpandContainersSplitType()) .build(); @@ -502,18 +504,29 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { return !(splitAttributes.getSplitType() instanceof ExpandContainersSplitType); } - // TODO: expand this method to apply SplitLayoutCalculator#computeSplitAttributesForState + @GuardedBy("mController.mLock") @NonNull SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties, @NonNull SplitRule rule, @Nullable Pair minDimensionsPair) { - final WindowMetrics taskWindowMetrics = getTaskWindowMetrics( - taskProperties.getConfiguration()); - final SplitAttributes splitAttributes; - if (rule.checkParentMetrics(taskWindowMetrics)) { - splitAttributes = rule.getDefaultSplitAttributes(); - } else { - splitAttributes = EXPAND_CONTAINERS_ATTRIBUTES; + final Configuration taskConfiguration = taskProperties.getConfiguration(); + final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration); + final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator(); + final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes(); + final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics); + if (calculator == null) { + if (!isDefaultMinSizeSatisfied) { + return EXPAND_CONTAINERS_ATTRIBUTES; + } + return sanitizeSplitAttributes(taskProperties, defaultSplitAttributes, + minDimensionsPair); } + final WindowLayoutInfo windowLayoutInfo = mController.mWindowLayoutComponent + .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(), + taskConfiguration.windowConfiguration); + final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams( + taskWindowMetrics, taskConfiguration, defaultSplitAttributes, + isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag()); + final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params); return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 3b52a3d0055cc..179696a063b12 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -28,9 +28,12 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_I import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY; import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTRIBUTES; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; @@ -76,6 +79,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.view.WindowInsets; +import android.view.WindowMetrics; import android.window.TaskFragmentInfo; import android.window.TaskFragmentOrganizer; import android.window.TaskFragmentParentInfo; @@ -1105,6 +1110,47 @@ public class SplitControllerTest { anyInt(), anyBoolean()); } + @Test + public void testHasSamePresentation() { + SplitPairRule splitRule1 = new SplitPairRule.Builder( + activityPair -> true, + activityIntentPair -> true, + windowMetrics -> true) + .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY) + .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) + .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) + .build(); + SplitPairRule splitRule2 = new SplitPairRule.Builder( + activityPair -> true, + activityIntentPair -> true, + windowMetrics -> true) + .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY) + .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) + .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) + .build(); + + assertTrue("Rules must have same presentation if tags are null and has same properties.", + SplitController.haveSamePresentation(splitRule1, splitRule2, + new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED))); + + splitRule2 = new SplitPairRule.Builder( + activityPair -> true, + activityIntentPair -> true, + windowMetrics -> true) + .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY) + .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) + .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) + .setTag(TEST_TAG) + .build(); + + assertFalse("Rules must have different presentations if tags are not equal regardless" + + "of other properties", + SplitController.haveSamePresentation(splitRule1, splitRule2, + new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED))); + + + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { final Activity activity = mock(Activity.class); 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 78ac91cc2e27d..6dae0a1086b3f 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 @@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.view.Display.DEFAULT_DISPLAY; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY; import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTRIBUTES; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; @@ -28,6 +30,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMock import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo; import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds; +import static androidx.window.extensions.embedding.SplitPresenter.EXPAND_CONTAINERS_ATTRIBUTES; import static androidx.window.extensions.embedding.SplitPresenter.POSITION_END; import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL; import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START; @@ -60,6 +63,7 @@ import android.content.res.Resources; import android.graphics.Rect; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.util.Pair; import android.util.Size; import android.window.TaskFragmentInfo; import android.window.WindowContainerTransaction; @@ -485,6 +489,49 @@ public class SplitPresenterTest { assertTrue(secondaryTf.isAbove(primaryTf)); } + @Test + public void testComputeSplitAttributes() { + final SplitPairRule splitPairRule = new SplitPairRule.Builder( + activityPair -> true, + activityIntentPair -> true, + windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS)) + .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY) + .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY) + .setDefaultSplitAttributes(SPLIT_ATTRIBUTES) + .build(); + final TaskContainer.TaskProperties taskProperties = getTaskProperty(); + + assertEquals(SPLIT_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties, + splitPairRule, null /* minDimensionsPair */)); + + final Pair minDimensionsPair = new Pair<>( + new Size(TASK_BOUNDS.width(), TASK_BOUNDS.height()), null); + + assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties, + splitPairRule, minDimensionsPair)); + + taskProperties.getConfiguration().windowConfiguration.setBounds(new Rect( + TASK_BOUNDS.left + 1, TASK_BOUNDS.top + 1, TASK_BOUNDS.right + 1, + TASK_BOUNDS.bottom + 1)); + + assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties, + splitPairRule, null /* minDimensionsPair */)); + + final SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType( + new SplitAttributes.SplitType.HingeSplitType( + SplitAttributes.SplitType.RatioSplitType.splitEqually() + ) + ).build(); + + mController.setSplitAttributesCalculator(params -> { + return splitAttributes; + }); + + assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties, + splitPairRule, null /* minDimensionsPair */)); + } + private Activity createMockActivity() { final Activity activity = mock(Activity.class); final Configuration activityConfig = new Configuration();