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

Commit d39583af authored by Hongwei Wang's avatar Hongwei Wang
Browse files

Start PiP dismiss from SysUI via TaskOrganizer

following ag/10570572, dismiss PiP is driven by SysUI as following
- SysUI issues WindowContainerTransaction and set the child windowing
mode at the beginning
- SysUI continues the animation
- SysUI issues WindowContainerTransaction and set the final windowing
mode at the end

This solves also the black background issue while exiting from PiP to
fullscreen mode.

Known issues
- app does not receive fullscreen configuration when transitioning from
PiP to fullscreen mode
- saving of the reentry bounds should be originated from SysUI rather
than WM going forward

Bug: 149947030
Bug: 151866274
Test: atest PinnedStackTests PipAnimationControllerTest
Change-Id: I7ed0d8b47dcc26653ebe2f3c08acab9e8b835db4
parent 7b1e562c
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -298,13 +298,6 @@ interface IActivityTaskManager {
    void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
            in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);

    /**
     * Dismisses PiP
     * @param animate True if the dismissal should be animated.
     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
     *                          default animation duration should be used.
     */
    void dismissPip(boolean animate, int animationDuration);
    void suppressResizeConfigChanges(boolean suppress);
    void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
    boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
+51 −11
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.systemui.pip;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
@@ -26,8 +29,6 @@ import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTI
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.window.ITaskOrganizerController;
import android.app.PictureInPictureParams;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -38,9 +39,9 @@ import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceControl;
import android.window.ITaskOrganizer;
import android.window.IWindowContainer;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;

@@ -216,6 +217,29 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        mOneShotAnimationType = animationType;
    }

    /**
     * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction}
     * - setActivityWindowingMode to fullscreen at beginning of the transaction. without changing
     *   the windowing mode of the Task itself. This makes sure the activity render it's fullscreen
     *   configuration while the Task is still in PiP.
     * - setWindowingMode to fullscreen at the end of transition
     * @param animationDurationMs duration in millisecond for the exiting PiP transition
     */
    public void dismissPip(int animationDurationMs) {
        try {
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            wct.setActivityWindowingMode(mToken, WINDOWING_MODE_FULLSCREEN);
            WindowOrganizer.applyTransaction(wct);
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to apply container transaction", e);
        }
        final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder());
        scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
                TRANSITION_DIRECTION_TO_FULLSCREEN, animationDurationMs,
                null /* updateBoundsCallback */);
        mInPip = false;
    }

    @Override
    public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
        Objects.requireNonNull(info, "Requires RunningTaskInfo");
@@ -235,7 +259,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        mBoundsToRestore.put(mToken.asBinder(), currentBounds);
        if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
            scheduleAnimateResizePip(currentBounds, destinationBounds,
                    TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null);
                    TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
                    null /* updateBoundsCallback */);
        } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
            mUpdateHandler.post(() -> mPipAnimationController
                    .getAnimator(mLeash, destinationBounds, 0f, 1f)
@@ -249,6 +274,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
    }

    /**
     * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}.
     * Meanwhile this callback is invoked whenever the task is removed. For instance:
     *   - as a result of removeStacksInWindowingModes from WM
     *   - activity itself is died
     */
    @Override
    public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
        IWindowContainer token = info.token;
@@ -259,7 +290,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
        final Rect boundsToRestore = mBoundsToRestore.remove(token.asBinder());
        scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
                TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null);
                TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration,
                null /* updateBoundsCallback */);
        mInPip = false;
    }

@@ -274,7 +306,8 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
                getAspectRatioOrDefault(newParams),
                null /* bounds */, getMinimalSize(info.topActivityInfo));
        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
        scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null);
        scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
                null /* updateBoundsCallback */);
    }

    /**
@@ -434,12 +467,19 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
        }
        mLastReportedBounds.set(destinationBounds);
        try {
            // If we are animating to fullscreen, then we need to reset the override bounds on the
            // task to ensure that the task "matches" the parent's bounds
            Rect taskBounds = direction == TRANSITION_DIRECTION_TO_FULLSCREEN
                    ? null
                    : destinationBounds;
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            final Rect taskBounds;
            if (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) {
                // If we are animating to fullscreen, then we need to reset the override bounds
                // on the task to ensure that the task "matches" the parent's bounds, this applies
                // also to the final windowing mode, which should be reset to undefined rather than
                // fullscreen.
                taskBounds = null;
                wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED)
                        .setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
            } else {
                taskBounds = destinationBounds;
            }
            if (direction == TRANSITION_DIRECTION_TO_PIP) {
                wct.scheduleFinishEnterPip(mToken, taskBounds);
            } else {
+1 −5
Original line number Diff line number Diff line
@@ -219,11 +219,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        cancelAnimations();
        mMenuController.hideMenuWithoutResize();
        mPipTaskOrganizer.getUpdateHandler().post(() -> {
            try {
                mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
            } catch (RemoteException e) {
                Log.e(TAG, "Error expanding PiP activity", e);
            }
            mPipTaskOrganizer.dismissPip(skipAnimation ? 0 : EXPAND_STACK_TO_FULLSCREEN_DURATION);
        });
    }

+1 −31
Original line number Diff line number Diff line
@@ -144,7 +144,6 @@ import android.app.IApplicationThread;
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
import android.app.IRequestFinishCallback;
import android.window.ITaskOrganizerController;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -230,9 +229,9 @@ import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import android.window.IWindowOrganizerController;
import android.window.WindowContainerTransaction;
import android.view.WindowManager;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
@@ -3979,35 +3978,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
        }
    }

    /**
     * Dismisses Pip
     * @param animate True if the dismissal should be animated.
     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
     *                          default animation duration should be used.
     */
    @Override
    public void dismissPip(boolean animate, int animationDuration) {
        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final ActivityStack stack =
                        mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
                if (stack == null) {
                    Slog.w(TAG, "dismissPip: pinned stack not found.");
                    return;
                }
                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
                    throw new IllegalArgumentException("Stack: " + stack
                            + " doesn't support animated resize.");
                }
                stack.dismissPip();
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    @Override
    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
+0 −1
Original line number Diff line number Diff line
@@ -1115,7 +1115,6 @@ public class RecentTasksTest extends ActivityTestsBase {
                () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
        assertSecurityException(expectCallable,
                () -> mService.setTaskWindowingModeSplitScreenPrimary(0, true));
        assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
        assertSecurityException(expectCallable,
                () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
        assertSecurityException(expectCallable, () -> mService.getAllStackInfos());