diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 1be1a1a13d..0ffe37bfe4 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -257,6 +257,7 @@ + diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt index 407a21231b..3d618f989e 100644 --- a/src/com/android/launcher3/responsive/SizeSpec.kt +++ b/src/com/android/launcher3/responsive/SizeSpec.kt @@ -15,22 +15,27 @@ import kotlin.math.roundToInt * @param ofAvailableSpace a percentage of the available space * @param ofRemainderSpace a percentage of the remaining space (available space minus used space) * @param matchWorkspace indicates whether the workspace value will be used or not. + * @param maxSize restricts the maximum value allowed for the [SizeSpec]. */ data class SizeSpec( val fixedSize: Float = 0f, val ofAvailableSpace: Float = 0f, val ofRemainderSpace: Float = 0f, - val matchWorkspace: Boolean = false + val matchWorkspace: Boolean = false, + val maxSize: Int = Int.MAX_VALUE ) { /** Retrieves the correct value for [SizeSpec]. */ fun getCalculatedValue(availableSpace: Int, workspaceValue: Int): Int { - return when { - fixedSize > 0 -> fixedSize.roundToInt() - ofAvailableSpace > 0 -> (ofAvailableSpace * availableSpace).roundToInt() - matchWorkspace -> workspaceValue - else -> 0 - } + val calculatedValue = + when { + fixedSize > 0 -> fixedSize.roundToInt() + matchWorkspace -> workspaceValue + ofAvailableSpace > 0 -> (ofAvailableSpace * availableSpace).roundToInt() + else -> 0 + } + + return calculatedValue.coerceAtMost(maxSize) } /** @@ -38,11 +43,14 @@ data class SizeSpec( * is 0, returns a default value. */ fun getRemainderSpaceValue(remainderSpace: Int, defaultValue: Int): Int { - return if (ofRemainderSpace > 0) { - (ofRemainderSpace * remainderSpace).roundToInt() - } else { - defaultValue - } + val remainderSpaceValue = + if (ofRemainderSpace > 0) { + (ofRemainderSpace * remainderSpace).roundToInt() + } else { + defaultValue + } + + return remainderSpaceValue.coerceAtMost(maxSize) } fun isValid(): Boolean { @@ -69,12 +77,17 @@ data class SizeSpec( return false } - // Invalid fixed size - if (fixedSize < 0f) { + // Invalid fixed or max size + if (fixedSize < 0f || maxSize < 0f) { Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.") return false } + if (fixedSize > 0f && fixedSize > maxSize) { + Log.e(TAG, "SizeSpec#isValid - fixed size should be smaller than the max size.") + return false + } + return true } @@ -96,10 +109,12 @@ data class SizeSpec( val ofAvailableSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofAvailableSpace) val ofRemainderSpace = getValue(styledAttrs, R.styleable.SizeSpec_ofRemainderSpace) val matchWorkspace = styledAttrs.getBoolean(R.styleable.SizeSpec_matchWorkspace, false) + val maxSize = + styledAttrs.getDimensionPixelSize(R.styleable.SizeSpec_maxSize, Int.MAX_VALUE) styledAttrs.recycle() - return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace) + return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace, maxSize) } } } diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml index 54f038110b..32bc550b2b 100644 --- a/tests/res/values/attrs.xml +++ b/tests/res/values/attrs.xml @@ -31,6 +31,7 @@ + diff --git a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt index 5db86ff2b4..088cae1485 100644 --- a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt @@ -45,7 +45,12 @@ class SizeSpecTest : AbstractDeviceProfileTest() { SizeSpec(0f, 1f, 0f, false), SizeSpec(0f, 0f, 1f, false), SizeSpec(0f, 0f, 0f, false), - SizeSpec(0f, 0f, 0f, true) + SizeSpec(0f, 0f, 0f, true), + SizeSpec(100f, 0f, 0f, false, 100), + SizeSpec(0f, 1f, 0f, false, 100), + SizeSpec(0f, 0f, 1f, false, 100), + SizeSpec(0f, 0f, 0f, false, 100), + SizeSpec(0f, 0f, 0f, true, 100) ) for (instance in combinations) { @@ -62,7 +67,12 @@ class SizeSpecTest : AbstractDeviceProfileTest() { SizeSpec(100f) to 100, SizeSpec(ofAvailableSpace = .5f) to (availableSpace * .5f).roundToInt(), SizeSpec(ofRemainderSpace = .5f) to 0, - SizeSpec(matchWorkspace = true) to matchWorkspaceValue + SizeSpec(matchWorkspace = true) to matchWorkspaceValue, + // Restricts max size up to 10 (calculated value > 10) + SizeSpec(100f, maxSize = 10) to 10, + SizeSpec(ofAvailableSpace = .5f, maxSize = 10) to 10, + SizeSpec(ofRemainderSpace = .5f, maxSize = 10) to 0, + SizeSpec(matchWorkspace = true, maxSize = 10) to 10 ) for ((sizeSpec, expectedValue) in combinations) { @@ -74,13 +84,18 @@ class SizeSpecTest : AbstractDeviceProfileTest() { @Test fun validate_getRemainderSpaceValue() { val remainderSpace = 100 - val defaultValue = 10 + val defaultValue = 50 val combinations = listOf( SizeSpec(100f) to defaultValue, SizeSpec(ofAvailableSpace = .5f) to defaultValue, SizeSpec(ofRemainderSpace = .5f) to (remainderSpace * .5f).roundToInt(), - SizeSpec(matchWorkspace = true) to defaultValue + SizeSpec(matchWorkspace = true) to defaultValue, + // Restricts max size up to 10 (defaultValue > 10) + SizeSpec(100f, maxSize = 10) to 10, + SizeSpec(ofAvailableSpace = .5f, maxSize = 10) to 10, + SizeSpec(ofRemainderSpace = .5f, maxSize = 10) to 10, + SizeSpec(matchWorkspace = true, maxSize = 10) to 10, ) for ((sizeSpec, expectedValue) in combinations) { @@ -111,11 +126,13 @@ class SizeSpecTest : AbstractDeviceProfileTest() { fun invalid_values() { val combinations = listOf( + SizeSpec(-1f, 0f, 0f, false), SizeSpec(0f, 1.1f, 0f, false), SizeSpec(0f, -0.1f, 0f, false), SizeSpec(0f, 0f, 1.1f, false), SizeSpec(0f, 0f, -0.1f, false), - SizeSpec(-1f, 0f, 0f, false) + SizeSpec(0f, 0f, 0f, false, -10), + SizeSpec(50f, 0f, 0f, false, 10) ) for (instance in combinations) { diff --git a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt index 51808e3e53..8b99a3a72f 100644 --- a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt +++ b/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt @@ -51,19 +51,23 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=84.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.15808, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false)" + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString()) @@ -74,19 +78,23 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "startPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=1.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=273.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false)" + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString()) @@ -97,19 +105,23 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "startPadding=SizeSpec(fixedSize=21.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=1.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=273.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false)" + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1) @@ -121,19 +133,23 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() { "startPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "endPadding=SizeSpec(fixedSize=58.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "gutter=SizeSpec(fixedSize=42.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.0, " + - "matchWorkspace=false), " + + "matchWorkspace=false, " + + "maxSize=2147483647), " + "cellSize=SizeSpec(fixedSize=0.0, " + "ofAvailableSpace=0.0, " + "ofRemainderSpace=0.25, " + - "matchWorkspace=false)" + + "matchWorkspace=false, " + + "maxSize=2147483647)" + ")" ) }