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
This commit is contained in:
Charles Chen
2022-08-08 16:06:15 +08:00
parent eaeeaa5608
commit 4f6fc19215
4 changed files with 158 additions and 12 deletions

View File

@@ -103,6 +103,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Currently applied split configuration.
@GuardedBy("mLock")
private final List<EmbeddingRule> 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:
* <ul>
* <li>{@link SplitPresenter#updateSplitContainer(SplitContainer, TaskFragmentContainer,
* WindowContainerTransaction)}</li>
* <li>There's a started Activity which matches {@link SplitPairRule} </li>
* <li>Checking whether the place holder should be launched if there's a Activity matches
* {@link SplitPlaceholderRule} </li>
* </ul>
*/
@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)

View File

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

View File

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

View File

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