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

Commit add68184 authored by Chris Li's avatar Chris Li
Browse files

Register remote animation per Task

Before, we register remote animation per organizer, and assume it only
organizes in one Task. Now, we register per organizer Task if there is a
rule that supports split in the Task width.

Bug: 207720388
Test: atest WMJetpackUnitTests
Change-Id: Iac4e10af9aa0e7846c81b036b2cf708b6cbe627e
parent 9519e3c9
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -35,15 +35,15 @@ interface ITaskFragmentOrganizerController {
    /**
     * Registers remote animations per transition type for the organizer. It will override the
     * animations if the transition only contains windows that belong to the organized
     * TaskFragments.
     * TaskFragments in the given Task.
     */
    void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
    void registerRemoteAnimations(in ITaskFragmentOrganizer organizer, int taskId,
        in RemoteAnimationDefinition definition);

    /**
     * Unregisters remote animations per transition type for the organizer.
     */
    void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
    void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer, int taskId);

    /**
      * Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
+8 −5
Original line number Diff line number Diff line
@@ -94,13 +94,16 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
    /**
     * Registers remote animations per transition type for the organizer. It will override the
     * animations if the transition only contains windows that belong to the organized
     * TaskFragments.
     * TaskFragments in the given Task.
     *
     * @param taskId overrides if the transition only contains windows belonging to this Task.
     * @hide
     */
    @CallSuper
    public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
    public void registerRemoteAnimations(int taskId,
            @NonNull RemoteAnimationDefinition definition) {
        try {
            getController().registerRemoteAnimations(mInterface, definition);
            getController().registerRemoteAnimations(mInterface, taskId, definition);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
@@ -111,9 +114,9 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
     * @hide
     */
    @CallSuper
    public void unregisterRemoteAnimations() {
    public void unregisterRemoteAnimations(int taskId) {
        try {
            getController().unregisterRemoteAnimations(mInterface);
            getController().unregisterRemoteAnimations(mInterface, taskId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
+14 −7
Original line number Diff line number Diff line
@@ -35,6 +35,8 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Map;
import java.util.concurrent.Executor;

@@ -56,7 +58,8 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
    final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();

    private final TaskFragmentCallback mCallback;
    private TaskFragmentAnimationController mAnimationController;
    @VisibleForTesting
    TaskFragmentAnimationController mAnimationController;

    /**
     * Callback that notifies the controller about changes to task fragments.
@@ -80,21 +83,25 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {

    @Override
    public void unregisterOrganizer() {
        stopOverrideSplitAnimation();
        if (mAnimationController != null) {
            mAnimationController.unregisterAllRemoteAnimations();
            mAnimationController = null;
        }
        super.unregisterOrganizer();
    }

    void startOverrideSplitAnimation() {
    /** Overrides the animation if the transition is on the given Task. */
    void startOverrideSplitAnimation(int taskId) {
        if (mAnimationController == null) {
            mAnimationController = new TaskFragmentAnimationController(this);
        }
        mAnimationController.registerRemoteAnimations();
        mAnimationController.registerRemoteAnimations(taskId);
    }

    void stopOverrideSplitAnimation() {
    /** No longer overrides the animation if the transition is on the given Task. */
    void stopOverrideSplitAnimation(int taskId) {
        if (mAnimationController != null) {
            mAnimationController.unregisterRemoteAnimations();
            mAnimationController.unregisterRemoteAnimations(taskId);
        }
    }

+73 −41
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.ArraySet;
import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -75,10 +76,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    private Consumer<List<SplitInfo>> mEmbeddingCallback;
    private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();

    // We currently only support split activity embedding within the one root Task.
    // TODO(b/207720388): move to TaskContainer
    private final Rect mParentBounds = new Rect();

    public SplitController() {
        mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
        ActivityThread activityThread = ActivityThread.currentActivityThread();
@@ -95,7 +92,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
        mSplitRules.clear();
        mSplitRules.addAll(rules);
        updateAnimationOverride();
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            updateAnimationOverride(mTaskContainers.keyAt(i));
        }
    }

    @NonNull
@@ -163,38 +162,49 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen

    @Override
    public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
        TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
        if (container == null) {
            return;
        }

        final TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
        if (container != null) {
            // Cleanup if the TaskFragment vanished is not requested by the organizer.
            mPresenter.cleanupContainer(container, true /* shouldFinishDependent */);
            updateCallbackIfNecessary();
        }
        cleanupTaskFragment(taskFragmentInfo.getFragmentToken());
    }

    @Override
    public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
            @NonNull Configuration parentConfig) {
        onParentBoundsMayChange(parentConfig.windowConfiguration.getBounds());
        TaskFragmentContainer container = getContainer(fragmentToken);
        if (container != null) {
            onTaskBoundsMayChange(container.getTaskId(),
                    parentConfig.windowConfiguration.getBounds());
            mPresenter.updateContainer(container);
            updateCallbackIfNecessary();
        }
    }

    private void onParentBoundsMayChange(Activity activity) {
        if (activity.isFinishing()) {
    /** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */
    private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
            final TaskContainer taskContainer = mTaskContainers.valueAt(i);
            if (!taskContainer.mFinishedContainer.remove(taskFragmentToken)) {
                continue;
            }
            if (taskContainer.isEmpty()) {
                // Cleanup the TaskContainer if it becomes empty.
                mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
                mTaskContainers.remove(taskContainer.mTaskId);
            }
            return;
        }

        onParentBoundsMayChange(mPresenter.getParentContainerBounds(activity));
    }

    private void onParentBoundsMayChange(Rect parentBounds) {
        if (!parentBounds.isEmpty() && !mParentBounds.equals(parentBounds)) {
            mParentBounds.set(parentBounds);
            updateAnimationOverride();
    private void onTaskBoundsMayChange(int taskId, @NonNull Rect taskBounds) {
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (taskContainer != null && !taskBounds.isEmpty()
                && !taskContainer.mTaskBounds.equals(taskBounds)) {
            taskContainer.mTaskBounds.set(taskBounds);
            updateAnimationOverride(taskId);
        }
    }

@@ -202,9 +212,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
     * 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() {
        if (mParentBounds.isEmpty()) {
            // We don't know about the parent bounds yet.
    private void updateAnimationOverride(int taskId) {
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) {
            // We don't know about the Task bounds yet.
            return;
        }

@@ -214,7 +225,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            if (!(rule instanceof SplitRule)) {
                continue;
            }
            if (mPresenter.shouldShowSideBySide(mParentBounds, (SplitRule) rule)) {
            if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
                supportSplit = true;
                break;
            }
@@ -222,9 +233,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen

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

@@ -243,11 +254,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
        final TaskFragmentContainer currentContainer = getContainerWithActivity(
                launchedActivity.getActivityToken());

        if (currentContainer == null) {
            // Initial check before any TaskFragment is created.
            onParentBoundsMayChange(launchedActivity);
        }

        // Check if the activity is configured to always be expanded.
        if (shouldExpand(launchedActivity, null, splitRules)) {
            if (shouldContainerBeExpanded(currentContainer)) {
@@ -326,8 +332,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            // onTaskFragmentParentInfoChanged
            return;
        }
        // The bounds of the container may have been changed.
        onParentBoundsMayChange(activity);

        // Check if activity requires a placeholder
        launchPlaceholderIfNecessary(activity);
@@ -357,9 +361,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    TaskFragmentContainer newContainer(@Nullable Activity activity, int taskId) {
        final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskId);
        if (!mTaskContainers.contains(taskId)) {
            mTaskContainers.put(taskId, new TaskContainer());
            mTaskContainers.put(taskId, new TaskContainer(taskId));
        }
        final TaskContainer taskContainer = mTaskContainers.get(taskId);
        taskContainer.mContainers.add(container);
        if (activity != null && !taskContainer.isTaskBoundsInitialized()) {
            // Initial check before any TaskFragment has appeared.
            onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity));
        }
        mTaskContainers.get(taskId).mContainers.add(container);
        return container;
    }

@@ -391,11 +400,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
            return;
        }
        taskContainer.mContainers.remove(container);
        if (taskContainer.mContainers.isEmpty()) {
            mTaskContainers.remove(taskId);
            // No more TaskFragment in this Task, so no need to check split container.
            return;
        }
        // Marked as a pending removal which will be removed after it is actually removed on the
        // server side (#onTaskFragmentVanished).
        // In this way, we can keep track of the Task bounds until we no longer have any
        // TaskFragment there.
        taskContainer.mFinishedContainer.add(container.getTaskFragmentToken());

        final List<SplitContainer> containersToRemove = new ArrayList<>();
        for (SplitContainer splitContainer : taskContainer.mSplitContainers) {
@@ -1035,7 +1044,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    /** Represents TaskFragments and split pairs below a Task. */
    @VisibleForTesting
    static class TaskContainer {
        /** The unique task id. */
        final int mTaskId;
        /** Active TaskFragments in this Task. */
        final List<TaskFragmentContainer> mContainers = new ArrayList<>();
        /** Active split pairs in this Task. */
        final List<SplitContainer> mSplitContainers = new ArrayList<>();
        /**
         * TaskFragments that the organizer has requested to be closed. They should be removed when
         * the organizer receives {@link #onTaskFragmentVanished(TaskFragmentInfo)} event for them.
         */
        final Set<IBinder> mFinishedContainer = new ArraySet<>();
        /** Available window bounds of this Task. */
        final Rect mTaskBounds = new Rect();

        TaskContainer(int taskId) {
            mTaskId = taskId;
        }

        boolean isEmpty() {
            return mContainers.isEmpty() && mFinishedContainer.isEmpty();
        }

        boolean isTaskBoundsInitialized() {
            return !mTaskBounds.isEmpty();
        }
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -419,7 +419,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            }
        }

        // TODO(b/190433398): Check if the client-side available info about parent bounds is enough.
        return getTaskBoundsFromActivity(activity);
    }

    @NonNull
    static Rect getTaskBoundsFromActivity(@NonNull Activity activity) {
        if (!activity.isInMultiWindowMode()) {
            // In fullscreen mode the max bounds should correspond to the task bounds.
            return activity.getResources().getConfiguration().windowConfiguration.getMaxBounds();
Loading