From d2d5e6831267f1b7ca123086260bbed9b66950cb Mon Sep 17 00:00:00 2001 From: Galia Peycheva Date: Tue, 1 Mar 2022 08:19:35 +0100 Subject: [PATCH] Add Tv Pip rounded corners tray This CL adds a permanent grey border around the pip surface, which has rounded corners, and a backing surface, with the same color as the border. This way the pip surface is sandwiched between the two pip menu surfaces Bug: 219883374 Test: m && open app in expanded pip && move pip to opposite edge && start dream && wake up dream && toggle expanded pip Change-Id: Idf22ca22044e7e5e3ca6b39326e3afb76e3ee43c --- .../res/drawable/tv_pip_menu_background.xml | 23 ++ .../Shell/res/drawable/tv_pip_menu_border.xml | 23 +- .../Shell/res/layout/tv_pip_menu.xml | 8 +- .../res/layout/tv_pip_menu_background.xml | 28 +++ .../Shell/res/values-tvdpi/dimen.xml | 9 +- .../Shell/res/values/colors_tv.xml | 3 +- .../wm/shell/pip/tv/TvPipBoundsAlgorithm.java | 9 +- .../wm/shell/pip/tv/TvPipBoundsState.java | 10 +- .../shell/pip/tv/TvPipKeepClearAlgorithm.kt | 47 +++- .../wm/shell/pip/tv/TvPipMenuController.java | 230 ++++++++++++------ .../wm/shell/pip/tv/TvPipMenuView.java | 48 ++-- 11 files changed, 320 insertions(+), 118 deletions(-) create mode 100644 libs/WindowManager/Shell/res/drawable/tv_pip_menu_background.xml create mode 100644 libs/WindowManager/Shell/res/layout/tv_pip_menu_background.xml diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_background.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_background.xml new file mode 100644 index 0000000000000..0c627921573c8 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_background.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml index 9bc03112b1186..846fdb3e8a58a 100644 --- a/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml +++ b/libs/WindowManager/Shell/res/drawable/tv_pip_menu_border.xml @@ -14,9 +14,20 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - - - - \ No newline at end of file + + + + + + + + + + + + + + diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml index b826d03bf7650..f84b8f642953f 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml @@ -16,9 +16,10 @@ --> + android:id="@+id/tv_pip_menu" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center|top"> diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu_background.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_background.xml new file mode 100644 index 0000000000000..5af40200d2401 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_background.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml index 558ec51752b93..63c26dadfab43 100644 --- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml +++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml @@ -22,7 +22,12 @@ 4dp 26dp 4dp - 4dp + 500 + + 4dp + 6dp 24dp @@ -30,5 +35,7 @@ 24dp 5dp + + 1dp diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml index 64b146ec3a834..4e7fee8e2e1e3 100644 --- a/libs/WindowManager/Shell/res/values/colors_tv.xml +++ b/libs/WindowManager/Shell/res/values/colors_tv.xml @@ -23,4 +23,5 @@ #E8EAED #990E0E0F #E8EAED - \ No newline at end of file + #1E232C + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java index 1aefd77419aaf..93f699e4645d4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java @@ -27,6 +27,7 @@ import static com.android.wm.shell.pip.tv.TvPipBoundsState.ORIENTATION_VERTICAL; import android.content.Context; import android.content.res.Resources; +import android.graphics.Insets; import android.graphics.Rect; import android.os.SystemClock; import android.util.ArraySet; @@ -150,6 +151,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { mKeepClearAlgorithm.setScreenSize(screenSize); mKeepClearAlgorithm.setMovementBounds(insetBounds); mKeepClearAlgorithm.setStashOffset(mTvPipBoundsState.getStashOffset()); + mKeepClearAlgorithm.setPipDecorInsets(mTvPipBoundsState.getPipMenuPermanentDecorInsets()); final Placement placement = mKeepClearAlgorithm.calculatePipPosition( pipSize, @@ -340,6 +342,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { final DisplayLayout displayLayout = mTvPipBoundsState.getDisplayLayout(); final float expandedRatio = mTvPipBoundsState.getDesiredTvExpandedAspectRatio(); // width / height + final Insets pipDecorations = mTvPipBoundsState.getPipMenuPermanentDecorInsets(); final Size expandedSize; if (expandedRatio == 0) { @@ -352,7 +355,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) { expandedSize = mTvPipBoundsState.getTvExpandedSize(); } else { - int maxHeight = displayLayout.height() - (2 * mScreenEdgeInsets.y); + int maxHeight = displayLayout.height() - (2 * mScreenEdgeInsets.y) + - pipDecorations.top - pipDecorations.bottom; float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio; if (maxHeight > aspectRatioHeight) { @@ -374,7 +378,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_VERTICAL) { expandedSize = mTvPipBoundsState.getTvExpandedSize(); } else { - int maxWidth = displayLayout.width() - (2 * mScreenEdgeInsets.x); + int maxWidth = displayLayout.width() - (2 * mScreenEdgeInsets.x) + - pipDecorations.left - pipDecorations.right; float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio; if (maxWidth > aspectRatioWidth) { if (DEBUG) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java index 9865548530340..cdb18bc180beb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java @@ -24,6 +24,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.graphics.Insets; import android.util.Size; import android.view.Gravity; @@ -60,7 +61,7 @@ public class TvPipBoundsState extends PipBoundsState { private @Orientation int mTvFixedPipOrientation; private int mTvPipGravity; private @Nullable Size mTvExpandedSize; - + private @NonNull Insets mPipMenuPermanentDecorInsets = Insets.NONE; public TvPipBoundsState(@NonNull Context context) { super(context); @@ -159,4 +160,11 @@ public class TvPipBoundsState extends PipBoundsState { return mIsTvExpandedPipSupported; } + public void setPipMenuPermanentDecorInsets(@NonNull Insets permanentInsets) { + mPipMenuPermanentDecorInsets = permanentInsets; + } + + public @NonNull Insets getPipMenuPermanentDecorInsets() { + return mPipMenuPermanentDecorInsets; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt index 5ac7a7200494d..48f76288f1ec6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.pip.tv +import android.graphics.Insets import android.graphics.Point import android.graphics.Rect import android.util.Size @@ -94,6 +95,9 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { private var lastAreasOverlappingUnstashPosition: Set = emptySet() private var lastStashTime: Long = Long.MIN_VALUE + /** Spaces around the PiP that we should leave space for when placing the PiP */ + private var pipDecorInsets = Insets.NONE + /** * Calculates the position the PiP should be placed at, taking into consideration the * given keep clear areas. @@ -120,7 +124,9 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { ): Placement { val transformedRestrictedAreas = transformAndFilterAreas(restrictedAreas) val transformedUnrestrictedAreas = transformAndFilterAreas(unrestrictedAreas) - val pipAnchorBounds = getNormalPipAnchorBounds(pipSize, transformedMovementBounds) + + val pipSizeWithDecors = addDecors(pipSize) + val pipAnchorBounds = getNormalPipAnchorBounds(pipSizeWithDecors, transformedMovementBounds) val result = calculatePipPositionTransformed( pipAnchorBounds, @@ -128,12 +134,17 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { transformedUnrestrictedAreas ) - val screenSpaceBounds = fromTransformedSpace(result.bounds) + val pipBounds = removeDecors(fromTransformedSpace(result.bounds)) + val anchorBounds = removeDecors(fromTransformedSpace(result.anchorBounds)) + val unstashedDestBounds = result.unstashDestinationBounds?.let { + removeDecors(fromTransformedSpace(it)) + } + return Placement( - screenSpaceBounds, - fromTransformedSpace(result.anchorBounds), - getStashType(screenSpaceBounds, movementBounds), - result.unstashDestinationBounds?.let { fromTransformedSpace(it) }, + pipBounds, + anchorBounds, + getStashType(pipBounds, movementBounds), + unstashedDestBounds, result.unstashTime ) } @@ -447,6 +458,11 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { transformedMovementBounds = toTransformedSpace(movementBounds) } + fun setPipDecorInsets(insets: Insets) { + if (pipDecorInsets == insets) return + pipDecorInsets = insets + } + /** * @param open Whether this event marks the opening of an occupied segment * @param pos The coordinate of this event @@ -735,6 +751,25 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { return horizontal && vertical } + /** + * Adds space around [size] to leave space for decorations that will be drawn around the pip + */ + private fun addDecors(size: Size): Size { + val bounds = Rect(0, 0, size.width, size.height) + bounds.inset(pipDecorInsets) + + return Size(bounds.width(), bounds.height()) + } + + /** + * Removes the space that was reserved for decorations around the pip + */ + private fun removeDecors(bounds: Rect): Rect { + val pipDecorReverseInsets = Insets.subtract(Insets.NONE, pipDecorInsets) + bounds.inset(pipDecorReverseInsets) + return bounds + } + private fun Rect.offsetCopy(dx: Int, dy: Int) = Rect(this).apply { offset(dx, dy) } private fun Rect.intersectsY(other: Rect) = bottom >= other.top && top <= other.bottom private fun Rect.intersectsX(other: Rect) = right >= other.left && left <= other.right diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 35c34ac8315fa..3e7d10c3fcb8e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -25,13 +25,17 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ParceledListSlice; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; import android.os.RemoteException; +import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; +import android.view.View; +import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import androidx.annotation.Nullable; @@ -53,6 +57,7 @@ import java.util.Objects; public class TvPipMenuController implements PipMenuController, TvPipMenuView.Listener { private static final String TAG = "TvPipMenuController"; private static final boolean DEBUG = TvPipController.DEBUG; + private static final String BACKGROUND_WINDOW_TITLE = "PipBackgroundView"; private final Context mContext; private final SystemWindows mSystemWindows; @@ -62,6 +67,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis private Delegate mDelegate; private SurfaceControl mLeash; private TvPipMenuView mPipMenuView; + private View mPipBackgroundView; // User can actively move the PiP via the DPAD. private boolean mInMoveMode; @@ -74,6 +80,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis private RemoteAction mCloseAction; private SyncRtSurfaceTransactionApplier mApplier; + private SyncRtSurfaceTransactionApplier mBackgroundApplier; RectF mTmpSourceRectF = new RectF(); RectF mTmpDestinationRectF = new RectF(); Matrix mMoveTransform = new Matrix(); @@ -91,6 +98,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis if (DEBUG) e.printStackTrace(); } }; + private final int mPipMenuSurfaceOuterSpace; public TvPipMenuController(Context context, TvPipBoundsState tvPipBoundsState, SystemWindows systemWindows, PipMediaController pipMediaController, @@ -113,6 +121,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mainHandler, Context.RECEIVER_EXPORTED); pipMediaController.addActionListener(this::onMediaActionsChanged); + + mPipMenuSurfaceOuterSpace = context.getResources() + .getDimensionPixelSize(R.dimen.pip_menu_outer_space); + + final int pipBorderWidth = context.getResources() + .getDimensionPixelSize(R.dimen.pip_menu_border_width); + mTvPipBoundsState.setPipMenuPermanentDecorInsets(Insets.of(-pipBorderWidth, + -pipBorderWidth, -pipBorderWidth, -pipBorderWidth)); } void setDelegate(Delegate delegate) { @@ -138,24 +154,54 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } mLeash = leash; + attachPipMenu(); + } + + private void attachPipMenu() { + if (DEBUG) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: attachPipMenu()", TAG); + } + + if (mPipMenuView != null) { + detachPipMenu(); + } + + attachPipBackgroundView(); attachPipMenuView(); } private void attachPipMenuView() { - if (DEBUG) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: attachPipMenuView()", TAG); - } - - if (mPipMenuView != null) { - detachPipMenuView(); - } - mPipMenuView = new TvPipMenuView(mContext); mPipMenuView.setListener(this); - mSystemWindows.addView(mPipMenuView, - getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), - 0, SHELL_ROOT_LAYER_PIP); + setUpViewSurfaceZOrder(mPipMenuView, 1); + addPipMenuViewToSystemWindows(mPipMenuView, MENU_WINDOW_TITLE); + } + + private void attachPipBackgroundView() { + mPipBackgroundView = LayoutInflater.from(mContext) + .inflate(R.layout.tv_pip_menu_background, null); + setUpViewSurfaceZOrder(mPipBackgroundView, -1); + addPipMenuViewToSystemWindows(mPipBackgroundView, BACKGROUND_WINDOW_TITLE); + } + + private void setUpViewSurfaceZOrder(View v, int zOrderRelativeToPip) { + v.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + v.getViewRootImpl().addSurfaceChangedCallback( + new PipMenuSurfaceChangedCallback(v, zOrderRelativeToPip)); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + } + + private void addPipMenuViewToSystemWindows(View v, String title) { + mSystemWindows.addView(v, getPipMenuLayoutParams(title, 0 /* width */, 0 /* height */), + 0 /* displayId */, SHELL_ROOT_LAYER_PIP); } void showMovementMenuOnly() { @@ -171,8 +217,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis @Override public void showMenu() { if (DEBUG) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: showMenu()", TAG); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMenu()", TAG); } mInMoveMode = false; mCloseAfterExitMoveMenu = false; @@ -183,24 +228,14 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis if (mPipMenuView == null) { return; } - Rect menuBounds = getMenuBounds(mTvPipBoundsState.getBounds()); - mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams( - MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height())); maybeUpdateMenuViewActions(); updateExpansionState(); - SurfaceControl menuSurfaceControl = getSurfaceControl(); - if (menuSurfaceControl != null) { - SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - t.setRelativeLayer(mPipMenuView.getWindowSurfaceControl(), mLeash, 1); - t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top); - t.apply(); - } grantPipMenuFocus(true); if (mInMoveMode) { mPipMenuView.showMoveMenu(mDelegate.getPipGravity()); } else { - mPipMenuView.showButtonMenu(); + mPipMenuView.showButtonsMenu(); } } @@ -214,11 +249,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mPipMenuView.setIsExpanded(mTvPipBoundsState.isTvPipExpanded()); } - private Rect getMenuBounds(Rect pipBounds) { - int extraSpaceInPx = mContext.getResources() - .getDimensionPixelSize(R.dimen.pip_menu_outer_space); + private Rect calculateMenuSurfaceBounds(Rect pipBounds) { Rect menuBounds = new Rect(pipBounds); - menuBounds.inset(-extraSpaceInPx, -extraSpaceInPx); + menuBounds.inset(-mPipMenuSurfaceOuterSpace, -mPipMenuSurfaceOuterSpace); return menuBounds; } @@ -227,11 +260,12 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: closeMenu()", TAG); } + if (mPipMenuView == null) { return; } - mPipMenuView.hideAll(); + mPipMenuView.hideAllUserControls(); grantPipMenuFocus(false); mDelegate.onMenuClosed(); } @@ -266,7 +300,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } if (mInMoveMode) { mInMoveMode = false; - mPipMenuView.showButtonMenu(); + mPipMenuView.showButtonsMenu(); return true; } return false; @@ -287,7 +321,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis @Override public void detach() { closeMenu(); - detachPipMenuView(); + detachPipMenu(); mLeash = null; } @@ -346,20 +380,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis @Override public boolean isMenuVisible() { - boolean isVisible = mPipMenuView != null && mPipMenuView.isVisible(); - if (DEBUG) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: isMenuVisible: %b", TAG, isVisible); - } - return isVisible; + return true; } /** * Does an immediate window crop of the PiP menu. */ @Override - public void resizePipMenu(@android.annotation.Nullable SurfaceControl pipLeash, - @android.annotation.Nullable SurfaceControl.Transaction t, + public void resizePipMenu(@Nullable SurfaceControl pipLeash, + @Nullable SurfaceControl.Transaction t, Rect destinationBounds) { if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, @@ -373,24 +402,36 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis return; } - SurfaceControl surfaceControl = getSurfaceControl(); - SyncRtSurfaceTransactionApplier.SurfaceParams - params = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(surfaceControl) - .withWindowCrop(getMenuBounds(destinationBounds)) + final Rect menuBounds = calculateMenuSurfaceBounds(destinationBounds); + + final SurfaceControl frontSurface = getSurfaceControl(mPipMenuView); + final SyncRtSurfaceTransactionApplier.SurfaceParams frontParams = + new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(frontSurface) + .withWindowCrop(menuBounds) .build(); + + final SurfaceControl backSurface = getSurfaceControl(mPipBackgroundView); + final SyncRtSurfaceTransactionApplier.SurfaceParams backParams = + new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(backSurface) + .withWindowCrop(menuBounds) + .build(); + + // TODO(b/226580399): switch to using SurfaceSyncer (see b/200284684) to synchronize the + // animations of the pip surface with the content of the front and back menu surfaces + mBackgroundApplier.scheduleApply(backParams); if (pipLeash != null && t != null) { - SyncRtSurfaceTransactionApplier.SurfaceParams + final SyncRtSurfaceTransactionApplier.SurfaceParams pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash) .withMergeTransaction(t) .build(); - mApplier.scheduleApply(params, pipParams); + mApplier.scheduleApply(frontParams, pipParams); } else { - mApplier.scheduleApply(params); + mApplier.scheduleApply(frontParams); } } - private SurfaceControl getSurfaceControl() { - return mSystemWindows.getViewSurface(mPipMenuView); + private SurfaceControl getSurfaceControl(View v) { + return mSystemWindows.getViewSurface(v); } @Override @@ -412,44 +453,52 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis return; } - Rect menuDestBounds = getMenuBounds(pipDestBounds); - Rect mTmpSourceBounds = new Rect(); + final Rect menuDestBounds = calculateMenuSurfaceBounds(pipDestBounds); + final Rect tmpSourceBounds = new Rect(); // If there is no pip leash supplied, that means the PiP leash is already finalized // resizing and the PiP menu is also resized. We then want to do a scale from the current // new menu bounds. if (pipLeash != null && transaction != null) { if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: mTmpSourceBounds based on mPipMenuView.getBoundsOnScreen()", TAG); + "%s: tmpSourceBounds based on mPipMenuView.getBoundsOnScreen()", TAG); } - mPipMenuView.getBoundsOnScreen(mTmpSourceBounds); + mPipMenuView.getBoundsOnScreen(tmpSourceBounds); } else { if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: mTmpSourceBounds based on menu width and height", TAG); + "%s: tmpSourceBounds based on menu width and height", TAG); } - mTmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height()); + tmpSourceBounds.set(0, 0, menuDestBounds.width(), menuDestBounds.height()); } - mTmpSourceRectF.set(mTmpSourceBounds); + mTmpSourceRectF.set(tmpSourceBounds); mTmpDestinationRectF.set(menuDestBounds); - mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + mMoveTransform.setTranslate(mTmpDestinationRectF.left, mTmpDestinationRectF.top); - SurfaceControl surfaceControl = getSurfaceControl(); - SyncRtSurfaceTransactionApplier.SurfaceParams params = - new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder( - surfaceControl) + final SurfaceControl frontSurface = getSurfaceControl(mPipMenuView); + final SyncRtSurfaceTransactionApplier.SurfaceParams frontParams = + new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(frontSurface) .withMatrix(mMoveTransform) .build(); + final SurfaceControl backSurface = getSurfaceControl(mPipBackgroundView); + final SyncRtSurfaceTransactionApplier.SurfaceParams backParams = + new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(backSurface) + .withMatrix(mMoveTransform) + .build(); + + // TODO(b/226580399): switch to using SurfaceSyncer (see b/200284684) to synchronize the + // animations of the pip surface with the content of the front and back menu surfaces + mBackgroundApplier.scheduleApply(backParams); if (pipLeash != null && transaction != null) { - SyncRtSurfaceTransactionApplier.SurfaceParams - pipParams = new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash) + final SyncRtSurfaceTransactionApplier.SurfaceParams pipParams = + new SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(pipLeash) .withMergeTransaction(transaction) .build(); - mApplier.scheduleApply(params, pipParams); + mApplier.scheduleApply(frontParams, pipParams); } else { - mApplier.scheduleApply(params); + mApplier.scheduleApply(frontParams); } if (mPipMenuView.getViewRootImpl() != null) { @@ -470,29 +519,40 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis if (mApplier == null) { mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView); } + if (mBackgroundApplier == null) { + mBackgroundApplier = new SyncRtSurfaceTransactionApplier(mPipBackgroundView); + } return true; } - private void detachPipMenuView() { - if (mPipMenuView == null) { - return; + private void detachPipMenu() { + if (mPipMenuView != null) { + mApplier = null; + mSystemWindows.removeView(mPipMenuView); + mPipMenuView = null; } - mApplier = null; - mSystemWindows.removeView(mPipMenuView); - mPipMenuView = null; + if (mPipBackgroundView != null) { + mBackgroundApplier = null; + mSystemWindows.removeView(mPipBackgroundView); + mPipBackgroundView = null; + } } @Override public void updateMenuBounds(Rect destinationBounds) { - Rect menuBounds = getMenuBounds(destinationBounds); + final Rect menuBounds = calculateMenuSurfaceBounds(destinationBounds); if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: updateMenuBounds: %s", TAG, menuBounds.toShortString()); } + mSystemWindows.updateViewLayout(mPipBackgroundView, + getPipMenuLayoutParams(BACKGROUND_WINDOW_TITLE, menuBounds.width(), + menuBounds.height())); mSystemWindows.updateViewLayout(mPipMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, menuBounds.width(), menuBounds.height())); + if (mPipMenuView != null) { mPipMenuView.updateLayout(destinationBounds); } @@ -555,4 +615,30 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis "%s: Unable to update focus, %s", TAG, e); } } + + private class PipMenuSurfaceChangedCallback implements ViewRootImpl.SurfaceChangedCallback { + private final View mView; + private final int mZOrder; + + PipMenuSurfaceChangedCallback(View v, int zOrder) { + mView = v; + mZOrder = zOrder; + } + + @Override + public void surfaceCreated(SurfaceControl.Transaction t) { + final SurfaceControl sc = getSurfaceControl(mView); + if (sc != null) { + t.setRelativeLayer(sc, mLeash, mZOrder); + } + } + + @Override + public void surfaceReplaced(SurfaceControl.Transaction t) { + } + + @Override + public void surfaceDestroyed() { + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index 9529d04fe1852..f5c8871cb9914 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -81,6 +81,8 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private final TvPipMenuActionButton mExpandButton; private final TvPipMenuActionButton mCloseButton; + private final int mPipMenuFadeAnimationDuration; + public TvPipMenuView(@NonNull Context context) { this(context, null); } @@ -121,6 +123,9 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { mArrowRight = findViewById(R.id.tv_pip_menu_arrow_right); mArrowDown = findViewById(R.id.tv_pip_menu_arrow_down); mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left); + + mPipMenuFadeAnimationDuration = context.getResources() + .getInteger(R.integer.pip_menu_fade_animation_duration); } void updateLayout(Rect updatedBounds) { @@ -184,30 +189,32 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showMoveMenu()", TAG); } - showMenuButtons(false); + showButtonsMenu(false); showMovementHints(gravity); - showMenuFrame(true); + setFrameHighlighted(true); } - void showButtonMenu() { + void showButtonsMenu() { if (DEBUG) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: showButtonMenu()", TAG); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: showButtonsMenu()", TAG); } - showMenuButtons(true); + showButtonsMenu(true); hideMovementHints(); - showMenuFrame(true); + setFrameHighlighted(true); } /** * Hides all menu views, including the menu frame. */ - void hideAll() { + void hideAllUserControls() { if (DEBUG) { - ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: hideAll()", TAG); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "%s: hideAllUserControls()", TAG); } - showMenuButtons(false); + showButtonsMenu(false); hideMovementHints(); - showMenuFrame(false); + setFrameHighlighted(false); } private void animateAlphaTo(float alpha, View view) { @@ -217,7 +224,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { view.animate() .alpha(alpha) .setInterpolator(alpha == 0f ? TvPipInterpolators.EXIT : TvPipInterpolators.ENTER) - .setDuration(500) + .setDuration(mPipMenuFadeAnimationDuration) .withStartAction(() -> { if (alpha != 0) { view.setVisibility(VISIBLE); @@ -230,15 +237,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { }); } - boolean isVisible() { - return mMenuFrameView.getAlpha() != 0f - || mActionButtonsContainer.getAlpha() != 0f - || mArrowUp.getAlpha() != 0f - || mArrowRight.getAlpha() != 0f - || mArrowDown.getAlpha() != 0f - || mArrowLeft.getAlpha() != 0f; - } - void setAdditionalActions(List actions, RemoteAction closeAction, Handler mainHandler) { if (DEBUG) { @@ -423,18 +421,18 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { } /** - * Show or hide the pip user actions. + * Show or hide the pip buttons menu. */ - public void showMenuButtons(boolean show) { + public void showButtonsMenu(boolean show) { if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, - "%s: showMenuButtons: %b", TAG, show); + "%s: showUserActions: %b", TAG, show); } animateAlphaTo(show ? 1 : 0, mActionButtonsContainer); } - private void showMenuFrame(boolean show) { - animateAlphaTo(show ? 1 : 0, mMenuFrameView); + private void setFrameHighlighted(boolean highlighted) { + mMenuFrameView.setActivated(highlighted); } interface Listener {