mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-28 07:46:55 +00:00
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:
@@ -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">
|
||||
|
||||
73
src/com/android/launcher3/responsive/SizeSpec.kt
Normal file
73
src/com/android/launcher3/responsive/SizeSpec.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
58
tests/res/xml/invalid_workspace_file_case_4.xml
Normal file
58
tests/res/xml/invalid_workspace_file_case_4.xml
Normal 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>
|
||||
88
tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
Normal file
88
tests/src/com/android/launcher3/responsive/SizeSpecTest.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user