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

Commit d338520c authored by Robert Horvath's avatar Robert Horvath Committed by Android (Google) Code Review
Browse files

Merge changes I49d32c18,I8e8736fc into tm-dev

* changes:
  Apply temporary PiP decor separately from overall placement
  Debounce TV PiP movements
parents 7a662cfb 86c660c7
Loading
Loading
Loading
Loading
+21 −4
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.dagger;


import android.content.Context;
import android.content.Context;
import android.os.Handler;
import android.os.Handler;
import android.os.SystemClock;


import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -39,6 +40,7 @@ import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipUiEventLogger;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsController;
import com.android.wm.shell.pip.tv.TvPipBoundsState;
import com.android.wm.shell.pip.tv.TvPipBoundsState;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipMenuController;
@@ -64,6 +66,7 @@ public abstract class TvPipModule {
            Context context,
            Context context,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
            TvPipBoundsController tvPipBoundsController,
            PipAppOpsListener pipAppOpsListener,
            PipAppOpsListener pipAppOpsListener,
            PipTaskOrganizer pipTaskOrganizer,
            PipTaskOrganizer pipTaskOrganizer,
            TvPipMenuController tvPipMenuController,
            TvPipMenuController tvPipMenuController,
@@ -74,13 +77,13 @@ public abstract class TvPipModule {
            PipParamsChangedForwarder pipParamsChangedForwarder,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            DisplayController displayController,
            DisplayController displayController,
            WindowManagerShellWrapper windowManagerShellWrapper,
            WindowManagerShellWrapper windowManagerShellWrapper,
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellMainThread ShellExecutor mainExecutor) {
            @ShellMainThread Handler mainHandler) {
        return Optional.of(
        return Optional.of(
                TvPipController.create(
                TvPipController.create(
                        context,
                        context,
                        tvPipBoundsState,
                        tvPipBoundsState,
                        tvPipBoundsAlgorithm,
                        tvPipBoundsAlgorithm,
                        tvPipBoundsController,
                        pipAppOpsListener,
                        pipAppOpsListener,
                        pipTaskOrganizer,
                        pipTaskOrganizer,
                        pipTransitionController,
                        pipTransitionController,
@@ -91,8 +94,22 @@ public abstract class TvPipModule {
                        pipParamsChangedForwarder,
                        pipParamsChangedForwarder,
                        displayController,
                        displayController,
                        windowManagerShellWrapper,
                        windowManagerShellWrapper,
                        mainExecutor,
                        mainExecutor));
                        mainHandler));
    }

    @WMSingleton
    @Provides
    static TvPipBoundsController provideTvPipBoundsController(
            Context context,
            @ShellMainThread Handler mainHandler,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm) {
        return new TvPipBoundsController(
                context,
                SystemClock::uptimeMillis,
                mainHandler,
                tvPipBoundsState,
                tvPipBoundsAlgorithm);
    }
    }


    @WMSingleton
    @WMSingleton
+18 −12
Original line number Original line Diff line number Diff line
@@ -29,7 +29,6 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.Size;
import android.util.Size;
import android.view.Gravity;
import android.view.Gravity;
@@ -66,7 +65,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
            @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
            @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
        super(context, tvPipBoundsState, pipSnapAlgorithm);
        super(context, tvPipBoundsState, pipSnapAlgorithm);
        this.mTvPipBoundsState = tvPipBoundsState;
        this.mTvPipBoundsState = tvPipBoundsState;
        this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm(SystemClock::uptimeMillis);
        this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
        reloadResources(context);
        reloadResources(context);
    }
    }


@@ -80,7 +79,6 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
                res.getDimensionPixelSize(R.dimen.pip_keep_clear_area_padding));
                res.getDimensionPixelSize(R.dimen.pip_keep_clear_area_padding));
        mKeepClearAlgorithm.setMaxRestrictedDistanceFraction(
        mKeepClearAlgorithm.setMaxRestrictedDistanceFraction(
                res.getFraction(R.fraction.config_pipMaxRestrictedMoveDistance, 1, 1));
                res.getFraction(R.fraction.config_pipMaxRestrictedMoveDistance, 1, 1));
        mKeepClearAlgorithm.setStashDuration(res.getInteger(R.integer.config_pipStashDuration));
    }
    }


    @Override
    @Override
@@ -104,7 +102,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
            updateGravityOnExpandToggled(Gravity.NO_GRAVITY, true);
            updateGravityOnExpandToggled(Gravity.NO_GRAVITY, true);
        }
        }
        mTvPipBoundsState.setTvPipExpanded(isPipExpanded);
        mTvPipBoundsState.setTvPipExpanded(isPipExpanded);
        return getTvPipBounds().getBounds();
        return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds());
    }
    }


    /** Returns the current bounds adjusted to the new aspect ratio, if valid. */
    /** Returns the current bounds adjusted to the new aspect ratio, if valid. */
@@ -114,13 +112,27 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio);
                    "%s: getAdjustedDestinationBounds: %f", TAG, newAspectRatio);
        }
        }
        return getTvPipBounds().getBounds();
        return adjustBoundsForTemporaryDecor(getTvPipPlacement().getBounds());
    }

    Rect adjustBoundsForTemporaryDecor(Rect bounds) {
        Rect boundsWithDecor = new Rect(bounds);
        Insets decorInset = mTvPipBoundsState.getPipMenuTemporaryDecorInsets();
        Insets pipDecorReverseInsets = Insets.subtract(Insets.NONE, decorInset);
        boundsWithDecor.inset(decorInset);
        Gravity.apply(mTvPipBoundsState.getTvPipGravity(),
                boundsWithDecor.width(), boundsWithDecor.height(), bounds, boundsWithDecor);

        // remove temporary decoration again
        boundsWithDecor.inset(pipDecorReverseInsets);
        return boundsWithDecor;
    }
    }


    /**
    /**
     * Calculates the PiP bounds.
     * Calculates the PiP bounds.
     */
     */
    public Placement getTvPipBounds() {
    @NonNull
    public Placement getTvPipPlacement() {
        final Size pipSize = getPipSize();
        final Size pipSize = getPipSize();
        final Rect displayBounds = mTvPipBoundsState.getDisplayBounds();
        final Rect displayBounds = mTvPipBoundsState.getDisplayBounds();
        final Size screenSize = new Size(displayBounds.width(), displayBounds.height());
        final Size screenSize = new Size(displayBounds.width(), displayBounds.height());
@@ -153,8 +165,6 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
        mKeepClearAlgorithm.setStashOffset(mTvPipBoundsState.getStashOffset());
        mKeepClearAlgorithm.setStashOffset(mTvPipBoundsState.getStashOffset());
        mKeepClearAlgorithm.setPipPermanentDecorInsets(
        mKeepClearAlgorithm.setPipPermanentDecorInsets(
                mTvPipBoundsState.getPipMenuPermanentDecorInsets());
                mTvPipBoundsState.getPipMenuPermanentDecorInsets());
        mKeepClearAlgorithm.setPipTemporaryDecorInsets(
                mTvPipBoundsState.getPipMenuTemporaryDecorInsets());


        final Placement placement = mKeepClearAlgorithm.calculatePipPosition(
        final Placement placement = mKeepClearAlgorithm.calculatePipPosition(
                pipSize,
                pipSize,
@@ -407,8 +417,4 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
                    TAG, expandedSize.getWidth(), expandedSize.getHeight());
                    TAG, expandedSize.getWidth(), expandedSize.getHeight());
        }
        }
    }
    }

    void keepUnstashedForCurrentKeepClearAreas() {
        mKeepClearAlgorithm.keepUnstashedForCurrentKeepClearAreas();
    }
}
}
+253 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 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 com.android.wm.shell.pip.tv;

import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Handler;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

import java.util.Objects;
import java.util.function.Supplier;

/**
 * Controller managing the PiP's position.
 * Manages debouncing of PiP movements and scheduling of unstashing.
 */
public class TvPipBoundsController {
    private static final boolean DEBUG = false;
    private static final String TAG = "TvPipBoundsController";

    /**
     * Time the calculated PiP position needs to be stable before PiP is moved there,
     * to avoid erratic movement.
     * Some changes will cause the PiP to be repositioned immediately, such as changes to
     * unrestricted keep clear areas.
     */
    @VisibleForTesting
    static final long POSITION_DEBOUNCE_TIMEOUT_MILLIS = 300L;

    private final Context mContext;
    private final Supplier<Long> mClock;
    private final Handler mMainHandler;
    private final TvPipBoundsState mTvPipBoundsState;
    private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;

    @Nullable
    private PipBoundsListener mListener;

    private int mResizeAnimationDuration;
    private int mStashDurationMs;
    private Rect mCurrentPlacementBounds;
    private Rect mPipTargetBounds;

    private final Runnable mApplyPendingPlacementRunnable = this::applyPendingPlacement;
    private boolean mPendingStash;
    private Placement mPendingPlacement;
    private int mPendingPlacementAnimationDuration;
    private Runnable mUnstashRunnable;

    public TvPipBoundsController(
            Context context,
            Supplier<Long> clock,
            Handler mainHandler,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm) {
        mContext = context;
        mClock = clock;
        mMainHandler = mainHandler;
        mTvPipBoundsState = tvPipBoundsState;
        mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;

        loadConfigurations();
    }

    private void loadConfigurations() {
        final Resources res = mContext.getResources();
        mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
        mStashDurationMs = res.getInteger(R.integer.config_pipStashDuration);
    }

    void setListener(PipBoundsListener listener) {
        mListener = listener;
    }

    /**
     * Update the PiP bounds based on the state of the PiP, decors, and keep clear areas.
     * Unless {@code immediate} is {@code true}, the PiP does not move immediately to avoid
     * keep clear areas, but waits for a new position to stay uncontested for
     * {@link #POSITION_DEBOUNCE_TIMEOUT_MILLIS} before moving to it.
     * Temporary decor changes are applied immediately.
     *
     * @param stayAtAnchorPosition If true, PiP will be placed at the anchor position
     * @param disallowStashing     If true, PiP will not be placed off-screen in a stashed position
     * @param animationDuration    Duration of the animation to the new position
     * @param immediate            If true, PiP will move immediately to avoid keep clear areas
     */
    @VisibleForTesting
    void recalculatePipBounds(boolean stayAtAnchorPosition, boolean disallowStashing,
            int animationDuration, boolean immediate) {
        final Placement placement = mTvPipBoundsAlgorithm.getTvPipPlacement();

        final int stashType = disallowStashing ? STASH_TYPE_NONE : placement.getStashType();
        mTvPipBoundsState.setStashed(stashType);
        if (stayAtAnchorPosition) {
            cancelScheduledPlacement();
            applyPlacementBounds(placement.getAnchorBounds(), animationDuration);
        } else if (disallowStashing) {
            cancelScheduledPlacement();
            applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
        } else if (immediate) {
            cancelScheduledPlacement();
            applyPlacementBounds(placement.getBounds(), animationDuration);
            scheduleUnstashIfNeeded(placement);
        } else {
            applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
            schedulePinnedStackPlacement(placement, animationDuration);
        }
    }

    private void schedulePinnedStackPlacement(@NonNull final Placement placement,
            int animationDuration) {
        if (DEBUG) {
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: schedulePinnedStackPlacement() - pip bounds: %s",
                    TAG, placement.getBounds().toShortString());
        }

        if (mPendingPlacement != null && Objects.equals(mPendingPlacement.getBounds(),
                placement.getBounds())) {
            mPendingStash = mPendingStash || placement.getTriggerStash();
            return;
        }

        mPendingStash = placement.getStashType() != STASH_TYPE_NONE
                && (mPendingStash || placement.getTriggerStash());

        mMainHandler.removeCallbacks(mApplyPendingPlacementRunnable);
        mPendingPlacement = placement;
        mPendingPlacementAnimationDuration = animationDuration;
        mMainHandler.postAtTime(mApplyPendingPlacementRunnable,
                mClock.get() + POSITION_DEBOUNCE_TIMEOUT_MILLIS);
    }

    private void scheduleUnstashIfNeeded(final Placement placement) {
        if (mUnstashRunnable != null) {
            mMainHandler.removeCallbacks(mUnstashRunnable);
            mUnstashRunnable = null;
        }
        if (placement.getUnstashDestinationBounds() != null) {
            mUnstashRunnable = () -> {
                applyPlacementBounds(placement.getUnstashDestinationBounds(),
                        mResizeAnimationDuration);
                mUnstashRunnable = null;
            };
            mMainHandler.postAtTime(mUnstashRunnable, mClock.get() + mStashDurationMs);
        }
    }

    private void applyPendingPlacement() {
        if (DEBUG) {
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: applyPendingPlacement()", TAG);
        }
        if (mPendingPlacement != null) {
            if (mPendingStash) {
                mPendingStash = false;
                scheduleUnstashIfNeeded(mPendingPlacement);
            }

            if (mUnstashRunnable != null) {
                // currently stashed, use stashed pos
                applyPlacementBounds(mPendingPlacement.getBounds(),
                        mPendingPlacementAnimationDuration);
            } else {
                applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
                        mPendingPlacementAnimationDuration);
            }
        }

        mPendingPlacement = null;
    }

    void onPipDismissed() {
        mCurrentPlacementBounds = null;
        mPipTargetBounds = null;
        cancelScheduledPlacement();
    }

    private void cancelScheduledPlacement() {
        mMainHandler.removeCallbacks(mApplyPendingPlacementRunnable);
        mPendingPlacement = null;

        if (mUnstashRunnable != null) {
            mMainHandler.removeCallbacks(mUnstashRunnable);
            mUnstashRunnable = null;
        }
    }

    private void applyPlacementBounds(Rect bounds, int animationDuration) {
        if (bounds == null) {
            return;
        }

        mCurrentPlacementBounds = bounds;
        Rect adjustedBounds = mTvPipBoundsAlgorithm.adjustBoundsForTemporaryDecor(bounds);
        movePipTo(adjustedBounds, animationDuration);
    }

    /** Animates the PiP to the given bounds with the given animation duration. */
    private void movePipTo(Rect bounds, int animationDuration) {
        if (Objects.equals(mPipTargetBounds, bounds)) {
            return;
        }

        mPipTargetBounds = bounds;
        if (DEBUG) {
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: movePipTo() - new pip bounds: %s", TAG, bounds.toShortString());
        }

        if (mListener != null) {
            mListener.onPipTargetBoundsChange(bounds, animationDuration);
        }
    }

    /**
     * Interface being notified of changes to the PiP bounds as calculated by
     * @link TvPipBoundsController}.
     */
    public interface PipBoundsListener {
        /**
         * Called when the calculated PiP bounds are changing.
         *
         * @param newTargetBounds The new bounds of the PiP.
         * @param animationDuration The animation duration for the PiP movement.
         */
        void onPipTargetBoundsChange(Rect newTargetBounds, int animationDuration);
    }
}
+27 −63
Original line number Original line Diff line number Diff line
@@ -25,12 +25,10 @@ import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.RemoteAction;
import android.app.TaskInfo;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.RemoteException;
import android.view.Gravity;
import android.view.Gravity;


@@ -46,25 +44,24 @@ import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.protolog.ShellProtoLogGroup;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Set;


/**
/**
 * Manages the picture-in-picture (PIP) UI and states.
 * Manages the picture-in-picture (PIP) UI and states.
 */
 */
public class TvPipController implements PipTransitionController.PipTransitionCallback,
public class TvPipController implements PipTransitionController.PipTransitionCallback,
        TvPipMenuController.Delegate, TvPipNotificationController.Delegate,
        TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate,
        DisplayController.OnDisplaysChangedListener {
        TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener {
    private static final String TAG = "TvPipController";
    private static final String TAG = "TvPipController";
    static final boolean DEBUG = false;
    static final boolean DEBUG = false;


@@ -98,19 +95,18 @@ public class TvPipController implements PipTransitionController.PipTransitionCal


    private final TvPipBoundsState mTvPipBoundsState;
    private final TvPipBoundsState mTvPipBoundsState;
    private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
    private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
    private final TvPipBoundsController mTvPipBoundsController;
    private final PipAppOpsListener mAppOpsListener;
    private final PipAppOpsListener mAppOpsListener;
    private final PipTaskOrganizer mPipTaskOrganizer;
    private final PipTaskOrganizer mPipTaskOrganizer;
    private final PipMediaController mPipMediaController;
    private final PipMediaController mPipMediaController;
    private final TvPipNotificationController mPipNotificationController;
    private final TvPipNotificationController mPipNotificationController;
    private final TvPipMenuController mTvPipMenuController;
    private final TvPipMenuController mTvPipMenuController;
    private final ShellExecutor mMainExecutor;
    private final ShellExecutor mMainExecutor;
    private final Handler mMainHandler;
    private final TvPipImpl mImpl = new TvPipImpl();
    private final TvPipImpl mImpl = new TvPipImpl();


    private @State int mState = STATE_NO_PIP;
    private @State int mState = STATE_NO_PIP;
    private int mPreviousGravity = TvPipBoundsState.DEFAULT_TV_GRAVITY;
    private int mPreviousGravity = TvPipBoundsState.DEFAULT_TV_GRAVITY;
    private int mPinnedTaskId = NONEXISTENT_TASK_ID;
    private int mPinnedTaskId = NONEXISTENT_TASK_ID;
    private Runnable mUnstashRunnable;


    private RemoteAction mCloseAction;
    private RemoteAction mCloseAction;
    // How long the shell will wait for the app to close the PiP if a custom action is set.
    // How long the shell will wait for the app to close the PiP if a custom action is set.
@@ -123,6 +119,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
            Context context,
            Context context,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
            TvPipBoundsController tvPipBoundsController,
            PipAppOpsListener pipAppOpsListener,
            PipAppOpsListener pipAppOpsListener,
            PipTaskOrganizer pipTaskOrganizer,
            PipTaskOrganizer pipTaskOrganizer,
            PipTransitionController pipTransitionController,
            PipTransitionController pipTransitionController,
@@ -133,12 +130,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
            PipParamsChangedForwarder pipParamsChangedForwarder,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            DisplayController displayController,
            DisplayController displayController,
            WindowManagerShellWrapper wmShell,
            WindowManagerShellWrapper wmShell,
            ShellExecutor mainExecutor,
            ShellExecutor mainExecutor) {
            Handler mainHandler) {
        return new TvPipController(
        return new TvPipController(
                context,
                context,
                tvPipBoundsState,
                tvPipBoundsState,
                tvPipBoundsAlgorithm,
                tvPipBoundsAlgorithm,
                tvPipBoundsController,
                pipAppOpsListener,
                pipAppOpsListener,
                pipTaskOrganizer,
                pipTaskOrganizer,
                pipTransitionController,
                pipTransitionController,
@@ -149,14 +146,14 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
                pipParamsChangedForwarder,
                pipParamsChangedForwarder,
                displayController,
                displayController,
                wmShell,
                wmShell,
                mainExecutor,
                mainExecutor).mImpl;
                mainHandler).mImpl;
    }
    }


    private TvPipController(
    private TvPipController(
            Context context,
            Context context,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsState tvPipBoundsState,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
            TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
            TvPipBoundsController tvPipBoundsController,
            PipAppOpsListener pipAppOpsListener,
            PipAppOpsListener pipAppOpsListener,
            PipTaskOrganizer pipTaskOrganizer,
            PipTaskOrganizer pipTaskOrganizer,
            PipTransitionController pipTransitionController,
            PipTransitionController pipTransitionController,
@@ -167,16 +164,16 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
            PipParamsChangedForwarder pipParamsChangedForwarder,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            DisplayController displayController,
            DisplayController displayController,
            WindowManagerShellWrapper wmShell,
            WindowManagerShellWrapper wmShell,
            ShellExecutor mainExecutor,
            ShellExecutor mainExecutor) {
            Handler mainHandler) {
        mContext = context;
        mContext = context;
        mMainExecutor = mainExecutor;
        mMainExecutor = mainExecutor;
        mMainHandler = mainHandler;


        mTvPipBoundsState = tvPipBoundsState;
        mTvPipBoundsState = tvPipBoundsState;
        mTvPipBoundsState.setDisplayId(context.getDisplayId());
        mTvPipBoundsState.setDisplayId(context.getDisplayId());
        mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
        mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
        mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
        mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
        mTvPipBoundsController = tvPipBoundsController;
        mTvPipBoundsController.setListener(this);


        mPipMediaController = pipMediaController;
        mPipMediaController = pipMediaController;


@@ -227,7 +224,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
    /**
    /**
     * Starts the process if bringing up the Pip menu if by issuing a command to move Pip
     * Starts the process if bringing up the Pip menu if by issuing a command to move Pip
     * task/window to the "Menu" position. We'll show the actual Menu UI (eg. actions) once the Pip
     * task/window to the "Menu" position. We'll show the actual Menu UI (eg. actions) once the Pip
     * task/window is properly positioned in {@link #onPipTransitionFinished(ComponentName, int)}.
     * task/window is properly positioned in {@link #onPipTransitionFinished(int)}.
     */
     */
    @Override
    @Override
    public void showPictureInPictureMenu() {
    public void showPictureInPictureMenu() {
@@ -256,7 +253,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
                    "%s: closeMenu(), state before=%s", TAG, stateToName(mState));
                    "%s: closeMenu(), state before=%s", TAG, stateToName(mState));
        }
        }
        setState(STATE_PIP);
        setState(STATE_PIP);
        mTvPipBoundsAlgorithm.keepUnstashedForCurrentKeepClearAreas();
        updatePinnedStackBounds();
        updatePinnedStackBounds();
    }
    }


@@ -331,68 +327,35 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
    public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
    public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
            Set<Rect> unrestricted) {
            Set<Rect> unrestricted) {
        if (mTvPipBoundsState.getDisplayId() == displayId) {
        if (mTvPipBoundsState.getDisplayId() == displayId) {
            boolean unrestrictedAreasChanged = !Objects.equals(unrestricted,
                    mTvPipBoundsState.getUnrestrictedKeepClearAreas());
            mTvPipBoundsState.setKeepClearAreas(restricted, unrestricted);
            mTvPipBoundsState.setKeepClearAreas(restricted, unrestricted);
            updatePinnedStackBounds();
            updatePinnedStackBounds(mResizeAnimationDuration, unrestrictedAreasChanged);
        }
        }
    }
    }


    private void updatePinnedStackBounds() {
    private void updatePinnedStackBounds() {
        updatePinnedStackBounds(mResizeAnimationDuration);
        updatePinnedStackBounds(mResizeAnimationDuration, true);
    }
    }


    /**
    /**
     * Update the PiP bounds based on the state of the PiP and keep clear areas.
     * Update the PiP bounds based on the state of the PiP and keep clear areas.
     * Animates to the current PiP bounds, and schedules unstashing the PiP if necessary.
     */
     */
    private void updatePinnedStackBounds(int animationDuration) {
    private void updatePinnedStackBounds(int animationDuration, boolean immediate) {
        if (mState == STATE_NO_PIP) {
        if (mState == STATE_NO_PIP) {
            return;
            return;
        }
        }

        final boolean stayAtAnchorPosition = mTvPipMenuController.isInMoveMode();
        final boolean stayAtAnchorPosition = mTvPipMenuController.isInMoveMode();
        final boolean disallowStashing = mState == STATE_PIP_MENU || stayAtAnchorPosition;
        final boolean disallowStashing = mState == STATE_PIP_MENU || stayAtAnchorPosition;
        final Placement placement = mTvPipBoundsAlgorithm.getTvPipBounds();
        mTvPipBoundsController.recalculatePipBounds(stayAtAnchorPosition, disallowStashing,

                animationDuration, immediate);
        int stashType =
                disallowStashing ? PipBoundsState.STASH_TYPE_NONE : placement.getStashType();
        mTvPipBoundsState.setStashed(stashType);

        if (stayAtAnchorPosition) {
            movePinnedStackTo(placement.getAnchorBounds());
        } else if (disallowStashing) {
            movePinnedStackTo(placement.getUnstashedBounds());
        } else {
            movePinnedStackTo(placement.getBounds());
        }

        if (mUnstashRunnable != null) {
            mMainHandler.removeCallbacks(mUnstashRunnable);
            mUnstashRunnable = null;
        }
        if (!disallowStashing && placement.getUnstashDestinationBounds() != null) {
            mUnstashRunnable = () -> {
                movePinnedStackTo(placement.getUnstashDestinationBounds(), animationDuration);
            };
            mMainHandler.postAtTime(mUnstashRunnable, placement.getUnstashTime());
        }
    }
    }


    /** Animates the PiP to the given bounds. */
    @Override
    private void movePinnedStackTo(Rect bounds) {
    public void onPipTargetBoundsChange(Rect newTargetBounds, int animationDuration) {
        movePinnedStackTo(bounds, mResizeAnimationDuration);
        mPipTaskOrganizer.scheduleAnimateResizePip(newTargetBounds,
    }
                animationDuration, rect -> mTvPipMenuController.updateExpansionState());

        mTvPipMenuController.onPipTransitionStarted(newTargetBounds);
    /** Animates the PiP to the given bounds with the given animation duration. */
    private void movePinnedStackTo(Rect bounds, int animationDuration) {
        if (DEBUG) {
            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: movePinnedStack() - new pip bounds: %s", TAG, bounds.toShortString());
        }
        mPipTaskOrganizer.scheduleAnimateResizePip(bounds,
                animationDuration, rect -> {
                    mTvPipMenuController.updateExpansionState();
                });
        mTvPipMenuController.onPipTransitionStarted(bounds);
    }
    }


    /**
    /**
@@ -431,7 +394,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal


    @Override
    @Override
    public void closeEduText() {
    public void closeEduText() {
        updatePinnedStackBounds(mEduTextWindowExitAnimationDurationMs);
        updatePinnedStackBounds(mEduTextWindowExitAnimationDurationMs, false);
    }
    }


    private void registerSessionListenerForCurrentUser() {
    private void registerSessionListenerForCurrentUser() {
@@ -473,6 +436,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
        mPipNotificationController.dismiss();
        mPipNotificationController.dismiss();
        mTvPipMenuController.closeMenu();
        mTvPipMenuController.closeMenu();
        mTvPipBoundsState.resetTvPipState();
        mTvPipBoundsState.resetTvPipState();
        mTvPipBoundsController.onPipDismissed();
        setState(STATE_NO_PIP);
        setState(STATE_NO_PIP);
        mPinnedTaskId = NONEXISTENT_TASK_ID;
        mPinnedTaskId = NONEXISTENT_TASK_ID;
    }
    }
+12 −66

File changed.

Preview size limit exceeded, changes collapsed.

Loading