Merge "Adding match workspace to SizeSpec for responsive grid support" into udc-qpr-dev am: d22a85cd64

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/23587361

Change-Id: I42622131302d1e34bb7d7c52029bcc7094626c9e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jordan Silva
2023-06-09 20:51:40 +00:00
committed by Automerger Merge Worker
8 changed files with 275 additions and 83 deletions

View File

@@ -204,6 +204,7 @@
<!-- File that contains the specs for the workspace.
Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
<attr name="workspaceSpecsId" format="reference" />
<!-- By default all categories are enabled -->
<attr name="deviceCategory" format="integer">
<!-- Enable on phone only -->
@@ -251,10 +252,11 @@
<attr name="maxAvailableSize" format="dimension" />
</declare-styleable>
<declare-styleable name="SpecSize">
<declare-styleable name="SizeSpec">
<attr name="fixedSize" format="dimension" />
<attr name="ofAvailableSpace" format="float" />
<attr name="ofRemainderSpace" format="float" />
<attr name="matchWorkspace" format="boolean" />
</declare-styleable>
<declare-styleable name="ProfileDisplayOption">

View File

@@ -0,0 +1,73 @@
package com.android.launcher3.responsive
import android.content.res.TypedArray
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import com.android.launcher3.R
import com.android.launcher3.util.ResourceHelper
data class SizeSpec(
val fixedSize: Float,
val ofAvailableSpace: Float,
val ofRemainderSpace: Float,
val matchWorkspace: Boolean
) {
fun isValid(): Boolean {
// All attributes are empty
if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f && !matchWorkspace) {
Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
return false
}
// More than one attribute is filled
val attrCount =
(if (fixedSize > 0) 1 else 0) +
(if (ofAvailableSpace > 0) 1 else 0) +
(if (ofRemainderSpace > 0) 1 else 0) +
(if (matchWorkspace) 1 else 0)
if (attrCount > 1) {
Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
return false
}
// Values should be between 0 and 1
if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
return false
}
// Invalid fixed size
if (fixedSize < 0f) {
Log.e(TAG, "SizeSpec#isValid - values should be bigger or equal to zero.")
return false
}
return true
}
companion object {
private const val TAG = "WorkspaceSpecs::SizeSpec"
private fun getValue(a: TypedArray, index: Int): Float {
return when (a.getType(index)) {
TypedValue.TYPE_DIMENSION -> a.getDimensionPixelSize(index, 0).toFloat()
TypedValue.TYPE_FLOAT -> a.getFloat(index, 0f)
else -> 0f
}
}
fun create(resourceHelper: ResourceHelper, attrs: AttributeSet): SizeSpec {
val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SizeSpec)
val fixedSize = getValue(styledAttrs, R.styleable.SizeSpec_fixedSize)
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)
styledAttrs.recycle()
return SizeSpec(fixedSize, ofAvailableSpace, ofRemainderSpace, matchWorkspace)
}
}
}

View File

@@ -16,13 +16,12 @@
package com.android.launcher3.workspace
import android.content.res.TypedArray
import android.content.res.XmlResourceParser
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.util.Xml
import com.android.launcher3.R
import com.android.launcher3.responsive.SizeSpec
import com.android.launcher3.util.ResourceHelper
import java.io.IOException
import kotlin.math.roundToInt
@@ -95,16 +94,16 @@ class WorkspaceSpecs(resourceHelper: ResourceHelper) {
if (type == XmlPullParser.START_TAG) {
when (parser.name) {
XmlTags.START_PADDING -> {
startPadding = SizeSpec(resourceHelper, attr)
startPadding = SizeSpec.create(resourceHelper, attr)
}
XmlTags.END_PADDING -> {
endPadding = SizeSpec(resourceHelper, attr)
endPadding = SizeSpec.create(resourceHelper, attr)
}
XmlTags.GUTTER -> {
gutter = SizeSpec(resourceHelper, attr)
gutter = SizeSpec.create(resourceHelper, attr)
}
XmlTags.CELL_SIZE -> {
cellSize = SizeSpec(resourceHelper, attr)
cellSize = SizeSpec.create(resourceHelper, attr)
}
}
}
@@ -270,61 +269,12 @@ data class WorkspaceSpec(
}
private fun allSpecsAreValid(): Boolean =
startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
}
class SizeSpec(resourceHelper: ResourceHelper, attrs: AttributeSet) {
val fixedSize: Float
val ofAvailableSpace: Float
val ofRemainderSpace: Float
init {
val styledAttrs = resourceHelper.obtainStyledAttributes(attrs, R.styleable.SpecSize)
fixedSize = getValue(styledAttrs, R.styleable.SpecSize_fixedSize)
ofAvailableSpace = getValue(styledAttrs, R.styleable.SpecSize_ofAvailableSpace)
ofRemainderSpace = getValue(styledAttrs, R.styleable.SpecSize_ofRemainderSpace)
styledAttrs.recycle()
}
private fun getValue(a: TypedArray, index: Int): Float {
if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
return a.getDimensionPixelSize(index, 0).toFloat()
} else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
return a.getFloat(index, 0f)
}
return 0f
}
fun isValid(): Boolean {
// All attributes are empty
if (fixedSize < 0f && ofAvailableSpace <= 0f && ofRemainderSpace <= 0f) {
Log.e(TAG, "SizeSpec#isValid - all attributes are empty")
return false
}
// More than one attribute is filled
val attrCount =
(if (fixedSize > 0) 1 else 0) +
(if (ofAvailableSpace > 0) 1 else 0) +
(if (ofRemainderSpace > 0) 1 else 0)
if (attrCount > 1) {
Log.e(TAG, "SizeSpec#isValid - more than one attribute is filled")
return false
}
// Values should be between 0 and 1
if (ofAvailableSpace !in 0f..1f || ofRemainderSpace !in 0f..1f) {
Log.e(TAG, "SizeSpec#isValid - values should be between 0 and 1")
return false
}
return true
}
override fun toString(): String {
return "SizeSpec(fixedSize=$fixedSize, ofAvailableSpace=$ofAvailableSpace, " +
"ofRemainderSpace=$ofRemainderSpace)"
}
startPadding.isValid() &&
endPadding.isValid() &&
gutter.isValid() &&
cellSize.isValid() &&
!startPadding.matchWorkspace &&
!endPadding.matchWorkspace &&
!gutter.matchWorkspace &&
!cellSize.matchWorkspace
}

View File

@@ -26,10 +26,10 @@
<attr name="maxAvailableSize" format="dimension" />
</declare-styleable>
<declare-styleable name="SpecSize">
<declare-styleable name="SizeSpec">
<attr name="fixedSize" format="dimension" />
<attr name="ofAvailableSpace" format="float" />
<attr name="ofRemainderSpace" format="float" />
<attr name="matchWorkspace" format="boolean" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,58 @@
<?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.
-->
<workspaceSpecs xmlns:launcher="http://schemas.android.com/apk/res-auto">
<workspaceSpec
launcher:specType="height"
launcher:maxAvailableSize="648dp">
<startPadding
launcher:ofAvailableSpace="0.0125" />
<endPadding
launcher:ofAvailableSpace="0.05" />
<!-- value in workspace spec using matchWorkspace -->
<gutter
launcher:matchWorkspace="true" />
<cellSize
launcher:ofRemainderSpace="0.2" />
</workspaceSpec>
<workspaceSpec
launcher:specType="height"
launcher:maxAvailableSize="9999dp">
<startPadding
launcher:ofAvailableSpace="0.0306" />
<endPadding
launcher:ofAvailableSpace="0.068" />
<gutter
launcher:fixedSize="16dp" />
<cellSize
launcher:ofRemainderSpace="0.2" />
</workspaceSpec>
<!-- Width spec is always the same -->
<workspaceSpec
launcher:specType="width"
launcher:maxAvailableSize="9999dp">
<startPadding
launcher:ofRemainderSpace="0.21436227" />
<endPadding
launcher:ofRemainderSpace="0.21436227" />
<gutter
launcher:ofRemainderSpace="0.11425509" />
<cellSize
launcher:fixedSize="120dp" />
</workspaceSpec>
</workspaceSpecs>

View File

@@ -0,0 +1,88 @@
/*
* 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.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class SizeSpecTest : AbstractDeviceProfileTest() {
override val runningContext: Context = InstrumentationRegistry.getInstrumentation().context
@Before
fun setup() {
initializeVarsForPhone(deviceSpecs["phone"]!!)
}
@Test
fun valid_values() {
val combinations =
listOf(
SizeSpec(100f, 0f, 0f, false),
SizeSpec(0f, 1f, 0f, false),
SizeSpec(0f, 0f, 1f, false),
SizeSpec(0f, 0f, 0f, false),
SizeSpec(0f, 0f, 0f, true)
)
for (instance in combinations) {
assertThat(instance.isValid()).isEqualTo(true)
}
}
@Test
fun multiple_values_assigned() {
val combinations =
listOf(
SizeSpec(1f, 1f, 0f, false),
SizeSpec(1f, 0f, 1f, false),
SizeSpec(1f, 0f, 0f, true),
SizeSpec(0f, 1f, 1f, false),
SizeSpec(0f, 1f, 0f, true),
SizeSpec(0f, 0f, 1f, true),
SizeSpec(1f, 1f, 1f, true)
)
for (instance in combinations) {
assertThat(instance.isValid()).isEqualTo(false)
}
}
@Test
fun invalid_values() {
val combinations =
listOf(
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)
)
for (instance in combinations) {
assertThat(instance.isValid()).isEqualTo(false)
}
}
}

View File

@@ -27,7 +27,7 @@ class TestResourceHelper(private val context: Context, private val specsFileId:
ResourceHelper(context, specsFileId) {
override fun obtainStyledAttributes(attrs: AttributeSet, styleId: IntArray): TypedArray {
var clone = styleId.clone()
if (styleId == R.styleable.SpecSize) clone = TestR.styleable.SpecSize
if (styleId == R.styleable.SizeSpec) clone = TestR.styleable.SizeSpec
else if (styleId == R.styleable.WorkspaceSpec) clone = TestR.styleable.WorkspaceSpec
return context.obtainStyledAttributes(attrs, clone)
}

View File

@@ -50,16 +50,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"endPadding=SizeSpec(fixedSize=84.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"cellSize=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.15808, " +
"ofRemainderSpace=0.0)" +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false)" +
")"
)
assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString())
@@ -69,16 +73,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"endPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=1.0), " +
"ofRemainderSpace=1.0, " +
"matchWorkspace=false), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"cellSize=SizeSpec(fixedSize=273.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0)" +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false)" +
")"
)
assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString())
@@ -88,16 +96,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
"specType=HEIGHT, " +
"startPadding=SizeSpec(fixedSize=21.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"endPadding=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=1.0), " +
"ofRemainderSpace=1.0, " +
"matchWorkspace=false), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"cellSize=SizeSpec(fixedSize=273.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0)" +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false)" +
")"
)
assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1)
@@ -108,16 +120,20 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
"specType=WIDTH, " +
"startPadding=SizeSpec(fixedSize=58.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"endPadding=SizeSpec(fixedSize=58.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"gutter=SizeSpec(fixedSize=42.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.0), " +
"ofRemainderSpace=0.0, " +
"matchWorkspace=false), " +
"cellSize=SizeSpec(fixedSize=0.0, " +
"ofAvailableSpace=0.0, " +
"ofRemainderSpace=0.25)" +
"ofRemainderSpace=0.25, " +
"matchWorkspace=false)" +
")"
)
}
@@ -136,4 +152,9 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
fun parseInvalidFile_valueBiggerThan1_throwsError() {
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3))
}
@Test(expected = IllegalStateException::class)
fun parseInvalidFile_matchWorkspace_true_throwsError() {
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4))
}
}