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

Commit 148fdaa8 authored by Chris Li's avatar Chris Li Committed by Automerger Merge Worker
Browse files

Merge "No embed activity when Task is in PIP" into tm-dev am: a6b968f8 am: 8ebb15f5

parents 2a5e9d9c 8ebb15f5
Loading
Loading
Loading
Loading
+134 −23
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package androidx.window.extensions.embedding;
package androidx.window.extensions.embedding;


import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;

import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
@@ -93,7 +95,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        mSplitRules.clear();
        mSplitRules.clear();
        mSplitRules.addAll(rules);
        mSplitRules.addAll(rules);
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            updateAnimationOverride(mTaskContainers.keyAt(i));
            updateAnimationOverride(mTaskContainers.valueAt(i));
        }
        }
    }
    }


@@ -147,15 +149,31 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            return;
            return;
        }
        }


        final boolean wasInPip = isInPictureInPicture(container);
        container.setInfo(taskFragmentInfo);
        container.setInfo(taskFragmentInfo);
        final boolean isInPip = isInPictureInPicture(container);
        // Check if there are no running activities - consider the container empty if there are no
        // Check if there are no running activities - consider the container empty if there are no
        // non-finishing activities left.
        // non-finishing activities left.
        if (!taskFragmentInfo.hasRunningActivity()) {
        if (!taskFragmentInfo.hasRunningActivity()) {
            // TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP
            // Task.
            // Do not finish the dependents if this TaskFragment was cleared due to launching
            // Do not finish the dependents if this TaskFragment was cleared due to launching
            // activity in the Task.
            // activity in the Task.
            final boolean shouldFinishDependent =
            final boolean shouldFinishDependent =
                    !taskFragmentInfo.isTaskClearedForReuse();
                    !taskFragmentInfo.isTaskClearedForReuse();
            mPresenter.cleanupContainer(container, shouldFinishDependent);
            mPresenter.cleanupContainer(container, shouldFinishDependent);
        } else if (wasInPip && isInPip) {
            // No update until exit PIP.
            return;
        } else if (isInPip) {
            // Enter PIP.
            // All overrides will be cleanup.
            container.setLastRequestedBounds(null /* bounds */);
            cleanupForEnterPip(container);
        } else if (wasInPip) {
            // Exit PIP.
            // Updates the presentation of the container. Expand or launch placeholder if needed.
            mPresenter.updateContainer(container);
        }
        }
        updateCallbackIfNecessary();
        updateCallbackIfNecessary();
    }
    }
@@ -174,10 +192,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @Override
    @Override
    public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
    public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
            @NonNull Configuration parentConfig) {
            @NonNull Configuration parentConfig) {
        TaskFragmentContainer container = getContainer(fragmentToken);
        final TaskFragmentContainer container = getContainer(fragmentToken);
        if (container != null) {
        if (container != null) {
            onTaskBoundsMayChange(container.getTaskId(),
            onTaskConfigurationChanged(container.getTaskId(), parentConfig);
                    parentConfig.windowConfiguration.getBounds());
            if (isInPictureInPicture(parentConfig)) {
                // No need to update presentation in PIP until the Task exit PIP.
                return;
            }
            mPresenter.updateContainer(container);
            mPresenter.updateContainer(container);
            updateCallbackIfNecessary();
            updateCallbackIfNecessary();
        }
        }
@@ -199,44 +220,70 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
        }
    }
    }


    private void onTaskBoundsMayChange(int taskId, @NonNull Rect taskBounds) {
    private void onTaskConfigurationChanged(int taskId, @NonNull Configuration config) {
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (taskContainer != null && !taskBounds.isEmpty()
        if (taskContainer == null) {
                && !taskContainer.mTaskBounds.equals(taskBounds)) {
            return;
        }
        final boolean wasInPip = isInPictureInPicture(taskContainer.mConfiguration);
        final boolean isInPIp = isInPictureInPicture(config);
        taskContainer.mConfiguration = config;

        // We need to check the animation override when enter/exit PIP or has bounds changed.
        boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
        if (onTaskBoundsMayChange(taskContainer, config.windowConfiguration.getBounds())
                && !isInPIp) {
            // We don't care the bounds change when it has already entered PIP.
            shouldUpdateAnimationOverride = true;
        }
        if (shouldUpdateAnimationOverride) {
            updateAnimationOverride(taskContainer);
        }
    }

    /** Returns {@code true} if the bounds is changed. */
    private boolean onTaskBoundsMayChange(@NonNull TaskContainer taskContainer,
            @NonNull Rect taskBounds) {
        if (!taskBounds.isEmpty() && !taskContainer.mTaskBounds.equals(taskBounds)) {
            taskContainer.mTaskBounds.set(taskBounds);
            taskContainer.mTaskBounds.set(taskBounds);
            updateAnimationOverride(taskId);
            return true;
        }
        }
        return false;
    }
    }


    /**
    /**
     * Updates if we should override transition animation. We only want to override if the Task
     * Updates if we should override transition animation. We only want to override if the Task
     * bounds is large enough for at least one split rule.
     * bounds is large enough for at least one split rule.
     */
     */
    private void updateAnimationOverride(int taskId) {
    private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (!taskContainer.isTaskBoundsInitialized()) {
        if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) {
            // We don't know about the Task bounds yet.
            // We don't know about the Task bounds yet.
            return;
            return;
        }
        }


        // We only want to override if it supports split.
        if (supportSplit(taskContainer)) {
            mPresenter.startOverrideSplitAnimation(taskContainer.mTaskId);
        } else {
            mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
        }
    }

    private boolean supportSplit(@NonNull TaskContainer taskContainer) {
        // No split inside PIP.
        if (isInPictureInPicture(taskContainer.mConfiguration)) {
            return false;
        }
        // Check if the parent container bounds can support any split rule.
        // Check if the parent container bounds can support any split rule.
        boolean supportSplit = false;
        for (EmbeddingRule rule : mSplitRules) {
        for (EmbeddingRule rule : mSplitRules) {
            if (!(rule instanceof SplitRule)) {
            if (!(rule instanceof SplitRule)) {
                continue;
                continue;
            }
            }
            if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
            if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
                supportSplit = true;
                return true;
                break;
            }
            }
        }
        }

        return false;
        // We only want to override if it supports split.
        if (supportSplit) {
            mPresenter.startOverrideSplitAnimation(taskId);
        } else {
            mPresenter.stopOverrideSplitAnimation(taskId);
        }
    }
    }


    void onActivityCreated(@NonNull Activity launchedActivity) {
    void onActivityCreated(@NonNull Activity launchedActivity) {
@@ -250,6 +297,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     */
     */
    // TODO(b/190433398): Break down into smaller functions.
    // TODO(b/190433398): Break down into smaller functions.
    void handleActivityCreated(@NonNull Activity launchedActivity) {
    void handleActivityCreated(@NonNull Activity launchedActivity) {
        if (isInPictureInPicture(launchedActivity)) {
            // We don't embed activity when it is in PIP.
            return;
        }
        final List<EmbeddingRule> splitRules = getSplitRules();
        final List<EmbeddingRule> splitRules = getSplitRules();
        final TaskFragmentContainer currentContainer = getContainerWithActivity(
        final TaskFragmentContainer currentContainer = getContainerWithActivity(
                launchedActivity.getActivityToken());
                launchedActivity.getActivityToken());
@@ -324,6 +375,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    }
    }


    private void onActivityConfigurationChanged(@NonNull Activity activity) {
    private void onActivityConfigurationChanged(@NonNull Activity activity) {
        if (isInPictureInPicture(activity)) {
            // We don't embed activity when it is in PIP.
            return;
        }
        final TaskFragmentContainer currentContainer = getContainerWithActivity(
        final TaskFragmentContainer currentContainer = getContainerWithActivity(
                activity.getActivityToken());
                activity.getActivityToken());


@@ -365,9 +420,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
        }
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        taskContainer.mContainers.add(container);
        taskContainer.mContainers.add(container);
        if (activity != null && !taskContainer.isTaskBoundsInitialized()) {
        if (activity != null && !taskContainer.isTaskBoundsInitialized()
                && onTaskBoundsMayChange(taskContainer,
                SplitPresenter.getTaskBoundsFromActivity(activity))) {
            // Initial check before any TaskFragment has appeared.
            // Initial check before any TaskFragment has appeared.
            onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity));
            updateAnimationOverride(taskContainer);
        }
        }
        return container;
        return container;
    }
    }
@@ -389,6 +446,40 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer);
        mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer);
    }
    }


    /** Cleanups all the dependencies when the TaskFragment is entering PIP. */
    private void cleanupForEnterPip(@NonNull TaskFragmentContainer container) {
        final int taskId = container.getTaskId();
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (taskContainer == null) {
            return;
        }
        final List<SplitContainer> splitsToRemove = new ArrayList<>();
        final Set<TaskFragmentContainer> containersToUpdate = new ArraySet<>();
        for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
            if (splitContainer.getPrimaryContainer() != container
                    && splitContainer.getSecondaryContainer() != container) {
                continue;
            }
            splitsToRemove.add(splitContainer);
            final TaskFragmentContainer splitTf = splitContainer.getPrimaryContainer() == container
                    ? splitContainer.getSecondaryContainer()
                    : splitContainer.getPrimaryContainer();
            containersToUpdate.add(splitTf);
            // We don't want the PIP TaskFragment to be removed as a result of any of its dependents
            // being removed.
            splitTf.removeContainerToFinishOnExit(container);
            if (container.getTopNonFinishingActivity() != null) {
                splitTf.removeActivityToFinishOnExit(container.getTopNonFinishingActivity());
            }
        }
        container.resetDependencies();
        taskContainer.mSplitContainers.removeAll(splitsToRemove);
        // If there is any TaskFragment split with the PIP TaskFragment, update their presentations
        // since the split is dismissed.
        // We don't want to close any of them even if they are dependencies of the PIP TaskFragment.
        mPresenter.updateContainers(containersToUpdate);
    }

    /**
    /**
     * Removes the container from bookkeeping records.
     * Removes the container from bookkeeping records.
     */
     */
@@ -916,6 +1007,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                return super.onStartActivity(who, intent, options);
                return super.onStartActivity(who, intent, options);
            }
            }
            final Activity launchingActivity = (Activity) who;
            final Activity launchingActivity = (Activity) who;
            if (isInPictureInPicture(launchingActivity)) {
                // We don't embed activity when it is in PIP.
                return super.onStartActivity(who, intent, options);
            }


            if (shouldExpand(null, intent, getSplitRules())) {
            if (shouldExpand(null, intent, getSplitRules())) {
                setLaunchingInExpandedContainer(launchingActivity, options);
                setLaunchingInExpandedContainer(launchingActivity, options);
@@ -1079,6 +1174,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        return !pairRule.shouldClearTop();
        return !pairRule.shouldClearTop();
    }
    }


    private static boolean isInPictureInPicture(@NonNull Activity activity) {
        return isInPictureInPicture(activity.getResources().getConfiguration());
    }

    private static boolean isInPictureInPicture(@NonNull TaskFragmentContainer tf) {
        return isInPictureInPicture(tf.getInfo().getConfiguration());
    }

    private static boolean isInPictureInPicture(@Nullable Configuration configuration) {
        return configuration != null
                && configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
    }

    /** Represents TaskFragments and split pairs below a Task. */
    /** Represents TaskFragments and split pairs below a Task. */
    @VisibleForTesting
    @VisibleForTesting
    static class TaskContainer {
    static class TaskContainer {
@@ -1095,6 +1203,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        final Set<IBinder> mFinishedContainer = new ArraySet<>();
        final Set<IBinder> mFinishedContainer = new ArraySet<>();
        /** Available window bounds of this Task. */
        /** Available window bounds of this Task. */
        final Rect mTaskBounds = new Rect();
        final Rect mTaskBounds = new Rect();
        /** Configuration of the Task. */
        @Nullable
        Configuration mConfiguration;


        TaskContainer(int taskId) {
        TaskContainer(int taskId) {
            mTaskId = taskId;
            mTaskId = taskId;
+16 −1
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Nullable;


import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;


/**
/**
@@ -65,12 +66,26 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    /**
    /**
     * Updates the presentation of the provided container.
     * Updates the presentation of the provided container.
     */
     */
    void updateContainer(TaskFragmentContainer container) {
    void updateContainer(@NonNull TaskFragmentContainer container) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        mController.updateContainer(wct, container);
        mController.updateContainer(wct, container);
        applyTransaction(wct);
        applyTransaction(wct);
    }
    }


    /**
     * Updates the presentation of the provided containers.
     */
    void updateContainers(@NonNull Collection<TaskFragmentContainer> containers) {
        if (containers.isEmpty()) {
            return;
        }
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        for (TaskFragmentContainer container : containers) {
            mController.updateContainer(wct, container);
        }
        applyTransaction(wct);
    }

    /**
    /**
     * Deletes the specified container and all other associated and dependent containers in the same
     * Deletes the specified container and all other associated and dependent containers in the same
     * transaction.
     * transaction.
+20 −0
Original line number Original line Diff line number Diff line
@@ -191,6 +191,13 @@ class TaskFragmentContainer {
        mContainersToFinishOnExit.add(containerToFinish);
        mContainersToFinishOnExit.add(containerToFinish);
    }
    }


    /**
     * Removes a container that should be finished when this container is finished.
     */
    void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
        mContainersToFinishOnExit.remove(containerToRemove);
    }

    /**
    /**
     * Adds an activity that should be finished when this container is finished.
     * Adds an activity that should be finished when this container is finished.
     */
     */
@@ -198,6 +205,19 @@ class TaskFragmentContainer {
        mActivitiesToFinishOnExit.add(activityToFinish);
        mActivitiesToFinishOnExit.add(activityToFinish);
    }
    }


    /**
     * Removes an activity that should be finished when this container is finished.
     */
    void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
        mActivitiesToFinishOnExit.remove(activityToRemove);
    }

    /** Removes all dependencies that should be finished when this container is finished. */
    void resetDependencies() {
        mContainersToFinishOnExit.clear();
        mActivitiesToFinishOnExit.clear();
    }

    /**
    /**
     * Removes all activities that belong to this process and finishes other containers/activities
     * Removes all activities that belong to this process and finishes other containers/activities
     * configured to finish together.
     * configured to finish together.