diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt new file mode 100644 index 0000000000000..fd000ee1d24f7 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2023 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.common.pip + +import android.content.Context +import android.content.res.Resources +import android.graphics.PointF +import android.util.Size +import com.android.wm.shell.R +import com.android.wm.shell.pip.PipDisplayLayoutState + +class LegacySizeSpecSource( + private val context: Context, + private val pipDisplayLayoutState: PipDisplayLayoutState +) : SizeSpecSource { + + private var mDefaultMinSize = 0 + /** The absolute minimum an overridden size's edge can be */ + private var mOverridableMinSize = 0 + /** The preferred minimum (and default minimum) size specified by apps. */ + private var mOverrideMinSize: Size? = null + + private var mDefaultSizePercent = 0f + private var mMinimumSizePercent = 0f + private var mMaxAspectRatioForMinSize = 0f + private var mMinAspectRatioForMinSize = 0f + + init { + reloadResources() + } + + private fun reloadResources() { + val res: Resources = context.getResources() + + mDefaultMinSize = res.getDimensionPixelSize( + R.dimen.default_minimal_size_pip_resizable_task) + mOverridableMinSize = res.getDimensionPixelSize( + R.dimen.overridable_minimal_size_pip_resizable_task) + + mDefaultSizePercent = res.getFloat(R.dimen.config_pictureInPictureDefaultSizePercent) + mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1) + + mMaxAspectRatioForMinSize = res.getFloat( + R.dimen.config_pictureInPictureAspectRatioLimitForMinSize) + mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize + } + + override fun onConfigurationChanged() { + reloadResources() + } + + override fun getMaxSize(aspectRatio: Float): Size { + val insetBounds = pipDisplayLayoutState.insetBounds + + val shorterLength: Int = Math.min(getDisplayBounds().width(), + getDisplayBounds().height()) + val totalHorizontalPadding: Int = (insetBounds.left + + (getDisplayBounds().width() - insetBounds.right)) + val totalVerticalPadding: Int = (insetBounds.top + + (getDisplayBounds().height() - insetBounds.bottom)) + + return if (aspectRatio > 1f) { + val maxWidth = Math.max(getDefaultSize(aspectRatio).width, + shorterLength - totalHorizontalPadding) + val maxHeight = (maxWidth / aspectRatio).toInt() + Size(maxWidth, maxHeight) + } else { + val maxHeight = Math.max(getDefaultSize(aspectRatio).height, + shorterLength - totalVerticalPadding) + val maxWidth = (maxHeight * aspectRatio).toInt() + Size(maxWidth, maxHeight) + } + } + + override fun getDefaultSize(aspectRatio: Float): Size { + if (mOverrideMinSize != null) { + return getMinSize(aspectRatio) + } + val smallestDisplaySize: Int = Math.min(getDisplayBounds().width(), + getDisplayBounds().height()) + val minSize = Math.max(getMinEdgeSize().toFloat(), + smallestDisplaySize * mDefaultSizePercent).toInt() + val width: Int + val height: Int + if (aspectRatio <= mMinAspectRatioForMinSize || + aspectRatio > mMaxAspectRatioForMinSize) { + // Beyond these points, we can just use the min size as the shorter edge + if (aspectRatio <= 1) { + // Portrait, width is the minimum size + width = minSize + height = Math.round(width / aspectRatio) + } else { + // Landscape, height is the minimum size + height = minSize + width = Math.round(height * aspectRatio) + } + } else { + // Within these points, ensure that the bounds fit within the radius of the limits + // at the points + val widthAtMaxAspectRatioForMinSize: Float = mMaxAspectRatioForMinSize * minSize + val radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize.toFloat()) + height = Math.round(Math.sqrt((radius * radius / + (aspectRatio * aspectRatio + 1)).toDouble())).toInt() + width = Math.round(height * aspectRatio) + } + return Size(width, height) + } + + override fun getMinSize(aspectRatio: Float): Size { + if (mOverrideMinSize != null) { + return adjustOverrideMinSizeToAspectRatio(aspectRatio)!! + } + val shorterLength: Int = Math.min(getDisplayBounds().width(), + getDisplayBounds().height()) + val minWidth: Int + val minHeight: Int + if (aspectRatio > 1f) { + minWidth = Math.min(getDefaultSize(aspectRatio).width.toFloat(), + shorterLength * mMinimumSizePercent).toInt() + minHeight = (minWidth / aspectRatio).toInt() + } else { + minHeight = Math.min(getDefaultSize(aspectRatio).height.toFloat(), + shorterLength * mMinimumSizePercent).toInt() + minWidth = (minHeight * aspectRatio).toInt() + } + return Size(minWidth, minHeight) + } + + override fun getSizeForAspectRatio(size: Size, aspectRatio: Float): Size { + val smallestSize = Math.min(size.width, size.height) + val minSize = Math.max(getMinEdgeSize(), smallestSize) + val width: Int + val height: Int + if (aspectRatio <= 1) { + // Portrait, width is the minimum size. + width = minSize + height = Math.round(width / aspectRatio) + } else { + // Landscape, height is the minimum size + height = minSize + width = Math.round(height * aspectRatio) + } + return Size(width, height) + } + + private fun getDisplayBounds() = pipDisplayLayoutState.displayBounds + + /** Sets the preferred size of PIP as specified by the activity in PIP mode. */ + override fun setOverrideMinSize(overrideMinSize: Size?) { + mOverrideMinSize = overrideMinSize + } + + /** Returns the preferred minimal size specified by the activity in PIP. */ + override fun getOverrideMinSize(): Size? { + val overrideMinSize = mOverrideMinSize ?: return null + return if (overrideMinSize.width < mOverridableMinSize || + overrideMinSize.height < mOverridableMinSize) { + Size(mOverridableMinSize, mOverridableMinSize) + } else { + overrideMinSize + } + } + + private fun getMinEdgeSize(): Int { + return if (mOverrideMinSize == null) mDefaultMinSize else getOverrideMinEdgeSize() + } + + /** + * Returns the adjusted overridden min size if it is set; otherwise, returns null. + * + * + * Overridden min size needs to be adjusted in its own way while making sure that the target + * aspect ratio is maintained + * + * @param aspectRatio target aspect ratio + */ + private fun adjustOverrideMinSizeToAspectRatio(aspectRatio: Float): Size? { + val size = getOverrideMinSize() ?: return null + val sizeAspectRatio = size.width / size.height.toFloat() + return if (sizeAspectRatio > aspectRatio) { + // Size is wider, fix the width and increase the height + Size(size.width, (size.width / aspectRatio).toInt()) + } else { + // Size is taller, fix the height and adjust the width. + Size((size.height * aspectRatio).toInt(), size.height) + } + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt new file mode 100644 index 0000000000000..c5630686f5282 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2023 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.common.pip + +import android.content.Context +import android.content.res.Resources +import android.os.SystemProperties +import android.util.Size +import com.android.wm.shell.R +import com.android.wm.shell.pip.PipDisplayLayoutState +import java.io.PrintWriter + +class PhoneSizeSpecSource( + private val context: Context, + private val pipDisplayLayoutState: PipDisplayLayoutState +) : SizeSpecSource { + private var DEFAULT_OPTIMIZED_ASPECT_RATIO = 9f / 16 + + private var mDefaultMinSize = 0 + /** The absolute minimum an overridden size's edge can be */ + private var mOverridableMinSize = 0 + /** The preferred minimum (and default minimum) size specified by apps. */ + private var mOverrideMinSize: Size? = null + + + /** Default and minimum percentages for the PIP size logic. */ + private val mDefaultSizePercent: Float + private val mMinimumSizePercent: Float + + /** Aspect ratio that the PIP size spec logic optimizes for. */ + private var mOptimizedAspectRatio = 0f + + init { + mDefaultSizePercent = SystemProperties + .get("com.android.wm.shell.pip.phone.def_percentage", "0.6").toFloat() + mMinimumSizePercent = SystemProperties + .get("com.android.wm.shell.pip.phone.min_percentage", "0.5").toFloat() + + reloadResources() + } + + private fun reloadResources() { + val res: Resources = context.getResources() + + mDefaultMinSize = res.getDimensionPixelSize( + R.dimen.default_minimal_size_pip_resizable_task) + mOverridableMinSize = res.getDimensionPixelSize( + R.dimen.overridable_minimal_size_pip_resizable_task) + + val requestedOptAspRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio) + // make sure the optimized aspect ratio is valid with a default value to fall back to + mOptimizedAspectRatio = if (requestedOptAspRatio > 1) { + DEFAULT_OPTIMIZED_ASPECT_RATIO + } else { + requestedOptAspRatio + } + } + + override fun onConfigurationChanged() { + reloadResources() + } + + /** + * Calculates the max size of PIP. + * + * Optimizes for 16:9 aspect ratios, making them take full length of shortest display edge. + * As aspect ratio approaches values close to 1:1, the logic does not let PIP occupy the + * whole screen. A linear function is used to calculate these sizes. + * + * @param aspectRatio aspect ratio of the PIP window + * @return dimensions of the max size of the PIP + */ + override fun getMaxSize(aspectRatio: Float): Size { + val insetBounds = pipDisplayLayoutState.insetBounds + val displayBounds = pipDisplayLayoutState.displayBounds + + val totalHorizontalPadding: Int = (insetBounds.left + + (displayBounds.width() - insetBounds.right)) + val totalVerticalPadding: Int = (insetBounds.top + + (displayBounds.height() - insetBounds.bottom)) + val shorterLength: Int = Math.min(displayBounds.width() - totalHorizontalPadding, + displayBounds.height() - totalVerticalPadding) + var maxWidth: Int + val maxHeight: Int + + // use the optimized max sizing logic only within a certain aspect ratio range + if (aspectRatio >= mOptimizedAspectRatio && aspectRatio <= 1 / mOptimizedAspectRatio) { + // this formula and its derivation is explained in b/198643358#comment16 + maxWidth = Math.round(mOptimizedAspectRatio * shorterLength + + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1 + aspectRatio)) + // make sure the max width doesn't go beyond shorter screen length after rounding + maxWidth = Math.min(maxWidth, shorterLength) + maxHeight = Math.round(maxWidth / aspectRatio) + } else { + if (aspectRatio > 1f) { + maxWidth = shorterLength + maxHeight = Math.round(maxWidth / aspectRatio) + } else { + maxHeight = shorterLength + maxWidth = Math.round(maxHeight * aspectRatio) + } + } + return Size(maxWidth, maxHeight) + } + + /** + * Decreases the dimensions by a percentage relative to max size to get default size. + * + * @param aspectRatio aspect ratio of the PIP window + * @return dimensions of the default size of the PIP + */ + override fun getDefaultSize(aspectRatio: Float): Size { + val minSize = getMinSize(aspectRatio) + if (mOverrideMinSize != null) { + return minSize + } + val maxSize = getMaxSize(aspectRatio) + val defaultWidth = Math.max(Math.round(maxSize.width * mDefaultSizePercent), + minSize.width) + val defaultHeight = Math.round(defaultWidth / aspectRatio) + return Size(defaultWidth, defaultHeight) + } + + /** + * Decreases the dimensions by a certain percentage relative to max size to get min size. + * + * @param aspectRatio aspect ratio of the PIP window + * @return dimensions of the min size of the PIP + */ + override fun getMinSize(aspectRatio: Float): Size { + // if there is an overridden min size provided, return that + if (mOverrideMinSize != null) { + return adjustOverrideMinSizeToAspectRatio(aspectRatio)!! + } + val maxSize = getMaxSize(aspectRatio) + var minWidth = Math.round(maxSize.width * mMinimumSizePercent) + var minHeight = Math.round(maxSize.height * mMinimumSizePercent) + + // make sure the calculated min size is not smaller than the allowed default min size + if (aspectRatio > 1f) { + minHeight = Math.max(minHeight, mDefaultMinSize) + minWidth = Math.round(minHeight * aspectRatio) + } else { + minWidth = Math.max(minWidth, mDefaultMinSize) + minHeight = Math.round(minWidth / aspectRatio) + } + return Size(minWidth, minHeight) + } + + /** + * Returns the size for target aspect ratio making sure new size conforms with the rules. + * + * + * Recalculates the dimensions such that the target aspect ratio is achieved, while + * maintaining the same maximum size to current size ratio. + * + * @param size current size + * @param aspectRatio target aspect ratio + */ + override fun getSizeForAspectRatio(size: Size, aspectRatio: Float): Size { + if (size == mOverrideMinSize) { + return adjustOverrideMinSizeToAspectRatio(aspectRatio)!! + } + + val currAspectRatio = size.width.toFloat() / size.height + + // getting the percentage of the max size that current size takes + val currentMaxSize = getMaxSize(currAspectRatio) + val currentPercent = size.width.toFloat() / currentMaxSize.width + + // getting the max size for the target aspect ratio + val updatedMaxSize = getMaxSize(aspectRatio) + var width = Math.round(updatedMaxSize.width * currentPercent) + var height = Math.round(updatedMaxSize.height * currentPercent) + + // adjust the dimensions if below allowed min edge size + val minEdgeSize = + if (mOverrideMinSize == null) mDefaultMinSize else getOverrideMinEdgeSize() + + if (width < minEdgeSize && aspectRatio <= 1) { + width = minEdgeSize + height = Math.round(width / aspectRatio) + } else if (height < minEdgeSize && aspectRatio > 1) { + height = minEdgeSize + width = Math.round(height * aspectRatio) + } + + // reduce the dimensions of the updated size to the calculated percentage + return Size(width, height) + } + + /** Sets the preferred size of PIP as specified by the activity in PIP mode. */ + override fun setOverrideMinSize(overrideMinSize: Size?) { + mOverrideMinSize = overrideMinSize + } + + /** Returns the preferred minimal size specified by the activity in PIP. */ + override fun getOverrideMinSize(): Size? { + val overrideMinSize = mOverrideMinSize ?: return null + return if (overrideMinSize.width < mOverridableMinSize || + overrideMinSize.height < mOverridableMinSize) { + Size(mOverridableMinSize, mOverridableMinSize) + } else { + overrideMinSize + } + } + + /** + * Returns the adjusted overridden min size if it is set; otherwise, returns null. + * + * + * Overridden min size needs to be adjusted in its own way while making sure that the target + * aspect ratio is maintained + * + * @param aspectRatio target aspect ratio + */ + private fun adjustOverrideMinSizeToAspectRatio(aspectRatio: Float): Size? { + val size = getOverrideMinSize() ?: return null + val sizeAspectRatio = size.width / size.height.toFloat() + return if (sizeAspectRatio > aspectRatio) { + // Size is wider, fix the width and increase the height + Size(size.width, (size.width / aspectRatio).toInt()) + } else { + // Size is taller, fix the height and adjust the width. + Size((size.height * aspectRatio).toInt(), size.height) + } + } + + override fun dump(pw: PrintWriter, prefix: String) { + val innerPrefix = "$prefix " + pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize) + pw.println(innerPrefix + "mOverridableMinSize=" + mOverridableMinSize) + pw.println(innerPrefix + "mDefaultMinSize=" + mDefaultMinSize) + pw.println(innerPrefix + "mDefaultSizePercent=" + mDefaultSizePercent) + pw.println(innerPrefix + "mMinimumSizePercent=" + mMinimumSizePercent) + pw.println(innerPrefix + "mOptimizedAspectRatio=" + mOptimizedAspectRatio) + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/SizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/SizeSpecSource.kt new file mode 100644 index 0000000000000..7b3b9efb0de05 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/SizeSpecSource.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 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.common.pip + +import android.util.Size +import java.io.PrintWriter + +interface SizeSpecSource { + /** Returns max size allowed for the PIP window */ + fun getMaxSize(aspectRatio: Float): Size + + /** Returns default size for the PIP window */ + fun getDefaultSize(aspectRatio: Float): Size + + /** Returns min size allowed for the PIP window */ + fun getMinSize(aspectRatio: Float): Size + + /** Returns the adjusted size based on current size and target aspect ratio */ + fun getSizeForAspectRatio(size: Size, aspectRatio: Float): Size + + /** Overrides the minimum pip size requested by the app */ + fun setOverrideMinSize(overrideMinSize: Size?) + + /** Returns the minimum pip size requested by the app */ + fun getOverrideMinSize(): Size? + + /** Returns the minimum edge size of the override minimum size, or 0 if not set. */ + fun getOverrideMinEdgeSize(): Int { + val overrideMinSize = getOverrideMinSize() ?: return 0 + return Math.min(overrideMinSize.width, overrideMinSize.height) + } + + fun onConfigurationChanged() {} + + /** Dumps the internal state of the size spec */ + fun dump(pw: PrintWriter, prefix: String) {} +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java index 16c39601e0bf8..54be90197e47b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java @@ -30,6 +30,8 @@ import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.onehanded.OneHandedController; @@ -53,7 +55,6 @@ import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipMotionHelper; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -87,7 +88,6 @@ public abstract class Pip1Module { PipBoundsAlgorithm pipBoundsAlgorithm, PhonePipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, - PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState, PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, @@ -110,8 +110,7 @@ public abstract class Pip1Module { context, shellInit, shellCommandHandler, shellController, displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm, - pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, - pipDisplayLayoutState, + pipKeepClearAlgorithm, pipBoundsState, pipDisplayLayoutState, pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController, windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, @@ -123,8 +122,8 @@ public abstract class Pip1Module { @WMSingleton @Provides static PipBoundsState providePipBoundsState(Context context, - PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) { - return new PipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState); + SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) { + return new PipBoundsState(context, sizeSpecSource, pipDisplayLayoutState); } @WMSingleton @@ -139,21 +138,14 @@ public abstract class Pip1Module { return new PhonePipKeepClearAlgorithm(context); } - @WMSingleton - @Provides - static PipSizeSpecHandler providePipSizeSpecHelper(Context context, - PipDisplayLayoutState pipDisplayLayoutState) { - return new PipSizeSpecHandler(context, pipDisplayLayoutState); - } - @WMSingleton @Provides static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context, PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm, PhonePipKeepClearAlgorithm pipKeepClearAlgorithm, - PipSizeSpecHandler pipSizeSpecHandler) { + PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) { return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm, - pipKeepClearAlgorithm, pipSizeSpecHandler); + pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource); } // Handler is used by Icon.loadDrawableAsync @@ -177,14 +169,14 @@ public abstract class Pip1Module { PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, - PipSizeSpecHandler pipSizeSpecHandler, + SizeSpecSource sizeSpecSource, PipTaskOrganizer pipTaskOrganizer, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, PipUiEventLogger pipUiEventLogger, @ShellMainThread ShellExecutor mainExecutor) { return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm, - pipBoundsState, pipSizeSpecHandler, pipTaskOrganizer, pipMotionHelper, + pipBoundsState, sizeSpecSource, pipTaskOrganizer, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor); } @@ -241,6 +233,13 @@ public abstract class Pip1Module { splitScreenOptional); } + @WMSingleton + @Provides + static SizeSpecSource provideSizeSpecSource(Context context, + PipDisplayLayoutState pipDisplayLayoutState) { + return new PhoneSizeSpecSource(context, pipDisplayLayoutState); + } + @WMSingleton @Provides static PipAppOpsListener providePipAppOpsListener(Context context, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java index 360bf8b70fca6..52c6d2008a9da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java @@ -28,6 +28,8 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.common.pip.LegacySizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.dagger.WMShellBaseModule; import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.pip.Pip; @@ -42,7 +44,6 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm; import com.android.wm.shell.pip.tv.TvPipBoundsController; import com.android.wm.shell.pip.tv.TvPipBoundsState; @@ -138,23 +139,23 @@ public abstract class TvPipModule { @Provides static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context, TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm, - PipSizeSpecHandler pipSizeSpecHandler) { + PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) { return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm, - pipSizeSpecHandler); + pipDisplayLayoutState, sizeSpecSource); } @WMSingleton @Provides static TvPipBoundsState provideTvPipBoundsState(Context context, - PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState) { - return new TvPipBoundsState(context, pipSizeSpecHandler, pipDisplayLayoutState); + SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) { + return new TvPipBoundsState(context, sizeSpecSource, pipDisplayLayoutState); } @WMSingleton @Provides - static PipSizeSpecHandler providePipSizeSpecHelper(Context context, + static SizeSpecSource provideSizeSpecSource(Context context, PipDisplayLayoutState pipDisplayLayoutState) { - return new PipSizeSpecHandler(context, pipDisplayLayoutState); + return new LegacySizeSpecSource(context, pipDisplayLayoutState); } // Handler needed for loadDrawableAsync() in PipControlsViewController 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 f51eb5299dd96..ac711eafe3ef7 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 @@ -28,7 +28,7 @@ import android.util.Size; import android.view.Gravity; import com.android.wm.shell.R; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; +import com.android.wm.shell.common.pip.SizeSpecSource; import java.io.PrintWriter; @@ -41,7 +41,8 @@ public class PipBoundsAlgorithm { private static final float INVALID_SNAP_FRACTION = -1f; @NonNull private final PipBoundsState mPipBoundsState; - @NonNull protected final PipSizeSpecHandler mPipSizeSpecHandler; + @NonNull protected final PipDisplayLayoutState mPipDisplayLayoutState; + @NonNull protected final SizeSpecSource mSizeSpecSource; private final PipSnapAlgorithm mSnapAlgorithm; private final PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm; @@ -53,11 +54,13 @@ public class PipBoundsAlgorithm { public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState, @NonNull PipSnapAlgorithm pipSnapAlgorithm, @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm, - @NonNull PipSizeSpecHandler pipSizeSpecHandler) { + @NonNull PipDisplayLayoutState pipDisplayLayoutState, + @NonNull SizeSpecSource sizeSpecSource) { mPipBoundsState = pipBoundsState; mSnapAlgorithm = pipSnapAlgorithm; mPipKeepClearAlgorithm = pipKeepClearAlgorithm; - mPipSizeSpecHandler = pipSizeSpecHandler; + mPipDisplayLayoutState = pipDisplayLayoutState; + mSizeSpecSource = sizeSpecSource; reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which @@ -74,11 +77,6 @@ public class PipBoundsAlgorithm { R.dimen.config_pictureInPictureDefaultAspectRatio); mDefaultStackGravity = res.getInteger( R.integer.config_defaultPictureInPictureGravity); - final String screenEdgeInsetsDpString = res.getString( - R.string.config_defaultPictureInPictureScreenEdgeInsets); - final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() - ? Size.parseSize(screenEdgeInsetsDpString) - : null; mMinAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); mMaxAspectRatio = res.getFloat( @@ -160,8 +158,8 @@ public class PipBoundsAlgorithm { // If either dimension is smaller than the allowed minimum, adjust them // according to mOverridableMinSize return new Size( - Math.max(windowLayout.minWidth, mPipSizeSpecHandler.getOverrideMinEdgeSize()), - Math.max(windowLayout.minHeight, mPipSizeSpecHandler.getOverrideMinEdgeSize())); + Math.max(windowLayout.minWidth, getOverrideMinEdgeSize()), + Math.max(windowLayout.minHeight, getOverrideMinEdgeSize())); } return null; } @@ -255,10 +253,10 @@ public class PipBoundsAlgorithm { final Size size; if (useCurrentMinEdgeSize || useCurrentSize) { // Use the existing size but adjusted to the new aspect ratio. - size = mPipSizeSpecHandler.getSizeForAspectRatio( + size = mSizeSpecSource.getSizeForAspectRatio( new Size(stackBounds.width(), stackBounds.height()), aspectRatio); } else { - size = mPipSizeSpecHandler.getDefaultSize(aspectRatio); + size = mSizeSpecSource.getDefaultSize(aspectRatio); } final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f); @@ -287,7 +285,7 @@ public class PipBoundsAlgorithm { getInsetBounds(insetBounds); // Calculate the default size - defaultSize = mPipSizeSpecHandler.getDefaultSize(mDefaultAspectRatio); + defaultSize = mSizeSpecSource.getDefaultSize(mDefaultAspectRatio); // Now that we have the default size, apply the snap fraction if valid or position the // bounds using the default gravity. @@ -309,7 +307,11 @@ public class PipBoundsAlgorithm { * Populates the bounds on the screen that the PIP can be visible in. */ public void getInsetBounds(Rect outRect) { - outRect.set(mPipSizeSpecHandler.getInsetBounds()); + outRect.set(mPipDisplayLayoutState.getInsetBounds()); + } + + private int getOverrideMinEdgeSize() { + return mSizeSpecSource.getOverrideMinEdgeSize(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index 9a775dff1f69d..279ffc50e07ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -36,7 +36,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.TriConsumer; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.io.PrintWriter; @@ -87,7 +87,7 @@ public class PipBoundsState { private int mStashOffset; private @Nullable PipReentryState mPipReentryState; private final LauncherState mLauncherState = new LauncherState(); - private final @Nullable PipSizeSpecHandler mPipSizeSpecHandler; + private final @NonNull SizeSpecSource mSizeSpecSource; private @Nullable ComponentName mLastPipComponentName; private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState(); private boolean mIsImeShowing; @@ -127,17 +127,20 @@ public class PipBoundsState { private @Nullable TriConsumer mOnShelfVisibilityChangeCallback; private List> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>(); - public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler, - PipDisplayLayoutState pipDisplayLayoutState) { + public PipBoundsState(@NonNull Context context, @NonNull SizeSpecSource sizeSpecSource, + @NonNull PipDisplayLayoutState pipDisplayLayoutState) { mContext = context; reloadResources(); - mPipSizeSpecHandler = pipSizeSpecHandler; + mSizeSpecSource = sizeSpecSource; mPipDisplayLayoutState = pipDisplayLayoutState; } /** Reloads the resources. */ public void onConfigurationChanged() { reloadResources(); + + // update the size spec resources upon config change too + mSizeSpecSource.onConfigurationChanged(); } private void reloadResources() { @@ -319,7 +322,7 @@ public class PipBoundsState { /** Sets the preferred size of PIP as specified by the activity in PIP mode. */ public void setOverrideMinSize(@Nullable Size overrideMinSize) { final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize()); - mPipSizeSpecHandler.setOverrideMinSize(overrideMinSize); + mSizeSpecSource.setOverrideMinSize(overrideMinSize); if (changed && mOnMinimalSizeChangeCallback != null) { mOnMinimalSizeChangeCallback.run(); } @@ -328,12 +331,12 @@ public class PipBoundsState { /** Returns the preferred minimal size specified by the activity in PIP. */ @Nullable public Size getOverrideMinSize() { - return mPipSizeSpecHandler.getOverrideMinSize(); + return mSizeSpecSource.getOverrideMinSize(); } /** Returns the minimum edge size of the override minimum size, or 0 if not set. */ public int getOverrideMinEdgeSize() { - return mPipSizeSpecHandler.getOverrideMinEdgeSize(); + return mSizeSpecSource.getOverrideMinEdgeSize(); } /** Get the state of the bounds in motion. */ @@ -613,5 +616,6 @@ public class PipBoundsState { } mLauncherState.dump(pw, innerPrefix); mMotionBoundsState.dump(pw, innerPrefix); + mSizeSpecSource.dump(pw, innerPrefix); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java index 0f76af48199ff..456f85b52da4b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java @@ -16,12 +16,18 @@ package com.android.wm.shell.pip; +import static com.android.wm.shell.pip.PipUtils.dpToPx; + import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; import android.graphics.Rect; +import android.util.Size; import android.view.Surface; import androidx.annotation.NonNull; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.dagger.WMSingleton; @@ -40,13 +46,51 @@ public class PipDisplayLayoutState { private int mDisplayId; @NonNull private DisplayLayout mDisplayLayout; + private Point mScreenEdgeInsets = null; + @Inject public PipDisplayLayoutState(Context context) { mContext = context; mDisplayLayout = new DisplayLayout(); + reloadResources(); } - /** Update the display layout. */ + /** Responds to configuration change. */ + public void onConfigurationChanged() { + reloadResources(); + } + + private void reloadResources() { + Resources res = mContext.getResources(); + + final String screenEdgeInsetsDpString = res.getString( + R.string.config_defaultPictureInPictureScreenEdgeInsets); + final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() + ? Size.parseSize(screenEdgeInsetsDpString) + : null; + mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point() + : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()), + dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics())); + } + + public Point getScreenEdgeInsets() { + return mScreenEdgeInsets; + } + + /** + * Returns the inset bounds the PIP window can be visible in. + */ + public Rect getInsetBounds() { + Rect insetBounds = new Rect(); + Rect insets = getDisplayLayout().stableInsets(); + insetBounds.set(insets.left + getScreenEdgeInsets().x, + insets.top + getScreenEdgeInsets().y, + getDisplayLayout().width() - insets.right - getScreenEdgeInsets().x, + getDisplayLayout().height() - insets.bottom - getScreenEdgeInsets().y); + return insetBounds; + } + + /** Set the display layout. */ public void setDisplayLayout(@NonNull DisplayLayout displayLayout) { mDisplayLayout.set(displayLayout); } @@ -87,5 +131,6 @@ public class PipDisplayLayoutState { pw.println(prefix + TAG); pw.println(innerPrefix + "mDisplayId=" + mDisplayId); pw.println(innerPrefix + "getDisplayBounds=" + getDisplayBounds()); + pw.println(innerPrefix + "mScreenEdgeInsets=" + mScreenEdgeInsets); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 26b7b684e1d8f..f396f3fedf64c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -142,7 +142,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb private PipBoundsAlgorithm mPipBoundsAlgorithm; private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm; private PipBoundsState mPipBoundsState; - private PipSizeSpecHandler mPipSizeSpecHandler; private PipDisplayLayoutState mPipDisplayLayoutState; private PipMotionHelper mPipMotionHelper; private PipTouchHandler mTouchHandler; @@ -406,7 +405,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb PipBoundsAlgorithm pipBoundsAlgorithm, PipKeepClearAlgorithmInterface pipKeepClearAlgorithm, PipBoundsState pipBoundsState, - PipSizeSpecHandler pipSizeSpecHandler, PipDisplayLayoutState pipDisplayLayoutState, PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, @@ -430,7 +428,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb return new PipController(context, shellInit, shellCommandHandler, shellController, displayController, pipAnimationController, pipAppOpsListener, - pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, + pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipDisplayLayoutState, pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController, windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, @@ -448,7 +446,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb PipBoundsAlgorithm pipBoundsAlgorithm, PipKeepClearAlgorithmInterface pipKeepClearAlgorithm, @NonNull PipBoundsState pipBoundsState, - PipSizeSpecHandler pipSizeSpecHandler, @NonNull PipDisplayLayoutState pipDisplayLayoutState, PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, @@ -474,7 +471,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipKeepClearAlgorithm = pipKeepClearAlgorithm; mPipBoundsState = pipBoundsState; - mPipSizeSpecHandler = pipSizeSpecHandler; mPipDisplayLayoutState = pipDisplayLayoutState; mPipMotionHelper = pipMotionHelper; mPipTaskOrganizer = pipTaskOrganizer; @@ -711,7 +707,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb // Try to move the PiP window if we have entered PiP mode. if (mPipTransitionState.hasEnteredPip()) { final Rect pipBounds = mPipBoundsState.getBounds(); - final Point edgeInsets = mPipSizeSpecHandler.getScreenEdgeInsets(); + final Point edgeInsets = mPipDisplayLayoutState.getScreenEdgeInsets(); if ((pipBounds.height() + 2 * edgeInsets.y) > (displayBounds.height() / 2)) { // PiP bounds is too big to fit either half, bail early. return; @@ -770,7 +766,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsAlgorithm.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); mPipBoundsState.onConfigurationChanged(); - mPipSizeSpecHandler.onConfigurationChanged(); + mPipDisplayLayoutState.onConfigurationChanged(); } @Override @@ -1224,7 +1220,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipTaskOrganizer.dump(pw, innerPrefix); mPipBoundsState.dump(pw, innerPrefix); mPipInputConsumer.dump(pw, innerPrefix); - mPipSizeSpecHandler.dump(pw, innerPrefix); mPipDisplayLayoutState.dump(pw, innerPrefix); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java deleted file mode 100644 index c6e5cf25f8445..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * 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.phone; - -import static com.android.wm.shell.pip.PipUtils.dpToPx; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.PointF; -import android.graphics.Rect; -import android.os.SystemProperties; -import android.util.Size; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.R; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.pip.PipDisplayLayoutState; - -import java.io.PrintWriter; - -/** - * Acts as a source of truth for appropriate size spec for PIP. - */ -public class PipSizeSpecHandler { - private static final String TAG = PipSizeSpecHandler.class.getSimpleName(); - - @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState; - - private final SizeSpecSource mSizeSpecSourceImpl; - - /** The preferred minimum (and default minimum) size specified by apps. */ - @Nullable private Size mOverrideMinSize; - private int mOverridableMinSize; - - /** Used to store values obtained from resource files. */ - private Point mScreenEdgeInsets; - private float mMinAspectRatioForMinSize; - private float mMaxAspectRatioForMinSize; - private int mDefaultMinSize; - - @NonNull private final Context mContext; - - private interface SizeSpecSource { - /** Returns max size allowed for the PIP window */ - Size getMaxSize(float aspectRatio); - - /** Returns default size for the PIP window */ - Size getDefaultSize(float aspectRatio); - - /** Returns min size allowed for the PIP window */ - Size getMinSize(float aspectRatio); - - /** Returns the adjusted size based on current size and target aspect ratio */ - Size getSizeForAspectRatio(Size size, float aspectRatio); - - /** Updates internal resources on configuration changes */ - default void reloadResources() {} - } - - /** - * Determines PIP window size optimized for large screens and aspect ratios close to 1:1 - */ - private class SizeSpecLargeScreenOptimizedImpl implements SizeSpecSource { - private static final float DEFAULT_OPTIMIZED_ASPECT_RATIO = 9f / 16; - - /** Default and minimum percentages for the PIP size logic. */ - private final float mDefaultSizePercent; - private final float mMinimumSizePercent; - - /** Aspect ratio that the PIP size spec logic optimizes for. */ - private float mOptimizedAspectRatio; - - private SizeSpecLargeScreenOptimizedImpl() { - mDefaultSizePercent = Float.parseFloat(SystemProperties - .get("com.android.wm.shell.pip.phone.def_percentage", "0.6")); - mMinimumSizePercent = Float.parseFloat(SystemProperties - .get("com.android.wm.shell.pip.phone.min_percentage", "0.5")); - } - - @Override - public void reloadResources() { - final Resources res = mContext.getResources(); - - mOptimizedAspectRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio); - // make sure the optimized aspect ratio is valid with a default value to fall back to - if (mOptimizedAspectRatio > 1) { - mOptimizedAspectRatio = DEFAULT_OPTIMIZED_ASPECT_RATIO; - } - } - - /** - * Calculates the max size of PIP. - * - * Optimizes for 16:9 aspect ratios, making them take full length of shortest display edge. - * As aspect ratio approaches values close to 1:1, the logic does not let PIP occupy the - * whole screen. A linear function is used to calculate these sizes. - * - * @param aspectRatio aspect ratio of the PIP window - * @return dimensions of the max size of the PIP - */ - @Override - public Size getMaxSize(float aspectRatio) { - final int totalHorizontalPadding = getInsetBounds().left - + (getDisplayBounds().width() - getInsetBounds().right); - final int totalVerticalPadding = getInsetBounds().top - + (getDisplayBounds().height() - getInsetBounds().bottom); - - final int shorterLength = Math.min(getDisplayBounds().width() - totalHorizontalPadding, - getDisplayBounds().height() - totalVerticalPadding); - - int maxWidth, maxHeight; - - // use the optimized max sizing logic only within a certain aspect ratio range - if (aspectRatio >= mOptimizedAspectRatio && aspectRatio <= 1 / mOptimizedAspectRatio) { - // this formula and its derivation is explained in b/198643358#comment16 - maxWidth = Math.round(mOptimizedAspectRatio * shorterLength - + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1 - + aspectRatio)); - // make sure the max width doesn't go beyond shorter screen length after rounding - maxWidth = Math.min(maxWidth, shorterLength); - maxHeight = Math.round(maxWidth / aspectRatio); - } else { - if (aspectRatio > 1f) { - maxWidth = shorterLength; - maxHeight = Math.round(maxWidth / aspectRatio); - } else { - maxHeight = shorterLength; - maxWidth = Math.round(maxHeight * aspectRatio); - } - } - - return new Size(maxWidth, maxHeight); - } - - /** - * Decreases the dimensions by a percentage relative to max size to get default size. - * - * @param aspectRatio aspect ratio of the PIP window - * @return dimensions of the default size of the PIP - */ - @Override - public Size getDefaultSize(float aspectRatio) { - Size minSize = this.getMinSize(aspectRatio); - - if (mOverrideMinSize != null) { - return minSize; - } - - Size maxSize = this.getMaxSize(aspectRatio); - - int defaultWidth = Math.max(Math.round(maxSize.getWidth() * mDefaultSizePercent), - minSize.getWidth()); - int defaultHeight = Math.round(defaultWidth / aspectRatio); - - return new Size(defaultWidth, defaultHeight); - } - - /** - * Decreases the dimensions by a certain percentage relative to max size to get min size. - * - * @param aspectRatio aspect ratio of the PIP window - * @return dimensions of the min size of the PIP - */ - @Override - public Size getMinSize(float aspectRatio) { - // if there is an overridden min size provided, return that - if (mOverrideMinSize != null) { - return adjustOverrideMinSizeToAspectRatio(aspectRatio); - } - - Size maxSize = this.getMaxSize(aspectRatio); - - int minWidth = Math.round(maxSize.getWidth() * mMinimumSizePercent); - int minHeight = Math.round(maxSize.getHeight() * mMinimumSizePercent); - - // make sure the calculated min size is not smaller than the allowed default min size - if (aspectRatio > 1f) { - minHeight = Math.max(minHeight, mDefaultMinSize); - minWidth = Math.round(minHeight * aspectRatio); - } else { - minWidth = Math.max(minWidth, mDefaultMinSize); - minHeight = Math.round(minWidth / aspectRatio); - } - return new Size(minWidth, minHeight); - } - - /** - * Returns the size for target aspect ratio making sure new size conforms with the rules. - * - *

Recalculates the dimensions such that the target aspect ratio is achieved, while - * maintaining the same maximum size to current size ratio. - * - * @param size current size - * @param aspectRatio target aspect ratio - */ - @Override - public Size getSizeForAspectRatio(Size size, float aspectRatio) { - float currAspectRatio = (float) size.getWidth() / size.getHeight(); - - // getting the percentage of the max size that current size takes - Size currentMaxSize = getMaxSize(currAspectRatio); - float currentPercent = (float) size.getWidth() / currentMaxSize.getWidth(); - - // getting the max size for the target aspect ratio - Size updatedMaxSize = getMaxSize(aspectRatio); - - int width = Math.round(updatedMaxSize.getWidth() * currentPercent); - int height = Math.round(updatedMaxSize.getHeight() * currentPercent); - - // adjust the dimensions if below allowed min edge size - if (width < getMinEdgeSize() && aspectRatio <= 1) { - width = getMinEdgeSize(); - height = Math.round(width / aspectRatio); - } else if (height < getMinEdgeSize() && aspectRatio > 1) { - height = getMinEdgeSize(); - width = Math.round(height * aspectRatio); - } - - // reduce the dimensions of the updated size to the calculated percentage - return new Size(width, height); - } - } - - private class SizeSpecDefaultImpl implements SizeSpecSource { - private float mDefaultSizePercent; - private float mMinimumSizePercent; - - @Override - public void reloadResources() { - final Resources res = mContext.getResources(); - - mMaxAspectRatioForMinSize = res.getFloat( - R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); - mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; - - mDefaultSizePercent = res.getFloat(R.dimen.config_pictureInPictureDefaultSizePercent); - mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1); - } - - @Override - public Size getMaxSize(float aspectRatio) { - final int shorterLength = Math.min(getDisplayBounds().width(), - getDisplayBounds().height()); - - final int totalHorizontalPadding = getInsetBounds().left - + (getDisplayBounds().width() - getInsetBounds().right); - final int totalVerticalPadding = getInsetBounds().top - + (getDisplayBounds().height() - getInsetBounds().bottom); - - final int maxWidth, maxHeight; - - if (aspectRatio > 1f) { - maxWidth = (int) Math.max(getDefaultSize(aspectRatio).getWidth(), - shorterLength - totalHorizontalPadding); - maxHeight = (int) (maxWidth / aspectRatio); - } else { - maxHeight = (int) Math.max(getDefaultSize(aspectRatio).getHeight(), - shorterLength - totalVerticalPadding); - maxWidth = (int) (maxHeight * aspectRatio); - } - - return new Size(maxWidth, maxHeight); - } - - @Override - public Size getDefaultSize(float aspectRatio) { - if (mOverrideMinSize != null) { - return this.getMinSize(aspectRatio); - } - - final int smallestDisplaySize = Math.min(getDisplayBounds().width(), - getDisplayBounds().height()); - final int minSize = (int) Math.max(getMinEdgeSize(), - smallestDisplaySize * mDefaultSizePercent); - - final int width; - final int height; - - if (aspectRatio <= mMinAspectRatioForMinSize - || aspectRatio > mMaxAspectRatioForMinSize) { - // Beyond these points, we can just use the min size as the shorter edge - if (aspectRatio <= 1) { - // Portrait, width is the minimum size - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - } else { - // Within these points, ensure that the bounds fit within the radius of the limits - // at the points - final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; - final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); - height = (int) Math.round(Math.sqrt((radius * radius) - / (aspectRatio * aspectRatio + 1))); - width = Math.round(height * aspectRatio); - } - - return new Size(width, height); - } - - @Override - public Size getMinSize(float aspectRatio) { - if (mOverrideMinSize != null) { - return adjustOverrideMinSizeToAspectRatio(aspectRatio); - } - - final int shorterLength = Math.min(getDisplayBounds().width(), - getDisplayBounds().height()); - final int minWidth, minHeight; - - if (aspectRatio > 1f) { - minWidth = (int) Math.min(getDefaultSize(aspectRatio).getWidth(), - shorterLength * mMinimumSizePercent); - minHeight = (int) (minWidth / aspectRatio); - } else { - minHeight = (int) Math.min(getDefaultSize(aspectRatio).getHeight(), - shorterLength * mMinimumSizePercent); - minWidth = (int) (minHeight * aspectRatio); - } - - return new Size(minWidth, minHeight); - } - - @Override - public Size getSizeForAspectRatio(Size size, float aspectRatio) { - final int smallestSize = Math.min(size.getWidth(), size.getHeight()); - final int minSize = Math.max(getMinEdgeSize(), smallestSize); - - final int width; - final int height; - if (aspectRatio <= 1) { - // Portrait, width is the minimum size. - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - - return new Size(width, height); - } - } - - public PipSizeSpecHandler(Context context, PipDisplayLayoutState pipDisplayLayoutState) { - mContext = context; - mPipDisplayLayoutState = pipDisplayLayoutState; - - // choose between two implementations of size spec logic - if (supportsPipSizeLargeScreen()) { - mSizeSpecSourceImpl = new SizeSpecLargeScreenOptimizedImpl(); - } else { - mSizeSpecSourceImpl = new SizeSpecDefaultImpl(); - } - - reloadResources(); - } - - /** Reloads the resources */ - public void onConfigurationChanged() { - reloadResources(); - } - - private void reloadResources() { - final Resources res = mContext.getResources(); - - mDefaultMinSize = res.getDimensionPixelSize( - R.dimen.default_minimal_size_pip_resizable_task); - mOverridableMinSize = res.getDimensionPixelSize( - R.dimen.overridable_minimal_size_pip_resizable_task); - - final String screenEdgeInsetsDpString = res.getString( - R.string.config_defaultPictureInPictureScreenEdgeInsets); - final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() - ? Size.parseSize(screenEdgeInsetsDpString) - : null; - mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point() - : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()), - dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics())); - - // update the internal resources of the size spec source's stub - mSizeSpecSourceImpl.reloadResources(); - } - - @NonNull - private Rect getDisplayBounds() { - return mPipDisplayLayoutState.getDisplayBounds(); - } - - public Point getScreenEdgeInsets() { - return mScreenEdgeInsets; - } - - /** - * Returns the inset bounds the PIP window can be visible in. - */ - public Rect getInsetBounds() { - Rect insetBounds = new Rect(); - DisplayLayout displayLayout = mPipDisplayLayoutState.getDisplayLayout(); - Rect insets = displayLayout.stableInsets(); - insetBounds.set(insets.left + mScreenEdgeInsets.x, - insets.top + mScreenEdgeInsets.y, - displayLayout.width() - insets.right - mScreenEdgeInsets.x, - displayLayout.height() - insets.bottom - mScreenEdgeInsets.y); - return insetBounds; - } - - /** Sets the preferred size of PIP as specified by the activity in PIP mode. */ - public void setOverrideMinSize(@Nullable Size overrideMinSize) { - mOverrideMinSize = overrideMinSize; - } - - /** Returns the preferred minimal size specified by the activity in PIP. */ - @Nullable - public Size getOverrideMinSize() { - if (mOverrideMinSize != null - && (mOverrideMinSize.getWidth() < mOverridableMinSize - || mOverrideMinSize.getHeight() < mOverridableMinSize)) { - return new Size(mOverridableMinSize, mOverridableMinSize); - } - - return mOverrideMinSize; - } - - /** Returns the minimum edge size of the override minimum size, or 0 if not set. */ - public int getOverrideMinEdgeSize() { - if (mOverrideMinSize == null) return 0; - return Math.min(getOverrideMinSize().getWidth(), getOverrideMinSize().getHeight()); - } - - public int getMinEdgeSize() { - return mOverrideMinSize == null ? mDefaultMinSize : getOverrideMinEdgeSize(); - } - - /** - * Returns the size for the max size spec. - */ - public Size getMaxSize(float aspectRatio) { - return mSizeSpecSourceImpl.getMaxSize(aspectRatio); - } - - /** - * Returns the size for the default size spec. - */ - public Size getDefaultSize(float aspectRatio) { - return mSizeSpecSourceImpl.getDefaultSize(aspectRatio); - } - - /** - * Returns the size for the min size spec. - */ - public Size getMinSize(float aspectRatio) { - return mSizeSpecSourceImpl.getMinSize(aspectRatio); - } - - /** - * Returns the adjusted size so that it conforms to the given aspectRatio. - * - * @param size current size - * @param aspectRatio target aspect ratio - */ - public Size getSizeForAspectRatio(@NonNull Size size, float aspectRatio) { - if (size.equals(mOverrideMinSize)) { - return adjustOverrideMinSizeToAspectRatio(aspectRatio); - } - - return mSizeSpecSourceImpl.getSizeForAspectRatio(size, aspectRatio); - } - - /** - * Returns the adjusted overridden min size if it is set; otherwise, returns null. - * - *

Overridden min size needs to be adjusted in its own way while making sure that the target - * aspect ratio is maintained - * - * @param aspectRatio target aspect ratio - */ - @Nullable - @VisibleForTesting - Size adjustOverrideMinSizeToAspectRatio(float aspectRatio) { - if (mOverrideMinSize == null) { - return null; - } - final Size size = getOverrideMinSize(); - final float sizeAspectRatio = size.getWidth() / (float) size.getHeight(); - if (sizeAspectRatio > aspectRatio) { - // Size is wider, fix the width and increase the height - return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio)); - } else { - // Size is taller, fix the height and adjust the width. - return new Size((int) (size.getHeight() * aspectRatio), size.getHeight()); - } - } - - @VisibleForTesting - boolean supportsPipSizeLargeScreen() { - // TODO(b/271468706): switch Tv to having a dedicated SizeSpecSource once the SizeSpecSource - // can be injected - return SystemProperties - .getBoolean("persist.wm.debug.enable_pip_size_large_screen", true) && !isTv(); - } - - private boolean isTv() { - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); - } - - /** Dumps internal state. */ - public void dump(PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - pw.println(prefix + TAG); - pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl); - pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize); - pw.println(innerPrefix + "mScreenEdgeInsets=" + mScreenEdgeInsets); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 415f398765c1c..ab65c9ebe7e5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -51,6 +51,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -85,7 +86,7 @@ public class PipTouchHandler { private final Context mContext; private final PipBoundsAlgorithm mPipBoundsAlgorithm; @NonNull private final PipBoundsState mPipBoundsState; - @NonNull private final PipSizeSpecHandler mPipSizeSpecHandler; + @NonNull private final SizeSpecSource mSizeSpecSource; private final PipUiEventLogger mPipUiEventLogger; private final PipDismissTargetHandler mPipDismissTargetHandler; private final PipTaskOrganizer mPipTaskOrganizer; @@ -179,7 +180,7 @@ public class PipTouchHandler { PhonePipMenuController menuController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, - @NonNull PipSizeSpecHandler pipSizeSpecHandler, + @NonNull SizeSpecSource sizeSpecSource, PipTaskOrganizer pipTaskOrganizer, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, @@ -190,7 +191,7 @@ public class PipTouchHandler { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; - mPipSizeSpecHandler = pipSizeSpecHandler; + mSizeSpecSource = sizeSpecSource; mPipTaskOrganizer = pipTaskOrganizer; mMenuController = menuController; mPipUiEventLogger = pipUiEventLogger; @@ -413,7 +414,7 @@ public class PipTouchHandler { // Calculate the expanded size float aspectRatio = (float) normalBounds.width() / normalBounds.height(); - Size expandedSize = mPipSizeSpecHandler.getDefaultSize(aspectRatio); + Size expandedSize = mSizeSpecSource.getDefaultSize(aspectRatio); mPipBoundsState.setExpandedBounds( new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight())); Rect expandedMovementBounds = new Rect(); @@ -517,10 +518,10 @@ public class PipTouchHandler { private void updatePinchResizeSizeConstraints(float aspectRatio) { final int minWidth, minHeight, maxWidth, maxHeight; - minWidth = mPipSizeSpecHandler.getMinSize(aspectRatio).getWidth(); - minHeight = mPipSizeSpecHandler.getMinSize(aspectRatio).getHeight(); - maxWidth = mPipSizeSpecHandler.getMaxSize(aspectRatio).getWidth(); - maxHeight = mPipSizeSpecHandler.getMaxSize(aspectRatio).getHeight(); + minWidth = mSizeSpecSource.getMinSize(aspectRatio).getWidth(); + minHeight = mSizeSpecSource.getMinSize(aspectRatio).getHeight(); + maxWidth = mSizeSpecSource.getMaxSize(aspectRatio).getWidth(); + maxHeight = mSizeSpecSource.getMaxSize(aspectRatio).getHeight(); mPipResizeGestureHandler.updateMinSize(minWidth, minHeight); mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight); 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 825b96921a22c..cd58ff4f5154d 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 @@ -36,10 +36,11 @@ import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipDisplayLayoutState; import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface; import com.android.wm.shell.pip.PipSnapAlgorithm; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -62,9 +63,10 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { public TvPipBoundsAlgorithm(Context context, @NonNull TvPipBoundsState tvPipBoundsState, @NonNull PipSnapAlgorithm pipSnapAlgorithm, - @NonNull PipSizeSpecHandler pipSizeSpecHandler) { + @NonNull PipDisplayLayoutState pipDisplayLayoutState, + @NonNull SizeSpecSource sizeSpecSource) { super(context, tvPipBoundsState, pipSnapAlgorithm, - new PipKeepClearAlgorithmInterface() {}, pipSizeSpecHandler); + new PipKeepClearAlgorithmInterface() {}, pipDisplayLayoutState, sizeSpecSource); this.mTvPipBoundsState = tvPipBoundsState; this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm(); reloadResources(context); @@ -291,7 +293,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { expandedSize = mTvPipBoundsState.getTvExpandedSize(); } else { int maxHeight = displayLayout.height() - - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().y) + - (2 * mPipDisplayLayoutState.getScreenEdgeInsets().y) - pipDecorations.top - pipDecorations.bottom; float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio; @@ -311,7 +313,7 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm { expandedSize = mTvPipBoundsState.getTvExpandedSize(); } else { int maxWidth = displayLayout.width() - - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().x) + - (2 * mPipDisplayLayoutState.getScreenEdgeInsets().x) - pipDecorations.left - pipDecorations.right; float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio; if (maxWidth > aspectRatioWidth) { 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 e1737eccc6e1d..4757efc846f9c 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 @@ -29,10 +29,10 @@ import android.util.Size; import android.view.Gravity; import android.view.View; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -76,9 +76,9 @@ public class TvPipBoundsState extends PipBoundsState { private Insets mPipMenuTemporaryDecorInsets = Insets.NONE; public TvPipBoundsState(@NonNull Context context, - @NonNull PipSizeSpecHandler pipSizeSpecHandler, + @NonNull SizeSpecSource sizeSpecSource, @NonNull PipDisplayLayoutState pipDisplayLayoutState) { - super(context, pipSizeSpecHandler, pipDisplayLayoutState); + super(context, sizeSpecSource, pipDisplayLayoutState); mContext = context; updateDefaultGravity(); mPreviousCollapsedGravity = mDefaultGravity; 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 addc2338144fb..bf1b7f9f86edf 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 @@ -32,7 +32,8 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import org.junit.Before; import org.junit.Test; @@ -60,7 +61,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private PipBoundsAlgorithm mPipBoundsAlgorithm; private DisplayInfo mDefaultDisplayInfo; - private PipBoundsState mPipBoundsState; private PipSizeSpecHandler mPipSizeSpecHandler; + private PipBoundsState mPipBoundsState; + private SizeSpecSource mSizeSpecSource; private PipDisplayLayoutState mPipDisplayLayoutState; @@ -68,11 +70,12 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { public void setUp() throws Exception { initializeMockResources(); mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); - mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState); - mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState); + + mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); + mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {}, - mPipSizeSpecHandler); + mPipDisplayLayoutState, mSizeSpecSource); DisplayLayout layout = new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true); @@ -132,7 +135,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { @Test public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() { - final Size defaultSize = mPipSizeSpecHandler.getDefaultSize(DEFAULT_ASPECT_RATIO); + final Size defaultSize = mSizeSpecSource.getDefaultSize(DEFAULT_ASPECT_RATIO); mPipBoundsState.setOverrideMinSize(null); final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java index f32000445ca92..4341c4c8c8aa4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java @@ -35,7 +35,8 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.function.TriConsumer; import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import org.junit.Before; import org.junit.Test; @@ -58,6 +59,7 @@ public class PipBoundsStateTest extends ShellTestCase { private static final int OVERRIDABLE_MIN_SIZE = 40; private PipBoundsState mPipBoundsState; + private SizeSpecSource mSizeSpecSource; private ComponentName mTestComponentName1; private ComponentName mTestComponentName2; @@ -69,8 +71,8 @@ public class PipBoundsStateTest extends ShellTestCase { OVERRIDABLE_MIN_SIZE); PipDisplayLayoutState pipDisplayLayoutState = new PipDisplayLayoutState(mContext); - mPipBoundsState = new PipBoundsState(mContext, - new PipSizeSpecHandler(mContext, pipDisplayLayoutState), pipDisplayLayoutState); + mSizeSpecSource = new PhoneSizeSpecSource(mContext, pipDisplayLayoutState); + mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, pipDisplayLayoutState); mTestComponentName1 = new ComponentName(mContext, "component1"); mTestComponentName2 = new ComponentName(mContext, "component2"); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 842c699fa42df..1e3fe421140ab 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -52,8 +52,9 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.phone.PhonePipMenuController; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; import com.android.wm.shell.splitscreen.SplitScreenController; import org.junit.Before; @@ -87,7 +88,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { private PipBoundsState mPipBoundsState; private PipTransitionState mPipTransitionState; private PipBoundsAlgorithm mPipBoundsAlgorithm; - private PipSizeSpecHandler mPipSizeSpecHandler; + private SizeSpecSource mSizeSpecSource; private PipDisplayLayoutState mPipDisplayLayoutState; private ComponentName mComponent1; @@ -99,12 +100,12 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); - mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState); - mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState); + mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); + mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState); mPipTransitionState = new PipTransitionState(); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {}, - mPipSizeSpecHandler); + mPipDisplayLayoutState, mSizeSpecSource); mMainExecutor = new TestShellExecutor(); mPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState, mPipDisplayLayoutState, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java similarity index 84% rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java index 528c23cd8115b..024cba319ffc4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java @@ -32,6 +32,8 @@ import android.view.DisplayInfo; import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipDisplayLayoutState; import org.junit.After; @@ -47,10 +49,10 @@ import java.util.Map; import java.util.function.Function; /** - * Unit test against {@link PipSizeSpecHandler} with feature flag on. + * Unit test against {@link PhoneSizeSpecSource} */ @RunWith(AndroidTestingRunner.class) -public class PipSizeSpecHandlerTest extends ShellTestCase { +public class PhoneSizeSpecSourceTest extends ShellTestCase { /** A sample overridden min edge size. */ private static final int OVERRIDE_MIN_EDGE_SIZE = 40; /** A sample default min edge size */ @@ -75,7 +77,7 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { @Mock private Resources mResources; private PipDisplayLayoutState mPipDisplayLayoutState; - private TestPipSizeSpecHandler mPipSizeSpecHandler; + private SizeSpecSource mSizeSpecSource; /** * Sets up static Mockito session for SystemProperties and mocks necessary static methods. @@ -158,10 +160,10 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { mPipDisplayLayoutState.setDisplayLayout(displayLayout); setUpStaticSystemPropertiesSession(); - mPipSizeSpecHandler = new TestPipSizeSpecHandler(mContext, mPipDisplayLayoutState); + mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); // no overridden min edge size by default - mPipSizeSpecHandler.setOverrideMinSize(null); + mSizeSpecSource.setOverrideMinSize(null); } @After @@ -172,19 +174,19 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { @Test public void testGetMaxSize() { forEveryTestCaseCheck(sExpectedMaxSizes, - (aspectRatio) -> mPipSizeSpecHandler.getMaxSize(aspectRatio)); + (aspectRatio) -> mSizeSpecSource.getMaxSize(aspectRatio)); } @Test public void testGetDefaultSize() { forEveryTestCaseCheck(sExpectedDefaultSizes, - (aspectRatio) -> mPipSizeSpecHandler.getDefaultSize(aspectRatio)); + (aspectRatio) -> mSizeSpecSource.getDefaultSize(aspectRatio)); } @Test public void testGetMinSize() { forEveryTestCaseCheck(sExpectedMinSizes, - (aspectRatio) -> mPipSizeSpecHandler.getMinSize(aspectRatio)); + (aspectRatio) -> mSizeSpecSource.getMinSize(aspectRatio)); } @Test @@ -193,7 +195,7 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { Size initSize = new Size(600, 337); Size expectedSize = new Size(338, 601); - Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16); + Size actualSize = mSizeSpecSource.getSizeForAspectRatio(initSize, 9f / 16); Assert.assertEquals(expectedSize, actualSize); } @@ -201,26 +203,12 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { @Test public void testGetSizeForAspectRatio_withOverrideMinSize() { // an initial size with a 1:1 aspect ratio - mPipSizeSpecHandler.setOverrideMinSize(new Size(OVERRIDE_MIN_EDGE_SIZE, - OVERRIDE_MIN_EDGE_SIZE)); - // make sure initial size is same as override min size - Size initSize = mPipSizeSpecHandler.getOverrideMinSize(); + Size initSize = new Size(OVERRIDE_MIN_EDGE_SIZE, OVERRIDE_MIN_EDGE_SIZE); + mSizeSpecSource.setOverrideMinSize(initSize); Size expectedSize = new Size(40, 71); - Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16); + Size actualSize = mSizeSpecSource.getSizeForAspectRatio(initSize, 9f / 16); Assert.assertEquals(expectedSize, actualSize); } - - static class TestPipSizeSpecHandler extends PipSizeSpecHandler { - - TestPipSizeSpecHandler(Context context, PipDisplayLayoutState displayLayoutState) { - super(context, displayLayoutState); - } - - @Override - boolean supportsPipSizeLargeScreen() { - return true; - } - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 85167cb97501a..2cc28acd0b17f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -109,7 +109,6 @@ public class PipControllerTest extends ShellTestCase { @Mock private PipMotionHelper mMockPipMotionHelper; @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper; @Mock private PipBoundsState mMockPipBoundsState; - @Mock private PipSizeSpecHandler mMockPipSizeSpecHandler; @Mock private PipDisplayLayoutState mMockPipDisplayLayoutState; @Mock private TaskStackListenerImpl mMockTaskStackListener; @Mock private ShellExecutor mMockExecutor; @@ -134,7 +133,7 @@ public class PipControllerTest extends ShellTestCase { mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler, mShellController, mMockDisplayController, mMockPipAnimationController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, - mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipDisplayLayoutState, + mMockPipBoundsState, mMockPipDisplayLayoutState, mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, @@ -226,7 +225,7 @@ public class PipControllerTest extends ShellTestCase { assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler, mShellController, mMockDisplayController, mMockPipAnimationController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, - mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipDisplayLayoutState, + mMockPipBoundsState, mMockPipDisplayLayoutState, mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java index 1dfdbf6514bae..689b5c5a708cb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java @@ -36,6 +36,8 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; @@ -87,7 +89,7 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { private PipBoundsState mPipBoundsState; - private PipSizeSpecHandler mPipSizeSpecHandler; + private SizeSpecSource mSizeSpecSource; private PipDisplayLayoutState mPipDisplayLayoutState; @@ -97,13 +99,14 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); - mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState); - mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState); + mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); + mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState); final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm(); final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm = new PipKeepClearAlgorithmInterface() {}; final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, - mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm, mPipSizeSpecHandler); + mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm, mPipDisplayLayoutState, + mSizeSpecSource); final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm, mMockPipTransitionController, mFloatingContentCoordinator); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index 10b1ddf1b868f..852183cbcbac4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -33,6 +33,8 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.PhoneSizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipDisplayLayoutState; @@ -92,7 +94,7 @@ public class PipTouchHandlerTest extends ShellTestCase { private PipSnapAlgorithm mPipSnapAlgorithm; private PipMotionHelper mMotionHelper; private PipResizeGestureHandler mPipResizeGestureHandler; - private PipSizeSpecHandler mPipSizeSpecHandler; + private SizeSpecSource mSizeSpecSource; private PipDisplayLayoutState mPipDisplayLayoutState; private DisplayLayout mDisplayLayout; @@ -108,16 +110,16 @@ public class PipTouchHandlerTest extends ShellTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); - mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState); - mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler, mPipDisplayLayoutState); + mSizeSpecSource = new PhoneSizeSpecSource(mContext, mPipDisplayLayoutState); + mPipBoundsState = new PipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState); mPipSnapAlgorithm = new PipSnapAlgorithm(); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm, - new PipKeepClearAlgorithmInterface() {}, mPipSizeSpecHandler); + new PipKeepClearAlgorithmInterface() {}, mPipDisplayLayoutState, mSizeSpecSource); PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm, mMockPipTransitionController, mFloatingContentCoordinator); mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController, - mPipBoundsAlgorithm, mPipBoundsState, mPipSizeSpecHandler, mPipTaskOrganizer, + mPipBoundsAlgorithm, mPipBoundsState, mSizeSpecSource, mPipTaskOrganizer, pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor); // We aren't actually using ShellInit, so just call init directly mPipTouchHandler.onInit(); @@ -162,8 +164,8 @@ public class PipTouchHandlerTest extends ShellTestCase { // getting the expected min and max size float aspectRatio = (float) mPipBounds.width() / mPipBounds.height(); - Size expectedMinSize = mPipSizeSpecHandler.getMinSize(aspectRatio); - Size expectedMaxSize = mPipSizeSpecHandler.getMaxSize(aspectRatio); + Size expectedMinSize = mSizeSpecSource.getMinSize(aspectRatio); + Size expectedMaxSize = mSizeSpecSource.getMaxSize(aspectRatio); assertEquals(expectedMovementBounds, mPipBoundsState.getNormalMovementBounds()); verify(mPipResizeGestureHandler, times(1)) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java index f9b772345b146..da6951bc588ec 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java @@ -26,9 +26,10 @@ import static org.junit.Assert.assertEquals; import android.view.Gravity; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.pip.LegacySizeSpecSource; +import com.android.wm.shell.common.pip.SizeSpecSource; import com.android.wm.shell.pip.PipDisplayLayoutState; import com.android.wm.shell.pip.PipSnapAlgorithm; -import com.android.wm.shell.pip.phone.PipSizeSpecHandler; import org.junit.Before; import org.junit.Test; @@ -47,7 +48,7 @@ public class TvPipGravityTest extends ShellTestCase { private TvPipBoundsState mTvPipBoundsState; private TvPipBoundsAlgorithm mTvPipBoundsAlgorithm; - private PipSizeSpecHandler mPipSizeSpecHandler; + private SizeSpecSource mSizeSpecSource; private PipDisplayLayoutState mPipDisplayLayoutState; @Before @@ -57,11 +58,11 @@ public class TvPipGravityTest extends ShellTestCase { } MockitoAnnotations.initMocks(this); mPipDisplayLayoutState = new PipDisplayLayoutState(mContext); - mPipSizeSpecHandler = new PipSizeSpecHandler(mContext, mPipDisplayLayoutState); - mTvPipBoundsState = new TvPipBoundsState(mContext, mPipSizeSpecHandler, + mSizeSpecSource = new LegacySizeSpecSource(mContext, mPipDisplayLayoutState); + mTvPipBoundsState = new TvPipBoundsState(mContext, mSizeSpecSource, mPipDisplayLayoutState); mTvPipBoundsAlgorithm = new TvPipBoundsAlgorithm(mContext, mTvPipBoundsState, - mMockPipSnapAlgorithm, mPipSizeSpecHandler); + mMockPipSnapAlgorithm, mPipDisplayLayoutState, mSizeSpecSource); setRTL(false); }