diff options
Diffstat (limited to 'libs')
20 files changed, 403 insertions, 226 deletions
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml new file mode 100644 index 000000000000..552048e8be9a --- /dev/null +++ b/libs/WindowManager/Shell/res/values-television/config.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<!-- These resources are around just to allow their values to be customized + for TV products. Do not translate. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- The percentage of the screen width to use for the default width or height of + picture-in-picture windows. Regardless of the percent set here, calculated size will never + be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item> + + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. + These values are in DPs and will be converted to pixel sizes internally. --> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> + 24x24 + </string> + + <!-- The default gravity for the picture-in-picture window. + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> +</resources> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 1b8032b7077b..d416c060c86c 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -70,4 +70,30 @@ <!-- Animation duration when exit starting window: reveal app --> <integer name="starting_window_app_reveal_anim_duration">266</integer> + + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. + These values are in DPs and will be converted to pixel sizes internally. --> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> + 16x16 + </string> + + <!-- The percentage of the screen width to use for the default width or height of + picture-in-picture windows. Regardless of the percent set here, calculated size will never + be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item> + + <!-- The default aspect ratio for picture-in-picture windows. --> + <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen"> + 1.777778 + </item> + + <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size + will be used instead of an adaptive size based loosely on area. --> + <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen"> + 1.777778 + </item> + + <!-- The default gravity for the picture-in-picture window. + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 8a8231d00195..2c96786bb521 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -18,8 +18,8 @@ <dimen name="dismiss_circle_size">96dp</dimen> <dimen name="dismiss_circle_small">60dp</dimen> - <!-- The height of the gradient indicating the dismiss edge when moving a PIP. --> - <dimen name="floating_dismiss_gradient_height">250dp</dimen> + <!-- The height of the gradient indicating the dismiss edge when moving a PIP or bubble. --> + <dimen name="floating_dismiss_gradient_height">548dp</dimen> <!-- The padding around a PiP actions. --> <dimen name="pip_action_padding">16dp</dimen> @@ -129,6 +129,9 @@ <dimen name="bubble_dismiss_encircle_size">52dp</dimen> <!-- Padding around the view displayed when the bubble is expanded --> <dimen name="bubble_expanded_view_padding">16dp</dimen> + <!-- Padding for the edge of the expanded view that is closest to the edge of the screen used + when displaying in landscape on a large screen. --> + <dimen name="bubble_expanded_view_largescreen_landscape_padding">128dp</dimen> <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include a slight touch slop around the expanded view. --> <dimen name="bubble_expanded_view_slop">8dp</dimen> @@ -251,4 +254,14 @@ <!-- The distance of the shift icon when early exit starting window. --> <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen> + + <!-- The default minimal size of a PiP task, in both dimensions. --> + <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen> + + <!-- + The overridable minimal size of a PiP task, in both dimensions. + Different from default_minimal_size_pip_resizable_task, this is to limit the dimension + when the pinned stack size is overridden by app via minWidth/minHeight. + --> + <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 241f1a7d4d1c..d0138a488295 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -47,7 +47,10 @@ import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -131,6 +134,10 @@ public class BubbleController { public static final String RIGHT_POSITION = "Right"; public static final String BOTTOM_POSITION = "Bottom"; + // Should match with PhoneWindowManager + private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; + private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; + private final Context mContext; private final BubblesImpl mImpl = new BubblesImpl(); private Bubbles.BubbleExpandListener mExpandListener; @@ -675,6 +682,7 @@ public class BubbleController { try { mAddedToWindowManager = true; + registerBroadcastReceiver(); mBubbleData.getOverflow().initialize(this); mWindowManager.addView(mStackView, mWmLayoutParams); mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> { @@ -717,6 +725,7 @@ public class BubbleController { try { mAddedToWindowManager = false; + mContext.unregisterReceiver(mBroadcastReceiver); if (mStackView != null) { mWindowManager.removeView(mStackView); mBubbleData.getOverflow().cleanUpExpandedState(); @@ -730,11 +739,34 @@ public class BubbleController { } } + private void registerBroadcastReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + filter.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(mBroadcastReceiver, filter); + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!isStackExpanded()) return; // Nothing to do + + String action = intent.getAction(); + String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); + if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) + && SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason)) + || Intent.ACTION_SCREEN_OFF.equals(action)) { + mMainExecutor.execute(() -> collapseStack()); + } + } + }; + /** * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been * added in the meantime. */ - void onAllBubblesAnimatedOut() { + @VisibleForTesting + public void onAllBubblesAnimatedOut() { if (mStackView != null) { mStackView.setVisibility(INVISIBLE); removeFromWindowManagerMaybe(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 75b19fb03e73..bc0db3635e35 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -67,7 +67,11 @@ public class BubblePositioner { /** The max percent of screen width to use for the flyout on phone. */ public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f; /** The percent of screen width that should be used for the expanded view on a large screen. **/ - public static final float EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT = 0.72f; + private static final float EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT = 0.48f; + /** The percent of screen width that should be used for the expanded view on a large screen. **/ + private static final float EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT = 0.70f; + /** The percent of screen width that should be used for the expanded view on a small tablet. **/ + private static final float EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT = 0.72f; private Context mContext; private WindowManager mWindowManager; @@ -77,6 +81,7 @@ public class BubblePositioner { private boolean mImeVisible; private int mImeHeight; private boolean mIsLargeScreen; + private boolean mIsSmallTablet; private Rect mPositionRect; private int mDefaultMaxBubbles; @@ -86,7 +91,8 @@ public class BubblePositioner { private int mExpandedViewMinHeight; private int mExpandedViewLargeScreenWidth; - private int mExpandedViewLargeScreenInset; + private int mExpandedViewLargeScreenInsetClosestEdge; + private int mExpandedViewLargeScreenInsetFurthestEdge; private int mOverflowWidth; private int mExpandedViewPadding; @@ -127,17 +133,26 @@ public class BubblePositioner { | WindowInsets.Type.statusBars() | WindowInsets.Type.displayCutout()); - mIsLargeScreen = mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600; + final Rect bounds = windowMetrics.getBounds(); + Configuration config = mContext.getResources().getConfiguration(); + mIsLargeScreen = config.smallestScreenWidthDp >= 600; + if (mIsLargeScreen) { + float largestEdgeDp = Math.max(config.screenWidthDp, config.screenHeightDp); + mIsSmallTablet = largestEdgeDp < 960; + } else { + mIsSmallTablet = false; + } if (BubbleDebugConfig.DEBUG_POSITIONER) { Log.w(TAG, "update positioner:" + " rotation: " + mRotation + " insets: " + insets + " isLargeScreen: " + mIsLargeScreen - + " bounds: " + windowMetrics.getBounds() + + " isSmallTablet: " + mIsSmallTablet + + " bounds: " + bounds + " showingInTaskbar: " + mShowingInTaskbar); } - updateInternal(mRotation, insets, windowMetrics.getBounds()); + updateInternal(mRotation, insets, bounds); } /** @@ -172,11 +187,31 @@ public class BubblePositioner { mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); - mExpandedViewLargeScreenWidth = (int) (bounds.width() - * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT); - mExpandedViewLargeScreenInset = mIsLargeScreen - ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2 - : mExpandedViewPadding; + if (mIsSmallTablet) { + mExpandedViewLargeScreenWidth = (int) (bounds.width() + * EXPANDED_VIEW_SMALL_TABLET_WIDTH_PERCENT); + } else { + mExpandedViewLargeScreenWidth = isLandscape() + ? (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_LANDSCAPE_WIDTH_PERCENT) + : (int) (bounds.width() * EXPANDED_VIEW_LARGE_SCREEN_PORTRAIT_WIDTH_PERCENT); + } + if (mIsLargeScreen) { + if (isLandscape() && !mIsSmallTablet) { + mExpandedViewLargeScreenInsetClosestEdge = res.getDimensionPixelSize( + R.dimen.bubble_expanded_view_largescreen_landscape_padding); + mExpandedViewLargeScreenInsetFurthestEdge = bounds.width() + - mExpandedViewLargeScreenInsetClosestEdge + - mExpandedViewLargeScreenWidth; + } else { + final int centeredInset = (bounds.width() - mExpandedViewLargeScreenWidth) / 2; + mExpandedViewLargeScreenInsetClosestEdge = centeredInset; + mExpandedViewLargeScreenInsetFurthestEdge = centeredInset; + } + } else { + mExpandedViewLargeScreenInsetClosestEdge = mExpandedViewPadding; + mExpandedViewLargeScreenInsetFurthestEdge = mExpandedViewPadding; + } + mOverflowWidth = mIsLargeScreen ? mExpandedViewLargeScreenWidth : res.getDimensionPixelSize( @@ -328,15 +363,18 @@ public class BubblePositioner { public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) { final int pointerTotalHeight = mPointerHeight - mPointerOverlap; if (mIsLargeScreen) { + // Note: + // If we're in portrait OR if we're a small tablet, then the two insets values will + // be equal. If we're landscape and a large tablet, the two values will be different. // [left, top, right, bottom] mPaddings[0] = onLeft - ? mExpandedViewLargeScreenInset - pointerTotalHeight - : mExpandedViewLargeScreenInset; + ? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight + : mExpandedViewLargeScreenInsetFurthestEdge; mPaddings[1] = 0; mPaddings[2] = onLeft - ? mExpandedViewLargeScreenInset - : mExpandedViewLargeScreenInset - pointerTotalHeight; - // Overflow doesn't show manage button / get padding from it so add padding here for it + ? mExpandedViewLargeScreenInsetFurthestEdge + : mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight; + // Overflow doesn't show manage button / get padding from it so add padding here mPaddings[3] = isOverflow ? mExpandedViewPadding : 0; return mPaddings; } else { @@ -494,12 +532,13 @@ public class BubblePositioner { float x; float y; if (showBubblesVertically()) { + int inset = mExpandedViewLargeScreenInsetClosestEdge; y = rowStart + positionInRow; int left = mIsLargeScreen - ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize + ? inset - mExpandedViewPadding - mBubbleSize : mPositionRect.left; int right = mIsLargeScreen - ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding + ? mPositionRect.right - inset + mExpandedViewPadding : mPositionRect.right - mBubbleSize; x = state.onLeft ? left diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt index 74672a336161..063dac3d4109 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt @@ -16,11 +16,16 @@ package com.android.wm.shell.bubbles +import android.animation.ObjectAnimator import android.content.Context -import android.graphics.drawable.TransitionDrawable +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.util.IntProperty import android.view.Gravity import android.view.View import android.view.ViewGroup +import android.view.WindowManager +import android.view.WindowInsets import android.widget.FrameLayout import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY @@ -28,8 +33,6 @@ import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW import com.android.wm.shell.R import com.android.wm.shell.animation.PhysicsAnimator import com.android.wm.shell.common.DismissCircleView -import android.view.WindowInsets -import android.view.WindowManager /* * View that handles interactions between DismissCircleView and BubbleStackView. @@ -41,9 +44,21 @@ class DismissView(context: Context) : FrameLayout(context) { private val animator = PhysicsAnimator.getInstance(circle) private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY) - private val DISMISS_SCRIM_FADE_MS = 200 + private val DISMISS_SCRIM_FADE_MS = 200L private var wm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + private var gradientDrawable = createGradient() + + private val GRADIENT_ALPHA: IntProperty<GradientDrawable> = + object : IntProperty<GradientDrawable>("alpha") { + override fun setValue(d: GradientDrawable, percent: Int) { + d.alpha = percent + } + override fun get(d: GradientDrawable): Int { + return d.alpha + } + } + init { setLayoutParams(LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, @@ -53,8 +68,7 @@ class DismissView(context: Context) : FrameLayout(context) { setClipToPadding(false) setClipChildren(false) setVisibility(View.INVISIBLE) - setBackgroundResource( - R.drawable.floating_dismiss_gradient_transition) + setBackgroundDrawable(gradientDrawable) val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size) addView(circle, LayoutParams(targetSize, targetSize, @@ -71,7 +85,11 @@ class DismissView(context: Context) : FrameLayout(context) { if (isShowing) return isShowing = true setVisibility(View.VISIBLE) - (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS) + val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, + gradientDrawable.alpha, 255) + alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS) + alphaAnim.start() + animator.cancel() animator .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring) @@ -85,7 +103,10 @@ class DismissView(context: Context) : FrameLayout(context) { fun hide() { if (!isShowing) return isShowing = false - (getBackground() as TransitionDrawable).reverseTransition(DISMISS_SCRIM_FADE_MS) + val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, + gradientDrawable.alpha, 0) + alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS) + alphaAnim.start() animator .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(), spring) @@ -93,6 +114,13 @@ class DismissView(context: Context) : FrameLayout(context) { .start() } + /** + * Cancels the animator for the dismiss target. + */ + fun cancelAnimators() { + animator.cancel() + } + fun updateResources() { updatePadding() layoutParams.height = resources.getDimensionPixelSize( @@ -104,6 +132,20 @@ class DismissView(context: Context) : FrameLayout(context) { circle.requestLayout() } + private fun createGradient(): GradientDrawable { + val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900) + val alpha = 0.7f * 255 + val gradientColorWithAlpha = Color.argb(alpha.toInt(), + Color.red(gradientColor), + Color.green(gradientColor), + Color.blue(gradientColor)) + val gd = GradientDrawable( + GradientDrawable.Orientation.BOTTOM_TOP, + intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT)) + gd.setAlpha(0) + return gd + } + private fun updatePadding() { val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets() val navInset = insets.getInsetsIgnoringVisibility( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index f7c92fed5522..8a482fbfa1c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -144,21 +144,42 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return Math.max(dividerInset, radius); } - /** Gets bounds of the primary split. */ + /** Gets bounds of the primary split with screen based coordinate. */ public Rect getBounds1() { return new Rect(mBounds1); } - /** Gets bounds of the secondary split. */ + /** Gets bounds of the primary split with parent based coordinate. */ + public Rect getRefBounds1() { + Rect outBounds = getBounds1(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + + /** Gets bounds of the secondary split with screen based coordinate. */ public Rect getBounds2() { return new Rect(mBounds2); } - /** Gets bounds of divider window. */ + /** Gets bounds of the secondary split with parent based coordinate. */ + public Rect getRefBounds2() { + final Rect outBounds = getBounds2(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + + /** Gets bounds of divider window with screen based coordinate. */ public Rect getDividerBounds() { return new Rect(mDividerBounds); } + /** Gets bounds of divider window with parent based coordinate. */ + public Rect getRefDividerBounds() { + final Rect outBounds = getDividerBounds(); + outBounds.offset(-mRootBounds.left, -mRootBounds.top); + return outBounds; + } + /** Returns leash of the current divider bar. */ @Nullable public SurfaceControl getDividerLeash() { @@ -452,14 +473,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { - t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top); + mTempRect.set(getRefDividerBounds()); + t.setPosition(dividerLeash, mTempRect.left, mTempRect.top); // Resets layer of divider bar to make sure it is always on top. t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); } - t.setPosition(leash1, mBounds1.left, mBounds1.top) - .setWindowCrop(leash1, mBounds1.width(), mBounds1.height()); - t.setPosition(leash2, mBounds2.left, mBounds2.top) - .setWindowCrop(leash2, mBounds2.width(), mBounds2.height()); + mTempRect.set(getRefBounds1()); + t.setPosition(leash1, mTempRect.left, mTempRect.top) + .setWindowCrop(leash1, mTempRect.width(), mTempRect.height()); + mTempRect.set(getRefBounds2()); + t.setPosition(leash2, mTempRect.left, mTempRect.top) + .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); if (mImePositionProcessor.adjustSurfaceLayoutForIme( t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index e29dde2726e3..797df413d262 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -32,6 +32,7 @@ import android.util.Size; import android.util.TypedValue; import android.view.Gravity; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; @@ -76,15 +77,15 @@ public class PipBoundsAlgorithm { protected void reloadResources(Context context) { final Resources res = context.getResources(); mDefaultAspectRatio = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); + R.dimen.config_pictureInPictureDefaultAspectRatio); mDefaultStackGravity = res.getInteger( - com.android.internal.R.integer.config_defaultPictureInPictureGravity); + R.integer.config_defaultPictureInPictureGravity); mDefaultMinSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task); + R.dimen.default_minimal_size_pip_resizable_task); mOverridableMinSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.overridable_minimal_size_pip_resizable_task); + R.dimen.overridable_minimal_size_pip_resizable_task); final String screenEdgeInsetsDpString = res.getString( - com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets); + R.string.config_defaultPictureInPictureScreenEdgeInsets); final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() ? Size.parseSize(screenEdgeInsetsDpString) : null; @@ -96,9 +97,9 @@ public class PipBoundsAlgorithm { mMaxAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); mDefaultSizePercent = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); + R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); + R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 180e3fb48c9d..d7322ce7beda 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -138,8 +138,8 @@ public class PipSurfaceTransactionHelper { // destination are different. final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH; final Rect crop = mTmpDestinationRect; - crop.set(0, 0, Transitions.ENABLE_SHELL_TRANSITIONS ? destH - : destW, Transitions.ENABLE_SHELL_TRANSITIONS ? destW : destH); + crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH + : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH); // Inverse scale for crop to fit in screen coordinates. crop.scale(1 / scale); crop.offset(insets.left, insets.top); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 60aac6806623..91615fe05417 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -97,7 +97,7 @@ public class PipTransition extends PipTransitionController { * meaningful if {@link #mInFixedRotation} is true. */ @Surface.Rotation - private int mFixedRotation; + private int mEndFixedRotation; /** Whether the PIP window has fade out for fixed rotation. */ private boolean mHasFadeOut; @@ -153,7 +153,7 @@ public class PipTransition extends PipTransitionController { final TransitionInfo.Change currentPipChange = findCurrentPipChange(info); final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); mInFixedRotation = fixedRotationChange != null; - mFixedRotation = mInFixedRotation + mEndFixedRotation = mInFixedRotation ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED; @@ -257,7 +257,7 @@ public class PipTransition extends PipTransitionController { final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo(); if (taskInfo != null) { startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(), - new Rect(mExitDestinationBounds)); + new Rect(mExitDestinationBounds), Surface.ROTATION_0); } mExitDestinationBounds.setEmpty(); mCurrentPipTaskToken = null; @@ -332,30 +332,31 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change pipChange) { - TransitionInfo.Change displayRotationChange = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getMode() == TRANSIT_CHANGE - && (change.getFlags() & FLAG_IS_DISPLAY) != 0 - && change.getStartRotation() != change.getEndRotation()) { - displayRotationChange = change; - break; - } - } - - if (displayRotationChange != null) { - // Exiting PIP to fullscreen with orientation change. - startExpandAndRotationAnimation(info, startTransaction, finishTransaction, - finishCallback, displayRotationChange, pipChange); - return; - } - - // When there is no rotation, we can simply expand the PIP window. mFinishCallback = (wct, wctCB) -> { mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); finishCallback.onTransitionFinished(wct, wctCB); }; + // Check if it is Shell rotation. + if (Transitions.SHELL_TRANSITIONS_ROTATION) { + TransitionInfo.Change displayRotationChange = null; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getMode() == TRANSIT_CHANGE + && (change.getFlags() & FLAG_IS_DISPLAY) != 0 + && change.getStartRotation() != change.getEndRotation()) { + displayRotationChange = change; + break; + } + } + if (displayRotationChange != null) { + // Exiting PIP to fullscreen with orientation change. + startExpandAndRotationAnimation(info, startTransaction, finishTransaction, + displayRotationChange, pipChange); + return; + } + } + // Set the initial frame as scaling the end to the start. final Rect destinationBounds = new Rect(pipChange.getEndAbsBounds()); final Point offset = pipChange.getEndRelOffset(); @@ -364,13 +365,41 @@ public class PipTransition extends PipTransitionController { mSurfaceTransactionHelper.scale(startTransaction, pipChange.getLeash(), destinationBounds, mPipBoundsState.getBounds()); startTransaction.apply(); - startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds); + + // Check if it is fixed rotation. + final int rotationDelta; + if (mInFixedRotation) { + final int startRotation = pipChange.getStartRotation(); + final int endRotation = mEndFixedRotation; + rotationDelta = deltaRotation(startRotation, endRotation); + final Rect endBounds = new Rect(destinationBounds); + + // Set the end frame since the display won't rotate until fixed rotation is finished + // in the next display change transition. + rotateBounds(endBounds, destinationBounds, rotationDelta); + final int degree, x, y; + if (rotationDelta == ROTATION_90) { + degree = 90; + x = destinationBounds.right; + y = destinationBounds.top; + } else { + degree = -90; + x = destinationBounds.left; + y = destinationBounds.bottom; + } + mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction, + pipChange.getLeash(), endBounds, endBounds, new Rect(), degree, x, y, + true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */); + } else { + rotationDelta = Surface.ROTATION_0; + } + startExpandAnimation(pipChange.getTaskInfo(), pipChange.getLeash(), destinationBounds, + rotationDelta); } private void startExpandAndRotationAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TransitionInfo.Change displayRotationChange, @NonNull TransitionInfo.Change pipChange) { final int rotateDelta = deltaRotation(displayRotationChange.getStartRotation(), @@ -380,11 +409,6 @@ public class PipTransition extends PipTransitionController { final CounterRotatorHelper rotator = new CounterRotatorHelper(); rotator.handleClosingChanges(info, startTransaction, displayRotationChange); - mFinishCallback = (wct, wctCB) -> { - mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo()); - finishCallback.onTransitionFinished(wct, wctCB); - }; - // Get the start bounds in new orientation. final Rect startBounds = new Rect(pipChange.getStartAbsBounds()); rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta); @@ -425,12 +449,11 @@ public class PipTransition extends PipTransitionController { } private void startExpandAnimation(final TaskInfo taskInfo, final SurfaceControl leash, - final Rect destinationBounds) { - PipAnimationController.PipTransitionAnimator animator = + final Rect destinationBounds, final int rotationDelta) { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(), destinationBounds, null, - TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, Surface.ROTATION_0); - + TRANSITION_DIRECTION_LEAVE_PIP, 0 /* startingAngle */, rotationDelta); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -526,7 +549,7 @@ public class PipTransition extends PipTransitionController { mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; - final int endRotation = mInFixedRotation ? mFixedRotation : enterPip.getEndRotation(); + final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation(); return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), startTransaction, finishTransaction, enterPip.getStartRotation(), endRotation); @@ -545,8 +568,8 @@ public class PipTransition extends PipTransitionController { taskInfo.pictureInPictureParams, currentBounds); if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) { // Need to get the bounds of new rotation in old rotation for fixed rotation, - sourceHintRect = computeRotatedBounds(rotationDelta, startRotation, endRotation, - taskInfo, TRANSITION_DIRECTION_TO_PIP, destinationBounds, sourceHintRect); + computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, + destinationBounds, sourceHintRect); } PipAnimationController.PipTransitionAnimator animator; // Set corner radius for entering pip. @@ -583,8 +606,6 @@ public class PipTransition extends PipTransitionController { startTransaction.setMatrix(leash, tmpTransform, new float[9]); } if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - // Reverse the rotation for Shell transition animation. - rotationDelta = deltaRotation(rotationDelta, 0); animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, rotationDelta); @@ -617,33 +638,22 @@ public class PipTransition extends PipTransitionController { return true; } - /** Computes destination bounds in old rotation and returns source hint rect if available. */ - @Nullable - private Rect computeRotatedBounds(int rotationDelta, int startRotation, int endRotation, - TaskInfo taskInfo, int direction, Rect outDestinationBounds, - @Nullable Rect sourceHintRect) { - if (direction == TRANSITION_DIRECTION_TO_PIP) { - mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); - final Rect displayBounds = mPipBoundsState.getDisplayBounds(); - outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); - // Transform the destination bounds to current display coordinates. - rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); - // When entering PiP (from button navigation mode), adjust the source rect hint by - // display cutout if applicable. - if (sourceHintRect != null && taskInfo.displayCutoutInsets != null) { - if (rotationDelta == Surface.ROTATION_270) { - sourceHintRect.offset(taskInfo.displayCutoutInsets.left, - taskInfo.displayCutoutInsets.top); - } + /** Computes destination bounds in old rotation and updates source hint rect if available. */ + private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation, + TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) { + mPipBoundsState.getDisplayLayout().rotateTo(mContext.getResources(), endRotation); + final Rect displayBounds = mPipBoundsState.getDisplayBounds(); + outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); + // Transform the destination bounds to current display coordinates. + rotateBounds(outDestinationBounds, displayBounds, endRotation, startRotation); + // When entering PiP (from button navigation mode), adjust the source rect hint by + // display cutout if applicable. + if (outSourceHintRect != null && taskInfo.displayCutoutInsets != null) { + if (rotationDelta == Surface.ROTATION_270) { + outSourceHintRect.offset(taskInfo.displayCutoutInsets.left, + taskInfo.displayCutoutInsets.top); } - } else if (direction == TRANSITION_DIRECTION_LEAVE_PIP) { - final Rect rotatedDestinationBounds = new Rect(outDestinationBounds); - rotateBounds(rotatedDestinationBounds, mPipBoundsState.getDisplayBounds(), - rotationDelta); - return PipBoundsAlgorithm.getValidSourceHintRect(taskInfo.pictureInPictureParams, - rotatedDestinationBounds); - } - return sourceHintRect; + } } private void startExitToSplitAnimation(TransitionInfo info, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index 915c5939c34b..3115f8afde3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -20,27 +20,20 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M import android.content.Context; import android.content.res.Resources; -import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.TransitionDrawable; -import android.view.Gravity; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; -import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; -import android.widget.FrameLayout; import androidx.annotation.NonNull; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.R; -import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.DismissView; import com.android.wm.shell.common.DismissCircleView; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -56,9 +49,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen /* The multiplier to apply scale the target size by when applying the magnetic field radius */ private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f; - /** Duration of the dismiss scrim fading in/out. */ - private static final int DISMISS_TRANSITION_DURATION_MS = 200; - /** * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move * PIP. @@ -69,7 +59,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen * Container for the dismiss circle, so that it can be animated within the container via * translation rather than within the WindowManager via slow layout animations. */ - private ViewGroup mTargetViewContainer; + private DismissView mTargetViewContainer; /** Circle view used to render the dismiss target. */ private DismissCircleView mTargetView; @@ -79,16 +69,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen */ private MagnetizedObject.MagneticTarget mMagneticTarget; - /** - * PhysicsAnimator instance for animating the dismiss target in/out. - */ - private PhysicsAnimator<View> mMagneticTargetAnimator; - - /** Default configuration to use for springing the dismiss target in/out. */ - private final PhysicsAnimator.SpringConfig mTargetSpringConfig = - new PhysicsAnimator.SpringConfig( - SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - // Allow dragging the PIP to a location to close it private boolean mEnableDismissDragToEdge; @@ -125,12 +105,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen cleanUpDismissTarget(); } - mTargetView = new DismissCircleView(mContext); - mTargetViewContainer = new FrameLayout(mContext); - mTargetViewContainer.setBackgroundDrawable( - mContext.getDrawable(R.drawable.floating_dismiss_gradient_transition)); - mTargetViewContainer.setClipChildren(false); - mTargetViewContainer.addView(mTargetView); + mTargetViewContainer = new DismissView(mContext); + mTargetView = mTargetViewContainer.getCircle(); mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> { if (!windowInsets.equals(mWindowInsets)) { mWindowInsets = windowInsets; @@ -187,7 +163,6 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen } }); - mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); } @Override @@ -213,19 +188,13 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen if (mTargetView == null) { return; } + if (mTargetViewContainer != null) { + mTargetViewContainer.updateResources(); + } final Resources res = mContext.getResources(); mTargetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size); mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); - final WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets(); - final Insets navInset = insets.getInsetsIgnoringVisibility( - WindowInsets.Type.navigationBars()); - final FrameLayout.LayoutParams newParams = - new FrameLayout.LayoutParams(mTargetSize, mTargetSize); - newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; - newParams.bottomMargin = navInset.bottom + mContext.getResources().getDimensionPixelSize( - R.dimen.floating_dismiss_bottom_margin); - mTargetView.setLayoutParams(newParams); // Set the magnetic field radius equal to the target size from the center of the target setMagneticFieldRadiusPercent(mMagneticFieldRadiusPercent); @@ -261,7 +230,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */ public void createOrUpdateDismissTarget() { if (!mTargetViewContainer.isAttachedToWindow()) { - mMagneticTargetAnimator.cancel(); + mTargetViewContainer.cancelAnimators(); mTargetViewContainer.setVisibility(View.INVISIBLE); mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this); @@ -312,18 +281,8 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen createOrUpdateDismissTarget(); if (mTargetViewContainer.getVisibility() != View.VISIBLE) { - mTargetView.setTranslationY(mTargetViewContainer.getHeight()); - mTargetViewContainer.setVisibility(View.VISIBLE); mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this); - - // Cancel in case we were in the middle of animating it out. - mMagneticTargetAnimator.cancel(); - mMagneticTargetAnimator - .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig) - .start(); - - ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition( - DISMISS_TRANSITION_DURATION_MS); + mTargetViewContainer.show(); } } @@ -332,16 +291,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen if (!mEnableDismissDragToEdge) { return; } - - mMagneticTargetAnimator - .spring(DynamicAnimation.TRANSLATION_Y, - mTargetViewContainer.getHeight(), - mTargetSpringConfig) - .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) - .start(); - - ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition( - DISMISS_TRANSITION_DURATION_MS); + mTargetViewContainer.hide(); } /** 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 69d6c9e0c3bd..32ebe2d6aecf 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 @@ -33,6 +33,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; +import android.view.WindowManagerGlobal; import androidx.annotation.Nullable; @@ -143,7 +144,6 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), 0, SHELL_ROOT_LAYER_PIP); - mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView)); } @Override @@ -164,6 +164,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis t.setPosition(menuSurfaceControl, menuBounds.left, menuBounds.top); t.apply(); } + grantPipMenuFocus(true); mPipMenuView.show(mInMoveMode, mDelegate.getPipGravity()); } } @@ -194,8 +195,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis if (DEBUG) Log.d(TAG, "hideMenu()"); } - mPipMenuView.hide(mInMoveMode); + mPipMenuView.hide(); if (!mInMoveMode) { + grantPipMenuFocus(false); mDelegate.closeMenu(); } } @@ -453,4 +455,15 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis void closePip(); } + + private void grantPipMenuFocus(boolean grantFocus) { + if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); + + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, + mSystemWindows.getFocusGrantToken(mPipMenuView), grantFocus); + } catch (Exception e) { + Log.e(TAG, "Unable to update focus", e); + } + } } 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 773e9bfa8977..3090139f6db9 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 @@ -30,7 +30,6 @@ import android.app.RemoteAction; import android.content.Context; import android.graphics.Rect; import android.os.Handler; -import android.os.IBinder; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; @@ -39,7 +38,6 @@ import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; -import android.view.WindowManagerGlobal; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -72,7 +70,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private final ImageView mArrowRight; private final ImageView mArrowDown; private final ImageView mArrowLeft; - private IBinder mFocusGrantToken = null; private final ViewGroup mScrollView; private final ViewGroup mHorizontalScrollView; @@ -152,10 +149,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { mListener = listener; } - void setFocusGrantToken(IBinder token) { - mFocusGrantToken = token; - } - void setExpandedModeEnabled(boolean enabled) { mExpandButton.setVisibility(enabled ? VISIBLE : GONE); } @@ -170,8 +163,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { void show(boolean inMoveMode, int gravity) { if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode); - grantWindowFocus(true); - if (inMoveMode) { showMovementHints(gravity); } else { @@ -180,15 +171,11 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { animateAlphaTo(1, mMenuFrameView); } - void hide(boolean isInMoveMode) { + void hide() { if (DEBUG) Log.d(TAG, "hide()"); animateAlphaTo(0, mActionButtonsContainer); animateAlphaTo(0, mMenuFrameView); hideMovementHints(); - - if (!isInMoveMode) { - grantWindowFocus(false); - } } private void animateAlphaTo(float alpha, View view) { @@ -217,17 +204,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { || mArrowLeft.getAlpha() != 0f; } - private void grantWindowFocus(boolean grantFocus) { - if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")"); - - try { - WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */, - mFocusGrantToken, grantFocus); - } catch (Exception e) { - Log.e(TAG, "Unable to update focus", e); - } - } - void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) { if (DEBUG) Log.d(TAG, "setAdditionalActions()"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 81dacdb753a7..76641f0e6c21 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -979,7 +979,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, t.setAlpha(dividerLeash, 1); t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); t.setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top); + mSplitLayout.getRefDividerBounds().left, + mSplitLayout.getRefDividerBounds().top); } else { t.hide(dividerLeash); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 5af15300d10b..4bc5850cd293 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -26,6 +26,7 @@ import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; +import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE; import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION; import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE; @@ -156,9 +157,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - boolean isDrawable = intent.getBooleanExtra( - EXTRA_RESOURCE_TYPE_DRAWABLE, /* default= */ false); - if (!isDrawable) { + if (intent.getIntExtra(EXTRA_RESOURCE_TYPE, /* default= */ -1) + != EXTRA_RESOURCE_TYPE_DRAWABLE) { return; } updateEnterpriseThumbnailDrawable(); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index 654fa4ec9446..8d542c8ec9e6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -56,13 +56,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { .sendBroadcast(createIntentWithAction(broadcastAction)) } - fun requestOrientationForPip(orientation: Int) { - instrumentation.context.sendBroadcast( - createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION) - .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString()) - ) - } - companion object { // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index d65388bd8cc5..f7384e742a04 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -25,11 +25,14 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group4 import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled +import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.testapp.Components import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder @@ -39,7 +42,7 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip with orientation changes. + * Test exiting Pip with orientation changes. * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest` */ @RequiresDevice @@ -61,30 +64,41 @@ open class SetRequestedOrientationWhilePinnedTest( override val transition: FlickerBuilder.() -> Unit get() = { - setupAndTeardown(this) - setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + } eachRun { - // Launch the PiP activity fixed as landscape + // Launch the PiP activity fixed as landscape. pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), - EXTRA_ENTER_PIP to "true")) + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) + // Enter PiP. + broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP) + wmHelper.waitPipShown() + wmHelper.waitForRotation(Surface.ROTATION_0) + wmHelper.waitForAppTransitionIdle() + // System bar may fade out during fixed rotation. + wmHelper.waitForNavBarStatusBarVisible() } } teardown { eachRun { pipApp.exit(wmHelper) + setRotation(Surface.ROTATION_0) + } + test { + removeAllTasksButHome() } } transitions { - // Request that the orientation is set to landscape - broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) - - // Launch the activity back into fullscreen and - // ensure that it is now in landscape + // Launch the activity back into fullscreen and ensure that it is now in landscape pipApp.launchViaIntent(wmHelper) wmHelper.waitForFullScreenApp(pipApp.component) wmHelper.waitForRotation(Surface.ROTATION_90) + wmHelper.waitForAppTransitionIdle() + // System bar may fade out during fixed rotation. + wmHelper.waitForNavBarStatusBarVisible() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt index 36e28049864f..8d764a8d0e69 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt @@ -29,6 +29,10 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized +/** + * Test exiting Pip with orientation changes. + * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTestShellTransit` + */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 90f898aa09da..0059846c6055 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -29,6 +29,7 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; @@ -72,16 +73,16 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void initializeMockResources() { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + R.dimen.config_pictureInPictureDefaultAspectRatio, DEFAULT_ASPECT_RATIO); res.addOverride( - com.android.internal.R.integer.config_defaultPictureInPictureGravity, + R.integer.config_defaultPictureInPictureGravity, Gravity.END | Gravity.BOTTOM); res.addOverride( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, + R.dimen.default_minimal_size_pip_resizable_task, DEFAULT_MIN_EDGE_SIZE); res.addOverride( - com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, + R.string.config_defaultPictureInPictureScreenEdgeInsets, "16x16"); res.addOverride( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio, @@ -107,7 +108,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { public void onConfigurationChanged_reloadResources() { final float newDefaultAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2; final TestableResources res = mContext.getOrCreateTestableResources(); - res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + res.addOverride(R.dimen.config_pictureInPictureDefaultAspectRatio, newDefaultAspectRatio); mPipBoundsAlgorithm.onConfigurationChanged(mContext); @@ -463,7 +464,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void overrideDefaultAspectRatio(float aspectRatio) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + R.dimen.config_pictureInPictureDefaultAspectRatio, aspectRatio); mPipBoundsAlgorithm.onConfigurationChanged(mContext); } @@ -471,7 +472,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void overrideDefaultStackGravity(int stackGravity) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.integer.config_defaultPictureInPictureGravity, + R.integer.config_defaultPictureInPictureGravity, stackGravity); mPipBoundsAlgorithm.onConfigurationChanged(mContext); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index dda1a8295e51..85527c81ad9e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -51,6 +52,7 @@ public class SplitTestUtils { final SurfaceControl leash = createMockSurface(); SplitLayout out = mock(SplitLayout.class); doReturn(dividerBounds).when(out).getDividerBounds(); + doReturn(dividerBounds).when(out).getRefDividerBounds(); doReturn(leash).when(out).getDividerLeash(); return out; } |