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

Commit 4944e8e0 authored by Louis Chang's avatar Louis Chang
Browse files

[3/n] Pin ActivityStack

Implementing pinTopActivityStack API, which required in extensions
vendor API level 4.

This is the initial change to make the top-most TF container to
split with another TF container below.

Also adding a SplitPinContainer to hold the two TF containers.
The primary container is set to mutable which could be changed
and updated in runtime (in latter CLs).

The pinned TF container and SplitPinContainer is ensured to be on
top on the client side. The server side change will be done in the
following CLs.

Bug: 208573140
Test: atest SplitControllerTest
Change-Id: Ic932756f0adb4997b3729f0ed53dd9542dd4b7ae
parent b5485855
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions {
    // TODO(b/241126279) Introduce constants to better version functionality
    @Override
    public int getVendorApiLevel() {
        return 3;
        return 4;
    }

    @NonNull
+26 −1
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ import androidx.window.extensions.core.util.function.Function;
 */
class SplitContainer {
    @NonNull
    private final TaskFragmentContainer mPrimaryContainer;
    private TaskFragmentContainer mPrimaryContainer;
    @NonNull
    private final TaskFragmentContainer mSecondaryContainer;
    @NonNull
@@ -46,17 +46,35 @@ class SplitContainer {
    @NonNull
    private final IBinder mToken;

    /**
     * Whether the selection of which container is primary can be changed at runtime. Runtime
     * updates is currently possible only for {@link SplitPinContainer}
     *
     * @see SplitPinContainer
     */
    private final boolean mIsPrimaryContainerMutable;

    SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
            @NonNull Activity primaryActivity,
            @NonNull TaskFragmentContainer secondaryContainer,
            @NonNull SplitRule splitRule,
            @NonNull SplitAttributes splitAttributes) {
        this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes,
                false /* isPrimaryContainerMutable */);
    }

    SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
            @NonNull Activity primaryActivity,
            @NonNull TaskFragmentContainer secondaryContainer,
            @NonNull SplitRule splitRule,
            @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) {
        mPrimaryContainer = primaryContainer;
        mSecondaryContainer = secondaryContainer;
        mSplitRule = splitRule;
        mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
        mCurrentSplitAttributes = splitAttributes;
        mToken = new Binder("SplitContainer");
        mIsPrimaryContainerMutable = isPrimaryContainerMutable;

        if (shouldFinishPrimaryWithSecondary(splitRule)) {
            if (mPrimaryContainer.getRunningActivityCount() == 1
@@ -74,6 +92,13 @@ class SplitContainer {
        }
    }

    void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) {
        if (!mIsPrimaryContainerMutable) {
            throw new IllegalStateException("Cannot update primary TaskFragmentContainer");
        }
        mPrimaryContainer = primaryContainer;
    }

    @NonNull
    TaskFragmentContainer getPrimaryContainer() {
        return mPrimaryContainer;
+59 −5
Original line number Diff line number Diff line
@@ -212,6 +212,56 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
    }

    @Override
    public boolean pinTopActivityStack(int taskId, @NonNull SplitPinRule splitPinRule) {
        synchronized (mLock) {
            final TaskContainer task = getTaskContainer(taskId);
            if (task == null) {
                Log.e(TAG, "Cannot find the task for id: " + taskId);
                return false;
            }

            final TaskFragmentContainer topContainer =
                    task.getTopNonFinishingTaskFragmentContainer();
            // Cannot pin the TaskFragment if no other TaskFragment behind it.
            if (topContainer == null || task.indexOf(topContainer) <= 0) {
                Log.w(TAG, "Cannot find an ActivityStack to pin or split");
                return false;
            }
            // Abort if the top container is already pinned.
            if (task.getSplitPinContainer() != null) {
                Log.w(TAG, "There is already a pinned ActivityStack.");
                return false;
            }

            // Find a valid adjacent TaskFragmentContainer
            final TaskFragmentContainer primaryContainer =
                    task.getNonFinishingTaskFragmentContainerBelow(topContainer);
            if (primaryContainer == null) {
                Log.w(TAG, "Cannot find another ActivityStack to split");
                return false;
            }

            // Registers a Split
            final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer,
                    topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes());
            task.addSplitContainer(splitPinContainer);

            // Updates the Split
            final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
            final WindowContainerTransaction wct = transactionRecord.getTransaction();
            mPresenter.updateSplitContainer(splitPinContainer, wct);
            transactionRecord.apply(false /* shouldApplyIndependently */);
            updateCallbackIfNecessary();
            return true;
        }
    }

    @Override
    public void unpinTopActivityStack(int taskId){
        // TODO
    }

    @Override
    public void setSplitAttributesCalculator(
            @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) {
@@ -672,7 +722,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        if (targetContainer == null) {
            // When there is no embedding rule matched, try to place it in the top container
            // like a normal launch.
            targetContainer = taskContainer.getTopTaskFragmentContainer();
            targetContainer = taskContainer.getTopNonFinishingTaskFragmentContainer();
        }
        if (targetContainer == null) {
            return;
@@ -791,7 +841,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen

        final TaskFragmentContainer container = getContainerWithActivity(activity);
        if (!isOnReparent && container != null
                && container.getTaskContainer().getTopTaskFragmentContainer() != container) {
                && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer()
                        != container) {
            // Do not resolve if the launched activity is not the top-most container in the Task.
            return true;
        }
@@ -888,7 +939,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        if (taskContainer == null) {
            return;
        }
        final TaskFragmentContainer targetContainer = taskContainer.getTopTaskFragmentContainer();
        final TaskFragmentContainer targetContainer =
                taskContainer.getTopNonFinishingTaskFragmentContainer();
        if (targetContainer == null) {
            return;
        }
@@ -1213,11 +1265,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen

        // 3. Whether the top activity (if any) should be split with the new activity intent.
        final TaskContainer taskContainer = getTaskContainer(taskId);
        if (taskContainer == null || taskContainer.getTopTaskFragmentContainer() == null) {
        if (taskContainer == null
                || taskContainer.getTopNonFinishingTaskFragmentContainer() == null) {
            // There is no other activity in the Task to check split with.
            return null;
        }
        final TaskFragmentContainer topContainer = taskContainer.getTopTaskFragmentContainer();
        final TaskFragmentContainer topContainer =
                taskContainer.getTopNonFinishingTaskFragmentContainer();
        final Activity topActivity = topContainer.getTopNonFinishingActivity();
        if (topActivity != null && topActivity != launchingActivity) {
            final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct,
+47 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 androidx.annotation.NonNull;

/**
 * Client-side descriptor of a split that holds two containers while the secondary
 * container is pinned on top of the Task and the primary container is the container that is
 * currently below the secondary container. The primary container could be updated to
 * another container whenever the existing primary container is removed or no longer
 * be the container that's right behind the secondary container.
 */
class SplitPinContainer extends SplitContainer {

    SplitPinContainer(@NonNull TaskFragmentContainer primaryContainer,
            @NonNull TaskFragmentContainer secondaryContainer,
            @NonNull SplitPinRule splitPinRule,
            @NonNull SplitAttributes splitAttributes) {
        super(primaryContainer, primaryContainer.getTopNonFinishingActivity(), secondaryContainer,
                splitPinRule, splitAttributes, true /* isPrimaryContainerMutable */);
    }

    @Override
    public String toString() {
        return "SplitPinContainer{"
                + " primaryContainer=" + getPrimaryContainer()
                + " secondaryContainer=" + getSecondaryContainer()
                + " splitPinRule=" + getSplitRule()
                + " splitAttributes" + getCurrentSplitAttributes()
                + "}";
    }
}
+0 −4
Original line number Diff line number Diff line
@@ -336,10 +336,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        // value.
        final SplitRule rule = splitContainer.getSplitRule();
        final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer();
        final Activity activity = primaryContainer.getTopNonFinishingActivity();
        if (activity == null) {
            return;
        }
        final TaskContainer taskContainer = splitContainer.getTaskContainer();
        final TaskProperties taskProperties = taskContainer.getTaskProperties();
        final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
Loading