Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4f6fc192 authored by Charles Chen's avatar Charles Chen
Browse files

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
parent eaeeaa56
Loading
Loading
Loading
Loading
+43 −3
Original line number Original line Diff line number Diff line
@@ -103,6 +103,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    // Currently applied split configuration.
    // Currently applied split configuration.
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
    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
     * Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
     * below it.
     * below it.
@@ -188,12 +205,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen


    @Override
    @Override
    public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
    public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
        // TODO: Implement this method
        synchronized (mLock) {
            mSplitAttributesCalculator = calculator;
        }
    }
    }


    @Override
    @Override
    public void clearSplitAttributesCalculator() {
    public void clearSplitAttributesCalculator() {
        // TODO: Implement this method
        synchronized (mLock) {
            mSplitAttributesCalculator = null;
        }
    }

    @GuardedBy("mLock")
    @Nullable
    SplitAttributesCalculator getSplitAttributesCalculator() {
        return mSplitAttributesCalculator;
    }
    }


    @NonNull
    @NonNull
@@ -613,6 +640,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        if (taskContainer.isInPictureInPicture()) {
        if (taskContainer.isInPictureInPicture()) {
            return false;
            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.
        // Check if the parent container bounds can support any split rule.
        for (EmbeddingRule rule : mSplitRules) {
        for (EmbeddingRule rule : mSplitRules) {
            if (!(rule instanceof SplitRule)) {
            if (!(rule instanceof SplitRule)) {
@@ -2005,8 +2036,17 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    }
    }


    /** Whether the two rules have the same presentation. */
    /** 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) {
            @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.
        // TODO(b/231655482): add util method to do the comparison in SplitPairRule.
        return rule1.getDefaultSplitAttributes().equals(rule2.getDefaultSplitAttributes())
        return rule1.getDefaultSplitAttributes().equals(rule2.getDefaultSplitAttributes())
                && rule1.checkParentMetrics(parentWindowMetrics)
                && rule1.checkParentMetrics(parentWindowMetrics)
+23 −10
Original line number Original line Diff line number Diff line
@@ -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.ExpandContainersSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
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.embedding.TaskContainer.TaskProperties;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
import androidx.window.extensions.layout.FoldingFeature;
@@ -126,7 +127,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    })
    })
    private @interface ResultCode {}
    private @interface ResultCode {}


    private static final SplitAttributes EXPAND_CONTAINERS_ATTRIBUTES =
    @VisibleForTesting
    static final SplitAttributes EXPAND_CONTAINERS_ATTRIBUTES =
            new SplitAttributes.Builder()
            new SplitAttributes.Builder()
            .setSplitType(new ExpandContainersSplitType())
            .setSplitType(new ExpandContainersSplitType())
            .build();
            .build();
@@ -502,18 +504,29 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        return !(splitAttributes.getSplitType() instanceof ExpandContainersSplitType);
        return !(splitAttributes.getSplitType() instanceof ExpandContainersSplitType);
    }
    }


    // TODO: expand this method to apply SplitLayoutCalculator#computeSplitAttributesForState
    @GuardedBy("mController.mLock")
    @NonNull
    @NonNull
    SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
    SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
            @NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
            @NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
        final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(
        final Configuration taskConfiguration = taskProperties.getConfiguration();
                taskProperties.getConfiguration());
        final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
        final SplitAttributes splitAttributes;
        final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator();
        if (rule.checkParentMetrics(taskWindowMetrics)) {
        final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
            splitAttributes = rule.getDefaultSplitAttributes();
        final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics);
        } else {
        if (calculator == null) {
            splitAttributes = EXPAND_CONTAINERS_ATTRIBUTES;
            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);
        return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair);
    }
    }


+46 −0
Original line number Original line Diff line number Diff line
@@ -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.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
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.SPLIT_ATTRIBUTES;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
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.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.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
@@ -76,6 +79,8 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
import android.view.WindowInsets;
import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentParentInfo;
@@ -1105,6 +1110,47 @@ public class SplitControllerTest {
                anyInt(), anyBoolean());
                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. */
    /** Creates a mock activity in the organizer process. */
    private Activity createMockActivity() {
    private Activity createMockActivity() {
        final Activity activity = mock(Activity.class);
        final Activity activity = mock(Activity.class);
+47 −0
Original line number Original line Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.DEFAULT_DISPLAY;
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.SPLIT_ATTRIBUTES;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
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.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.createSplitRule;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
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_END;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START;
@@ -60,6 +63,7 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
import android.util.Size;
import android.util.Size;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction;
@@ -485,6 +489,49 @@ public class SplitPresenterTest {
        assertTrue(secondaryTf.isAbove(primaryTf));
        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() {
    private Activity createMockActivity() {
        final Activity activity = mock(Activity.class);
        final Activity activity = mock(Activity.class);
        final Configuration activityConfig = new Configuration();
        final Configuration activityConfig = new Configuration();