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

Commit 8ebb15f5 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

parents d26a311a a6b968f8
Loading
Loading
Loading
Loading
+134 −23
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

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.getFinishSecondaryWithPrimaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule;
@@ -93,7 +95,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        mSplitRules.clear();
        mSplitRules.addAll(rules);
        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;
        }

        final boolean wasInPip = isInPictureInPicture(container);
        container.setInfo(taskFragmentInfo);
        final boolean isInPip = isInPictureInPicture(container);
        // Check if there are no running activities - consider the container empty if there are no
        // non-finishing activities left.
        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
            // activity in the Task.
            final boolean shouldFinishDependent =
                    !taskFragmentInfo.isTaskClearedForReuse();
            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();
    }
@@ -174,10 +192,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @Override
    public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
            @NonNull Configuration parentConfig) {
        TaskFragmentContainer container = getContainer(fragmentToken);
        final TaskFragmentContainer container = getContainer(fragmentToken);
        if (container != null) {
            onTaskBoundsMayChange(container.getTaskId(),
                    parentConfig.windowConfiguration.getBounds());
            onTaskConfigurationChanged(container.getTaskId(), parentConfig);
            if (isInPictureInPicture(parentConfig)) {
                // No need to update presentation in PIP until the Task exit PIP.
                return;
            }
            mPresenter.updateContainer(container);
            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);
        if (taskContainer != null && !taskBounds.isEmpty()
                && !taskContainer.mTaskBounds.equals(taskBounds)) {
        if (taskContainer == null) {
            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);
            updateAnimationOverride(taskId);
            return true;
        }
        return false;
    }

    /**
     * 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.
     */
    private void updateAnimationOverride(int taskId) {
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) {
    private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
        if (!taskContainer.isTaskBoundsInitialized()) {
            // We don't know about the Task bounds yet.
            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.
        boolean supportSplit = false;
        for (EmbeddingRule rule : mSplitRules) {
            if (!(rule instanceof SplitRule)) {
                continue;
            }
            if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
                supportSplit = true;
                break;
                return true;
            }
        }

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

    void onActivityCreated(@NonNull Activity launchedActivity) {
@@ -250,6 +297,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     */
    // TODO(b/190433398): Break down into smaller functions.
    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 TaskFragmentContainer currentContainer = getContainerWithActivity(
                launchedActivity.getActivityToken());
@@ -324,6 +375,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    }

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

@@ -365,9 +420,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        }
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        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.
            onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity));
            updateAnimationOverride(taskContainer);
        }
        return container;
    }
@@ -389,6 +446,40 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        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.
     */
@@ -916,6 +1007,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
                return super.onStartActivity(who, intent, options);
            }
            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())) {
                setLaunchingInExpandedContainer(launchingActivity, options);
@@ -1079,6 +1174,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        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. */
    @VisibleForTesting
    static class TaskContainer {
@@ -1095,6 +1203,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        final Set<IBinder> mFinishedContainer = new ArraySet<>();
        /** Available window bounds of this Task. */
        final Rect mTaskBounds = new Rect();
        /** Configuration of the Task. */
        @Nullable
        Configuration mConfiguration;

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

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

/**
@@ -65,12 +66,26 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
    /**
     * Updates the presentation of the provided container.
     */
    void updateContainer(TaskFragmentContainer container) {
    void updateContainer(@NonNull TaskFragmentContainer container) {
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        mController.updateContainer(wct, container);
        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
     * transaction.
+20 −0
Original line number Diff line number Diff line
@@ -191,6 +191,13 @@ class TaskFragmentContainer {
        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.
     */
@@ -198,6 +205,19 @@ class TaskFragmentContainer {
        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
     * configured to finish together.