Merge "Making SizeSpecSource platform agnostic" into udc-qpr-dev

This commit is contained in:
Ikram Gabiyev
2023-08-08 20:49:51 +00:00
committed by Android (Google) Code Review
21 changed files with 684 additions and 667 deletions

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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) {}
}

View File

@@ -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,

View File

@@ -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

View File

@@ -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();
}
/**

View File

@@ -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<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
private List<Consumer<Rect>> 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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.
*
* <p>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.
*
* <p>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);
}
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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();

View File

@@ -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");
}

View File

@@ -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,

View File

@@ -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;
}
}
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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))

View File

@@ -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);
}