Add FolderSpec for responsive grid support
Adds responsive grid implementation for folders. It follows the same concept as WorkspaceSpecs, so we have a parser similar to WorkspaceSpecs for FolderSpecs.
Bug: 284155638
Flag: ENABLE_RESPONSIVE_WORKSPACE
Test: FolderSpecsTest
Test: CalculatedFolderSpecsTest
Change-Id: Iea6d7d88ef42d1273aed7cf2ed5b397035518a52
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 80df78a..1be1a1a 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -259,6 +259,11 @@
<attr name="matchWorkspace" format="boolean" />
</declare-styleable>
+ <declare-styleable name="FolderSpec">
+ <attr name="specType" />
+ <attr name="maxAvailableSize" />
+ </declare-styleable>
+
<declare-styleable name="ProfileDisplayOption">
<attr name="name" />
<attr name="minWidthDps" format="float" />
diff --git a/src/com/android/launcher3/responsive/FolderSpecs.kt b/src/com/android/launcher3/responsive/FolderSpecs.kt
new file mode 100644
index 0000000..be97cf8
--- /dev/null
+++ b/src/com/android/launcher3/responsive/FolderSpecs.kt
@@ -0,0 +1,280 @@
+package com.android.launcher3.responsive
+
+import android.content.res.XmlResourceParser
+import android.util.AttributeSet
+import android.util.Log
+import android.util.Xml
+import com.android.launcher3.R
+import com.android.launcher3.responsive.FolderSpec.*
+import com.android.launcher3.util.ResourceHelper
+import com.android.launcher3.workspace.CalculatedWorkspaceSpec
+import com.android.launcher3.workspace.WorkspaceSpec
+import java.io.IOException
+import org.xmlpull.v1.XmlPullParser
+import org.xmlpull.v1.XmlPullParserException
+
+private const val LOG_TAG = "FolderSpecs"
+
+class FolderSpecs(resourceHelper: ResourceHelper) {
+
+ object XmlTags {
+ const val FOLDER_SPECS = "folderSpecs"
+
+ const val FOLDER_SPEC = "folderSpec"
+ const val START_PADDING = "startPadding"
+ const val END_PADDING = "endPadding"
+ const val GUTTER = "gutter"
+ const val CELL_SIZE = "cellSize"
+ }
+
+ private val _heightSpecs = mutableListOf<FolderSpec>()
+ val heightSpecs: List<FolderSpec>
+ get() = _heightSpecs
+
+ private val _widthSpecs = mutableListOf<FolderSpec>()
+ val widthSpecs: List<FolderSpec>
+ get() = _widthSpecs
+
+ // TODO(b/286538013) Remove this init after a more generic or reusable parser is created
+ init {
+ var parser: XmlResourceParser? = null
+ try {
+ parser = resourceHelper.getXml()
+ val depth = parser.depth
+ var type: Int
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > depth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ if (type == XmlPullParser.START_TAG && XmlTags.FOLDER_SPECS == parser.name) {
+ val displayDepth = parser.depth
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > displayDepth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ if (type == XmlPullParser.START_TAG && XmlTags.FOLDER_SPEC == parser.name) {
+ val attrs =
+ resourceHelper.obtainStyledAttributes(
+ Xml.asAttributeSet(parser),
+ R.styleable.FolderSpec
+ )
+ val maxAvailableSize =
+ attrs.getDimensionPixelSize(
+ R.styleable.FolderSpec_maxAvailableSize,
+ 0
+ )
+ val specType =
+ SpecType.values()[
+ attrs.getInt(
+ R.styleable.FolderSpec_specType,
+ SpecType.HEIGHT.ordinal
+ )]
+ attrs.recycle()
+
+ var startPadding: SizeSpec? = null
+ var endPadding: SizeSpec? = null
+ var gutter: SizeSpec? = null
+ var cellSize: SizeSpec? = null
+
+ val limitDepth = parser.depth
+ while (
+ (parser.next().also { type = it } != XmlPullParser.END_TAG ||
+ parser.depth > limitDepth) && type != XmlPullParser.END_DOCUMENT
+ ) {
+ val attr: AttributeSet = Xml.asAttributeSet(parser)
+ if (type == XmlPullParser.START_TAG) {
+ val sizeSpec = SizeSpec.create(resourceHelper, attr)
+ when (parser.name) {
+ XmlTags.START_PADDING -> startPadding = sizeSpec
+ XmlTags.END_PADDING -> endPadding = sizeSpec
+ XmlTags.GUTTER -> gutter = sizeSpec
+ XmlTags.CELL_SIZE -> cellSize = sizeSpec
+ }
+ }
+ }
+
+ checkNotNull(startPadding) {
+ "Attr 'startPadding' in FolderSpec must be defined."
+ }
+ checkNotNull(endPadding) {
+ "Attr 'endPadding' in FolderSpec must be defined."
+ }
+ checkNotNull(gutter) { "Attr 'gutter' in FolderSpec must be defined." }
+ checkNotNull(cellSize) {
+ "Attr 'cellSize' in FolderSpec must be defined."
+ }
+
+ val folderSpec =
+ FolderSpec(
+ maxAvailableSize,
+ specType,
+ startPadding,
+ endPadding,
+ gutter,
+ cellSize
+ )
+
+ check(folderSpec.isValid()) { "Invalid FolderSpec found." }
+
+ if (folderSpec.specType == SpecType.HEIGHT) {
+ _heightSpecs += folderSpec
+ } else {
+ _widthSpecs += folderSpec
+ }
+ }
+ }
+
+ check(_widthSpecs.isNotEmpty() && _heightSpecs.isNotEmpty()) {
+ "FolderSpecs is incomplete - " +
+ "height list size = ${_heightSpecs.size}; " +
+ "width list size = ${_widthSpecs.size}."
+ }
+ }
+ }
+ } catch (e: Exception) {
+ when (e) {
+ is IOException,
+ is XmlPullParserException -> {
+ throw RuntimeException("Failure parsing folder specs file.", e)
+ }
+ else -> throw e
+ }
+ } finally {
+ parser?.close()
+ }
+ }
+
+ /**
+ * Returns the [CalculatedFolderSpec] for width, based on the available width, FolderSpecs and
+ * WorkspaceSpecs.
+ */
+ fun getWidthSpec(
+ columns: Int,
+ availableWidth: Int,
+ workspaceSpec: CalculatedWorkspaceSpec
+ ): CalculatedFolderSpec {
+ check(workspaceSpec.workspaceSpec.specType == WorkspaceSpec.SpecType.WIDTH) {
+ "Invalid specType for CalculatedWorkspaceSpec. " +
+ "Expected: ${WorkspaceSpec.SpecType.WIDTH} - " +
+ "Found: ${workspaceSpec.workspaceSpec.specType}}"
+ }
+
+ val widthSpec = _widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize }
+ check(widthSpec != null) { "No FolderSpec for width spec found with $availableWidth." }
+
+ return convertToCalculatedFolderSpec(widthSpec, availableWidth, columns, workspaceSpec)
+ }
+
+ /**
+ * Returns the [CalculatedFolderSpec] for height, based on the available height, FolderSpecs and
+ * WorkspaceSpecs.
+ */
+ fun getHeightSpec(
+ rows: Int,
+ availableHeight: Int,
+ workspaceSpec: CalculatedWorkspaceSpec
+ ): CalculatedFolderSpec {
+ check(workspaceSpec.workspaceSpec.specType == WorkspaceSpec.SpecType.HEIGHT) {
+ "Invalid specType for CalculatedWorkspaceSpec. " +
+ "Expected: ${WorkspaceSpec.SpecType.HEIGHT} - " +
+ "Found: ${workspaceSpec.workspaceSpec.specType}}"
+ }
+
+ val heightSpec = _heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
+ check(heightSpec != null) { "No FolderSpec for height spec found with $availableHeight." }
+
+ return convertToCalculatedFolderSpec(heightSpec, availableHeight, rows, workspaceSpec)
+ }
+}
+
+data class CalculatedFolderSpec(
+ val startPaddingPx: Int,
+ val endPaddingPx: Int,
+ val gutterPx: Int,
+ val cellSizePx: Int,
+ val availableSpace: Int,
+ val cells: Int
+)
+
+/**
+ * Responsive folder specs to be used to calculate the paddings, gutter and cell size for folders in
+ * the workspace.
+ *
+ * @param maxAvailableSize indicates the breakpoint to use this specification.
+ * @param specType indicates whether the paddings and gutters will be applied vertically or
+ * horizontally.
+ * @param startPadding padding used at the top or left (right in RTL) in the workspace folder.
+ * @param endPadding padding used at the bottom or right (left in RTL) in the workspace folder.
+ * @param gutter the space between the cells vertically or horizontally depending on the [specType].
+ * @param cellSize height or width of the cell depending on the [specType].
+ */
+data class FolderSpec(
+ val maxAvailableSize: Int,
+ val specType: SpecType,
+ val startPadding: SizeSpec,
+ val endPadding: SizeSpec,
+ val gutter: SizeSpec,
+ val cellSize: SizeSpec
+) {
+
+ enum class SpecType {
+ HEIGHT,
+ WIDTH
+ }
+
+ fun isValid(): Boolean {
+ if (maxAvailableSize <= 0) {
+ Log.e(LOG_TAG, "FolderSpec#isValid - maxAvailableSize <= 0")
+ return false
+ }
+
+ // All specs are valid
+ if (
+ !(startPadding.isValid() &&
+ endPadding.isValid() &&
+ gutter.isValid() &&
+ cellSize.isValid())
+ ) {
+ Log.e(LOG_TAG, "FolderSpec#isValid - !allSpecsAreValid()")
+ return false
+ }
+
+ return true
+ }
+}
+
+/** Helper function to convert [FolderSpec] to [CalculatedFolderSpec] */
+private fun convertToCalculatedFolderSpec(
+ folderSpec: FolderSpec,
+ availableSpace: Int,
+ cells: Int,
+ workspaceSpec: CalculatedWorkspaceSpec
+): CalculatedFolderSpec {
+ // Map if is fixedSize, ofAvailableSpace or matchWorkspace
+ var startPaddingPx =
+ folderSpec.startPadding.getCalculatedValue(availableSpace, workspaceSpec.startPaddingPx)
+ var endPaddingPx =
+ folderSpec.endPadding.getCalculatedValue(availableSpace, workspaceSpec.endPaddingPx)
+ var gutterPx = folderSpec.gutter.getCalculatedValue(availableSpace, workspaceSpec.gutterPx)
+ var cellSizePx =
+ folderSpec.cellSize.getCalculatedValue(availableSpace, workspaceSpec.cellSizePx)
+
+ // Remainder space
+ val gutters = cells - 1
+ val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells)
+ val remainderSpace = availableSpace - usedSpace
+
+ startPaddingPx = folderSpec.startPadding.getRemainderSpaceValue(remainderSpace, startPaddingPx)
+ endPaddingPx = folderSpec.endPadding.getRemainderSpaceValue(remainderSpace, endPaddingPx)
+ gutterPx = folderSpec.gutter.getRemainderSpaceValue(remainderSpace, gutterPx)
+ cellSizePx = folderSpec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx)
+
+ return CalculatedFolderSpec(
+ startPaddingPx = startPaddingPx,
+ endPaddingPx = endPaddingPx,
+ gutterPx = gutterPx,
+ cellSizePx = cellSizePx,
+ availableSpace = availableSpace,
+ cells = cells
+ )
+}
diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt
index bf5ca1c..407a212 100644
--- a/src/com/android/launcher3/responsive/SizeSpec.kt
+++ b/src/com/android/launcher3/responsive/SizeSpec.kt
@@ -6,14 +6,45 @@
import android.util.TypedValue
import com.android.launcher3.R
import com.android.launcher3.util.ResourceHelper
+import kotlin.math.roundToInt
+/**
+ * [SizeSpec] is an attribute used to represent a property in the responsive grid specs.
+ *
+ * @param fixedSize a fixed size in dp to be used
+ * @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.
+ */
data class SizeSpec(
- val fixedSize: Float,
- val ofAvailableSpace: Float,
- val ofRemainderSpace: Float,
- val matchWorkspace: Boolean
+ val fixedSize: Float = 0f,
+ val ofAvailableSpace: Float = 0f,
+ val ofRemainderSpace: Float = 0f,
+ val matchWorkspace: Boolean = false
) {
+ /** 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
+ }
+ }
+
+ /**
+ * Calculates the [SizeSpec] value when remainder space value is defined. If no remainderSpace
+ * is 0, returns a default value.
+ */
+ fun getRemainderSpaceValue(remainderSpace: Int, defaultValue: Int): Int {
+ return if (ofRemainderSpace > 0) {
+ (ofRemainderSpace * remainderSpace).roundToInt()
+ } else {
+ defaultValue
+ }
+ }
+
fun isValid(): Boolean {
// All attributes are empty
if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) {
@@ -48,7 +79,8 @@
}
companion object {
- private const val TAG = "WorkspaceSpecs::SizeSpec"
+ private const val TAG = "SizeSpec"
+
private fun getValue(a: TypedArray, index: Int): Float {
return when (a.getType(index)) {
TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat()
diff --git a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
index 4815924..8cc0c59 100644
--- a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
+++ b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt
@@ -44,6 +44,7 @@
val workspaceHeightSpecList = mutableListOf<WorkspaceSpec>()
val workspaceWidthSpecList = mutableListOf<WorkspaceSpec>()
+ // TODO(b/286538013) Remove this init after a more generic or reusable parser is created
init {
try {
val parser: XmlResourceParser = resourceHelper.getXml()
diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml
index cb6da3b..54f0381 100644
--- a/tests/res/values/attrs.xml
+++ b/tests/res/values/attrs.xml
@@ -32,4 +32,10 @@
<attr name="ofRemainderSpace" format="float" />
<attr name="matchWorkspace" format="boolean" />
</declare-styleable>
+
+ <declare-styleable name="FolderSpec">
+ <attr name="specType" />
+ <attr name="maxAvailableSize" />
+ </declare-styleable>
+
</resources>
diff --git a/tests/res/xml/invalid_folders_specs_1.xml b/tests/res/xml/invalid_folders_specs_1.xml
new file mode 100644
index 0000000..0864249
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_1.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<!-- Tablet - 6x5 portrait -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <!-- missing startPadding -->
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_2.xml b/tests/res/xml/invalid_folders_specs_2.xml
new file mode 100644
index 0000000..0b7dd62
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_2.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<!-- Tablet - 6x5 portrait -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <!-- more than 1 value in one tag -->
+ <gutter
+ launcher:ofAvailableSpace="0.0125"
+ launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_3.xml b/tests/res/xml/invalid_folders_specs_3.xml
new file mode 100644
index 0000000..83fd3e1
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_3.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+
+<!-- Tablet - 6x5 portrait - More the one value first gutter -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <!-- value bigger than 1 -->
+ <cellSize launcher:ofRemainderSpace="1.001" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_4.xml b/tests/res/xml/invalid_folders_specs_4.xml
new file mode 100644
index 0000000..2d8c730
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_4.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<!-- missing height spec -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/invalid_folders_specs_5.xml b/tests/res/xml/invalid_folders_specs_5.xml
new file mode 100644
index 0000000..b4f1f4d
--- /dev/null
+++ b/tests/res/xml/invalid_folders_specs_5.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<!-- missing breakpoints > 800dp -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="104dp" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/res/xml/valid_folders_specs.xml b/tests/res/xml/valid_folders_specs.xml
new file mode 100644
index 0000000..0c45544
--- /dev/null
+++ b/tests/res/xml/valid_folders_specs.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<folderSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="800dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+ <folderSpec launcher:specType="width" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="16dp" />
+ <endPadding launcher:fixedSize="16dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:fixedSize="102dp" />
+ </folderSpec>
+
+ <!-- Height spec is fixed -->
+ <folderSpec launcher:specType="height" launcher:maxAvailableSize="9999dp">
+ <startPadding launcher:fixedSize="24dp" />
+ <!-- mapped to footer height size -->
+ <endPadding launcher:fixedSize="64dp" />
+ <gutter launcher:fixedSize="16dp" />
+ <cellSize launcher:matchWorkspace="true" />
+ </folderSpec>
+</folderSpecs>
diff --git a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt
new file mode 100644
index 0000000..c14722c
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.android.launcher3.testing.shared.ResourceUtils
+import com.android.launcher3.tests.R
+import com.android.launcher3.util.TestResourceHelper
+import com.android.launcher3.workspace.WorkspaceSpecs
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ private val deviceSpec = deviceSpecs["phone"]!!
+
+ @Before
+ fun setup() {
+ initializeVarsForPhone(deviceSpec)
+ }
+
+ @Test
+ fun validate_matchWidthWorkspace() {
+ val columns = 6
+
+ // Loading workspace specs
+ val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file)
+ val workspaceSpecs = WorkspaceSpecs(resourceHelperWorkspace)
+
+ // Loading folders specs
+ val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelperFolder)
+
+ assertThat(folderSpecs.widthSpecs.size).isEqualTo(2)
+ assertThat(folderSpecs.widthSpecs[0].cellSize.matchWorkspace).isEqualTo(true)
+ assertThat(folderSpecs.widthSpecs[1].cellSize.matchWorkspace).isEqualTo(false)
+
+ // Validate width spec <= 800
+ var availableWidth = deviceSpec.naturalSize.first
+ var calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth)
+ var calculatedWidthFolderSpec =
+ folderSpecs.getWidthSpec(columns, availableWidth, calculatedWorkspace)
+ with(calculatedWidthFolderSpec) {
+ assertThat(availableSpace).isEqualTo(availableWidth)
+ assertThat(cells).isEqualTo(columns)
+ assertThat(startPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(endPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(gutterPx).isEqualTo(16.dpToPx())
+ assertThat(cellSizePx).isEqualTo(calculatedWorkspace.cellSizePx)
+ }
+
+ // Validate width spec > 800
+ availableWidth = 2000.dpToPx()
+ calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth)
+ calculatedWidthFolderSpec =
+ folderSpecs.getWidthSpec(columns, availableWidth, calculatedWorkspace)
+ with(calculatedWidthFolderSpec) {
+ assertThat(availableSpace).isEqualTo(availableWidth)
+ assertThat(cells).isEqualTo(columns)
+ assertThat(startPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(endPaddingPx).isEqualTo(16.dpToPx())
+ assertThat(gutterPx).isEqualTo(16.dpToPx())
+ assertThat(cellSizePx).isEqualTo(102.dpToPx())
+ }
+ }
+
+ @Test
+ fun validate_matchHeightWorkspace() {
+ // Hotseat is roughly 495px on a real device, it doesn't need to be precise on unit tests
+ val hotseatSize = 495
+ val statusBarHeight = deviceSpec.statusBarNaturalPx
+ val availableHeight = deviceSpec.naturalSize.second - statusBarHeight - hotseatSize
+ val rows = 5
+
+ // Loading workspace specs
+ val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file)
+ val workspaceSpecs = WorkspaceSpecs(resourceHelperWorkspace)
+
+ // Loading folders specs
+ val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelperFolder)
+
+ assertThat(folderSpecs.heightSpecs.size).isEqualTo(1)
+ assertThat(folderSpecs.heightSpecs[0].cellSize.matchWorkspace).isEqualTo(true)
+
+ // Validate height spec
+ val calculatedWorkspace = workspaceSpecs.getCalculatedHeightSpec(rows, availableHeight)
+ val calculatedFolderSpec =
+ folderSpecs.getHeightSpec(rows, availableHeight, calculatedWorkspace)
+ with(calculatedFolderSpec) {
+ assertThat(availableSpace).isEqualTo(availableHeight)
+ assertThat(cells).isEqualTo(rows)
+ assertThat(startPaddingPx).isEqualTo(24.dpToPx())
+ assertThat(endPaddingPx).isEqualTo(64.dpToPx())
+ assertThat(gutterPx).isEqualTo(16.dpToPx())
+ assertThat(cellSizePx).isEqualTo(calculatedWorkspace.cellSizePx)
+ }
+ }
+
+ private fun Int.dpToPx(): Int {
+ return ResourceUtils.pxFromDp(this.toFloat(), context!!.resources.displayMetrics)
+ }
+}
diff --git a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt
new file mode 100644
index 0000000..796bf9a
--- /dev/null
+++ b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt
@@ -0,0 +1,269 @@
+/*
+ * 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.launcher3.responsive
+
+import android.content.Context
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.AbstractDeviceProfileTest
+import com.android.launcher3.testing.shared.ResourceUtils
+import com.android.launcher3.tests.R
+import com.android.launcher3.util.TestResourceHelper
+import com.android.launcher3.workspace.CalculatedWorkspaceSpec
+import com.android.launcher3.workspace.WorkspaceSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class FolderSpecsTest : AbstractDeviceProfileTest() {
+ override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
+
+ @Before
+ fun setup() {
+ initializeVarsForPhone(deviceSpecs["tablet"]!!)
+ }
+
+ @Test
+ fun parseValidFile() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+
+ val sizeSpec16 = SizeSpec(16f.dpToPx())
+ val widthSpecsExpected =
+ listOf(
+ FolderSpec(
+ maxAvailableSize = 800.dpToPx(),
+ specType = FolderSpec.SpecType.WIDTH,
+ startPadding = sizeSpec16,
+ endPadding = sizeSpec16,
+ gutter = sizeSpec16,
+ cellSize = SizeSpec(matchWorkspace = true)
+ ),
+ FolderSpec(
+ maxAvailableSize = 9999.dpToPx(),
+ specType = FolderSpec.SpecType.WIDTH,
+ startPadding = sizeSpec16,
+ endPadding = sizeSpec16,
+ gutter = sizeSpec16,
+ cellSize = SizeSpec(102f.dpToPx())
+ )
+ )
+
+ val heightSpecsExpected =
+ FolderSpec(
+ maxAvailableSize = 9999.dpToPx(),
+ specType = FolderSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(24f.dpToPx()),
+ endPadding = SizeSpec(64f.dpToPx()),
+ gutter = sizeSpec16,
+ cellSize = SizeSpec(matchWorkspace = true)
+ )
+
+ assertThat(folderSpecs.widthSpecs.size).isEqualTo(widthSpecsExpected.size)
+ assertThat(folderSpecs.widthSpecs[0]).isEqualTo(widthSpecsExpected[0])
+ assertThat(folderSpecs.widthSpecs[1]).isEqualTo(widthSpecsExpected[1])
+
+ assertThat(folderSpecs.heightSpecs.size).isEqualTo(1)
+ assertThat(folderSpecs.heightSpecs[0]).isEqualTo(heightSpecsExpected)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingTag_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_1)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_moreThanOneValuePerTag_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_2)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_valueBiggerThan1_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_3)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingSpecs_throwsError() {
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_4)
+ FolderSpecs(resourceHelper)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingWidthBreakpoint_throwsError() {
+ val availableSpace = 900.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.WIDTH,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun parseInvalidFile_missingHeightBreakpoint_throwsError() {
+ val availableSpace = 900.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_5)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ @Test
+ fun retrievesCalculatedWidthSpec() {
+ val availableSpace = 800.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.WIDTH,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val expectedResult =
+ CalculatedFolderSpec(
+ startPaddingPx = 16.dpToPx(),
+ endPaddingPx = 16.dpToPx(),
+ gutterPx = 16.dpToPx(),
+ cellSizePx = calculatedWorkspaceSpec.cellSizePx,
+ availableSpace = availableSpace,
+ cells = cells
+ )
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ val calculatedWidthSpec =
+ folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ assertThat(calculatedWidthSpec).isEqualTo(expectedResult)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun retrievesCalculatedWidthSpec_invalidCalculatedWorkspaceSpecType_throwsError() {
+ val availableSpace = 10.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ @Test
+ fun retrievesCalculatedHeightSpec() {
+ val availableSpace = 700.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.HEIGHT,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val expectedResult =
+ CalculatedFolderSpec(
+ startPaddingPx = 24.dpToPx(),
+ endPaddingPx = 64.dpToPx(),
+ gutterPx = 16.dpToPx(),
+ cellSizePx = calculatedWorkspaceSpec.cellSizePx,
+ availableSpace = availableSpace,
+ cells = cells
+ )
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ val calculatedHeightSpec =
+ folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ assertThat(calculatedHeightSpec).isEqualTo(expectedResult)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun retrievesCalculatedHeightSpec_invalidCalculatedWorkspaceSpecType_throwsError() {
+ val availableSpace = 10.dpToPx()
+ val cells = 3
+
+ val workspaceSpec =
+ WorkspaceSpec(
+ maxAvailableSize = availableSpace,
+ specType = WorkspaceSpec.SpecType.WIDTH,
+ startPadding = SizeSpec(fixedSize = 10f),
+ endPadding = SizeSpec(fixedSize = 10f),
+ gutter = SizeSpec(fixedSize = 10f),
+ cellSize = SizeSpec(fixedSize = 10f)
+ )
+ val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
+
+ val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
+ val folderSpecs = FolderSpecs(resourceHelper)
+ folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
+ }
+
+ private fun Float.dpToPx(): Float {
+ return ResourceUtils.pxFromDp(this, context!!.resources.displayMetrics).toFloat()
+ }
+
+ private fun Int.dpToPx(): Int {
+ return ResourceUtils.pxFromDp(this.toFloat(), context!!.resources.displayMetrics)
+ }
+}
diff --git a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
index 426777d..5db86ff 100644
--- a/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
+++ b/tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
@@ -22,6 +22,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.AbstractDeviceProfileTest
import com.google.common.truth.Truth.assertThat
+import kotlin.math.roundToInt
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,6 +54,42 @@
}
@Test
+ fun validate_getCalculatedValue() {
+ val availableSpace = 100
+ val matchWorkspaceValue = 101
+ val combinations =
+ listOf(
+ SizeSpec(100f) to 100,
+ SizeSpec(ofAvailableSpace = .5f) to (availableSpace * .5f).roundToInt(),
+ SizeSpec(ofRemainderSpace = .5f) to 0,
+ SizeSpec(matchWorkspace = true) to matchWorkspaceValue
+ )
+
+ for ((sizeSpec, expectedValue) in combinations) {
+ val value = sizeSpec.getCalculatedValue(availableSpace, matchWorkspaceValue)
+ assertThat(value).isEqualTo(expectedValue)
+ }
+ }
+
+ @Test
+ fun validate_getRemainderSpaceValue() {
+ val remainderSpace = 100
+ val defaultValue = 10
+ val combinations =
+ listOf(
+ SizeSpec(100f) to defaultValue,
+ SizeSpec(ofAvailableSpace = .5f) to defaultValue,
+ SizeSpec(ofRemainderSpace = .5f) to (remainderSpace * .5f).roundToInt(),
+ SizeSpec(matchWorkspace = true) to defaultValue
+ )
+
+ for ((sizeSpec, expectedValue) in combinations) {
+ val value = sizeSpec.getRemainderSpaceValue(remainderSpace, defaultValue)
+ assertThat(value).isEqualTo(expectedValue)
+ }
+ }
+
+ @Test
fun multiple_values_assigned() {
val combinations =
listOf(
diff --git a/tests/src/com/android/launcher3/util/TestResourceHelper.kt b/tests/src/com/android/launcher3/util/TestResourceHelper.kt
index 3f0054e..691a069 100644
--- a/tests/src/com/android/launcher3/util/TestResourceHelper.kt
+++ b/tests/src/com/android/launcher3/util/TestResourceHelper.kt
@@ -23,12 +23,17 @@
import com.android.launcher3.tests.R as TestR
import kotlin.IntArray
-class TestResourceHelper(private val context: Context, private val specsFileId: Int) :
+class TestResourceHelper(private val context: Context, specsFileId: Int) :
ResourceHelper(context, specsFileId) {
override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray {
- var clone = styleId.clone()
- if (styleId == R.styleable.SizeSpec) clone = TestR.styleable.SizeSpec
- else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec
+ val clone =
+ when {
+ styleId.contentEquals(R.styleable.SizeSpec) -> TestR.styleable.SizeSpec
+ styleId.contentEquals(R.styleable.WorkspaceSpec) -> TestR.styleable.WorkspaceSpec
+ styleId.contentEquals(R.styleable.FolderSpec) -> TestR.styleable.FolderSpec
+ else -> styleId.clone()
+ }
+
return context.obtainStyledAttributes(attrs, clone)
}
}