Merge "Create AllApps responsive spec" into udc-qpr-dev

This commit is contained in:
Thales Lima
2023-06-14 17:09:20 +00:00
committed by Android (Google) Code Review
10 changed files with 644 additions and 0 deletions
+5
View File
@@ -265,6 +265,11 @@
<attr name="maxAvailableSize" />
</declare-styleable>
<declare-styleable name="AllAppsSpec">
<attr name="specType" />
<attr name="maxAvailableSize" />
</declare-styleable>
<declare-styleable name="ProfileDisplayOption">
<attr name="name" />
<attr name="minWidthDps" format="float" />
@@ -0,0 +1,292 @@
/*
* 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.res.XmlResourceParser
import android.util.AttributeSet
import android.util.Log
import android.util.Xml
import com.android.launcher3.R
import com.android.launcher3.util.ResourceHelper
import com.android.launcher3.workspace.CalculatedWorkspaceSpec
import java.io.IOException
import kotlin.math.roundToInt
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParserException
private const val LOG_TAG = "AllAppsSpecs"
class AllAppsSpecs(resourceHelper: ResourceHelper) {
object XmlTags {
const val ALL_APPS_SPECS = "allAppsSpecs"
const val ALL_APPS_SPEC = "allAppsSpec"
const val START_PADDING = "startPadding"
const val END_PADDING = "endPadding"
const val GUTTER = "gutter"
const val CELL_SIZE = "cellSize"
}
val allAppsHeightSpecList = mutableListOf<AllAppsSpec>()
val allAppsWidthSpecList = mutableListOf<AllAppsSpec>()
// 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.ALL_APPS_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.ALL_APPS_SPEC == parser.name
) {
val attrs =
resourceHelper.obtainStyledAttributes(
Xml.asAttributeSet(parser),
R.styleable.AllAppsSpec
)
val maxAvailableSize =
attrs.getDimensionPixelSize(
R.styleable.AllAppsSpec_maxAvailableSize,
0
)
val specType =
AllAppsSpec.SpecType.values()[
attrs.getInt(
R.styleable.AllAppsSpec_specType,
AllAppsSpec.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) {
when (parser.name) {
XmlTags.START_PADDING -> {
startPadding = SizeSpec.create(resourceHelper, attr)
}
XmlTags.END_PADDING -> {
endPadding = SizeSpec.create(resourceHelper, attr)
}
XmlTags.GUTTER -> {
gutter = SizeSpec.create(resourceHelper, attr)
}
XmlTags.CELL_SIZE -> {
cellSize = SizeSpec.create(resourceHelper, attr)
}
}
}
}
if (
startPadding == null ||
endPadding == null ||
gutter == null ||
cellSize == null
) {
throw IllegalStateException(
"All attributes in AllAppsSpec must be defined"
)
}
val allAppsSpec =
AllAppsSpec(
maxAvailableSize,
specType,
startPadding,
endPadding,
gutter,
cellSize
)
if (allAppsSpec.isValid()) {
if (allAppsSpec.specType == AllAppsSpec.SpecType.HEIGHT)
allAppsHeightSpecList.add(allAppsSpec)
else allAppsWidthSpecList.add(allAppsSpec)
} else {
throw IllegalStateException("Invalid AllAppsSpec found.")
}
}
}
if (allAppsWidthSpecList.isEmpty() || allAppsHeightSpecList.isEmpty()) {
throw IllegalStateException(
"AllAppsSpecs is incomplete - " +
"height list size = ${allAppsHeightSpecList.size}; " +
"width list size = ${allAppsWidthSpecList.size}."
)
}
}
}
} catch (e: Exception) {
when (e) {
is IOException,
is XmlPullParserException -> {
throw RuntimeException("Failure parsing all apps specs file.", e)
}
else -> throw e
}
} finally {
parser?.close()
}
}
/**
* Returns the CalculatedAllAppsSpec for width, based on the available width, the AllAppsSpecs
* and the CalculatedWorkspaceSpec.
*/
fun getCalculatedWidthSpec(
columns: Int,
availableWidth: Int,
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
): CalculatedAllAppsSpec {
val widthSpec = allAppsWidthSpecList.first { availableWidth <= it.maxAvailableSize }
return CalculatedAllAppsSpec(availableWidth, columns, widthSpec, calculatedWorkspaceSpec)
}
/**
* Returns the CalculatedAllAppsSpec for height, based on the available height, the AllAppsSpecs
* and the CalculatedWorkspaceSpec.
*/
fun getCalculatedHeightSpec(
rows: Int,
availableHeight: Int,
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
): CalculatedAllAppsSpec {
val heightSpec = allAppsHeightSpecList.first { availableHeight <= it.maxAvailableSize }
return CalculatedAllAppsSpec(availableHeight, rows, heightSpec, calculatedWorkspaceSpec)
}
}
class CalculatedAllAppsSpec(
val availableSpace: Int,
val cells: Int,
private val allAppsSpec: AllAppsSpec,
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
) {
var startPaddingPx: Int = 0
private set
var endPaddingPx: Int = 0
private set
var gutterPx: Int = 0
private set
var cellSizePx: Int = 0
private set
init {
// Copy values from workspace
if (allAppsSpec.startPadding.matchWorkspace)
startPaddingPx = calculatedWorkspaceSpec.startPaddingPx
if (allAppsSpec.endPadding.matchWorkspace)
endPaddingPx = calculatedWorkspaceSpec.endPaddingPx
if (allAppsSpec.gutter.matchWorkspace) gutterPx = calculatedWorkspaceSpec.gutterPx
if (allAppsSpec.cellSize.matchWorkspace) cellSizePx = calculatedWorkspaceSpec.cellSizePx
// Calculate all fixed size first
if (allAppsSpec.startPadding.fixedSize > 0)
startPaddingPx = allAppsSpec.startPadding.fixedSize.roundToInt()
if (allAppsSpec.endPadding.fixedSize > 0)
endPaddingPx = allAppsSpec.endPadding.fixedSize.roundToInt()
if (allAppsSpec.gutter.fixedSize > 0) gutterPx = allAppsSpec.gutter.fixedSize.roundToInt()
if (allAppsSpec.cellSize.fixedSize > 0)
cellSizePx = allAppsSpec.cellSize.fixedSize.roundToInt()
// Calculate all available space next
if (allAppsSpec.startPadding.ofAvailableSpace > 0)
startPaddingPx =
(allAppsSpec.startPadding.ofAvailableSpace * availableSpace).roundToInt()
if (allAppsSpec.endPadding.ofAvailableSpace > 0)
endPaddingPx = (allAppsSpec.endPadding.ofAvailableSpace * availableSpace).roundToInt()
if (allAppsSpec.gutter.ofAvailableSpace > 0)
gutterPx = (allAppsSpec.gutter.ofAvailableSpace * availableSpace).roundToInt()
if (allAppsSpec.cellSize.ofAvailableSpace > 0)
cellSizePx = (allAppsSpec.cellSize.ofAvailableSpace * availableSpace).roundToInt()
// Calculate remainder space last
val gutters = cells - 1
val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells)
val remainderSpace = availableSpace - usedSpace
if (allAppsSpec.startPadding.ofRemainderSpace > 0)
startPaddingPx =
(allAppsSpec.startPadding.ofRemainderSpace * remainderSpace).roundToInt()
if (allAppsSpec.endPadding.ofRemainderSpace > 0)
endPaddingPx = (allAppsSpec.endPadding.ofRemainderSpace * remainderSpace).roundToInt()
if (allAppsSpec.gutter.ofRemainderSpace > 0)
gutterPx = (allAppsSpec.gutter.ofRemainderSpace * remainderSpace).roundToInt()
if (allAppsSpec.cellSize.ofRemainderSpace > 0)
cellSizePx = (allAppsSpec.cellSize.ofRemainderSpace * remainderSpace).roundToInt()
}
override fun toString(): String {
return "CalculatedAllAppsSpec(availableSpace=$availableSpace, " +
"cells=$cells, startPaddingPx=$startPaddingPx, endPaddingPx=$endPaddingPx, " +
"gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
"AllAppsSpec.maxAvailableSize=${allAppsSpec.maxAvailableSize})"
}
}
data class AllAppsSpec(
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, "AllAppsSpec#isValid - maxAvailableSize <= 0")
return false
}
// All specs need to be individually valid
if (!allSpecsAreValid()) {
Log.e(LOG_TAG, "AllAppsSpec#isValid - !allSpecsAreValid()")
return false
}
return true
}
private fun allSpecsAreValid(): Boolean =
startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
}
+4
View File
@@ -39,4 +39,8 @@
<attr name="maxAvailableSize" />
</declare-styleable>
<declare-styleable name="AllAppsSpec">
<attr name="specType" />
<attr name="maxAvailableSize" />
</declare-styleable>
</resources>
@@ -0,0 +1,36 @@
<?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.
-->
<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
<allAppsSpec
launcher:specType="height"
launcher:maxAvailableSize="9999dp">
<!-- missing startPadding -->
<endPadding launcher:fixedSize="0dp" />
<gutter launcher:matchWorkspace="true" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
<allAppsSpec
launcher:specType="width"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:matchWorkspace="true" />
<endPadding launcher:matchWorkspace="true" />
<gutter launcher:matchWorkspace="true" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
</allAppsSpecs>
@@ -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.
-->
<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
<allAppsSpec
launcher:specType="height"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:fixedSize="0dp" />
<endPadding launcher:fixedSize="0dp" />
<!-- more than 1 value in one tag -->
<gutter
launcher:matchWorkspace="true"
launcher:fixedSize="16dp" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
<allAppsSpec
launcher:specType="width"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:matchWorkspace="true" />
<endPadding launcher:matchWorkspace="true" />
<gutter launcher:matchWorkspace="true" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
</allAppsSpecs>
@@ -0,0 +1,37 @@
<?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.
-->
<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
<allAppsSpec
launcher:specType="height"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:fixedSize="0dp" />
<endPadding launcher:fixedSize="0dp" />
<gutter launcher:matchWorkspace="true" />
<!-- value bigger than 1 -->
<cellSize launcher:ofRemainderSpace="1.001" />
</allAppsSpec>
<allAppsSpec
launcher:specType="width"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:matchWorkspace="true" />
<endPadding launcher:matchWorkspace="true" />
<gutter launcher:matchWorkspace="true" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
</allAppsSpecs>
+36
View File
@@ -0,0 +1,36 @@
<?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.
-->
<allAppsSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
<allAppsSpec
launcher:specType="height"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:fixedSize="0dp" />
<endPadding launcher:fixedSize="0dp" />
<gutter launcher:matchWorkspace="true" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
<allAppsSpec
launcher:specType="width"
launcher:maxAvailableSize="9999dp">
<startPadding launcher:matchWorkspace="true" />
<endPadding launcher:matchWorkspace="true" />
<gutter launcher:matchWorkspace="true" />
<cellSize launcher:matchWorkspace="true" />
</allAppsSpec>
</allAppsSpecs>
@@ -0,0 +1,118 @@
/*
* 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.tests.R as TestR
import com.android.launcher3.util.TestResourceHelper
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 AllAppsSpecsTest : AbstractDeviceProfileTest() {
override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
@Before
fun setup() {
initializeVarsForPhone(deviceSpecs["phone"]!!)
}
@Test
fun parseValidFile() {
val allAppsSpecs =
AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file))
assertThat(allAppsSpecs.allAppsHeightSpecList.size).isEqualTo(1)
assertThat(allAppsSpecs.allAppsHeightSpecList[0].toString())
.isEqualTo(
"AllAppsSpec(" +
"maxAvailableSize=26247, " +
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false, " +
"maxSize=2147483647), " +
"endPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false, " +
"maxSize=2147483647), " +
"gutter=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=true, " +
"maxSize=2147483647), " +
"cellSize=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=true, " +
"maxSize=2147483647)" +
")"
)
assertThat(allAppsSpecs.allAppsWidthSpecList.size).isEqualTo(1)
assertThat(allAppsSpecs.allAppsWidthSpecList[0].toString())
.isEqualTo(
"AllAppsSpec(" +
"maxAvailableSize=26247, " +
"specType=WIDTH, " +
"startPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=true, " +
"maxSize=2147483647), " +
"endPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=true, " +
"maxSize=2147483647), " +
"gutter=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=true, " +
"maxSize=2147483647), " +
"cellSize=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=true, " +
"maxSize=2147483647)" +
")"
)
}
@Test(expected = IllegalStateException::class)
fun parseInvalidFile_missingTag_throwsError() {
AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_1))
}
@Test(expected = IllegalStateException::class)
fun parseInvalidFile_moreThanOneValuePerTag_throwsError() {
AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_2))
}
@Test(expected = IllegalStateException::class)
fun parseInvalidFile_valueBiggerThan1_throwsError() {
AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_3))
}
}
@@ -0,0 +1,77 @@
/*
* 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.tests.R as TestR
import com.android.launcher3.util.TestResourceHelper
import com.android.launcher3.workspace.WorkspaceSpecs
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() {
override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
/**
* This test tests:
* - (height spec) copy values from workspace
* - (width spec) copy values from workspace
*/
@Test
fun normalPhone_copiesFromWorkspace() {
val deviceSpec = deviceSpecs["phone"]!!
initializeVarsForPhone(deviceSpec)
val availableWidth = deviceSpec.naturalSize.first
// Hotseat size is roughly 495px on a real device,
// it doesn't need to be precise on unit tests
val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495
val workspaceSpecs =
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth)
val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight)
val allAppsSpecs =
AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file))
with(allAppsSpecs.getCalculatedWidthSpec(4, availableWidth, widthSpec)) {
assertThat(availableSpace).isEqualTo(availableWidth)
assertThat(cells).isEqualTo(4)
assertThat(startPaddingPx).isEqualTo(widthSpec.startPaddingPx)
assertThat(endPaddingPx).isEqualTo(widthSpec.endPaddingPx)
assertThat(gutterPx).isEqualTo(widthSpec.gutterPx)
assertThat(cellSizePx).isEqualTo(widthSpec.cellSizePx)
}
with(allAppsSpecs.getCalculatedHeightSpec(5, availableHeight, heightSpec)) {
assertThat(availableSpace).isEqualTo(availableHeight)
assertThat(cells).isEqualTo(5)
assertThat(startPaddingPx).isEqualTo(0)
assertThat(endPaddingPx).isEqualTo(0)
assertThat(gutterPx).isEqualTo(heightSpec.gutterPx)
assertThat(cellSizePx).isEqualTo(heightSpec.cellSizePx)
}
}
}
@@ -31,6 +31,7 @@ class TestResourceHelper(private val context: Context, specsFileId: Int) :
styleId.contentEquals(R.styleable.SizeSpec) -> TestR.styleable.SizeSpec
styleId.contentEquals(R.styleable.WorkspaceSpec) -> TestR.styleable.WorkspaceSpec
styleId.contentEquals(R.styleable.FolderSpec) -> TestR.styleable.FolderSpec
styleId.contentEquals(R.styleable.AllAppsSpec) -> TestR.styleable.AllAppsSpec
else -> styleId.clone()
}