Merge "Improving responsive grid xml parser" into udc-qpr-dev am: 6382afd657
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/23766603 Change-Id: I399e20cfee26953f38b5614244f57b8be287b363 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
+12
-7
@@ -252,7 +252,7 @@
|
||||
</declare-styleable>
|
||||
|
||||
<!-- Responsive grids attributes -->
|
||||
<declare-styleable name="WorkspaceSpec">
|
||||
<declare-styleable name="ResponsiveSpec">
|
||||
<attr name="specType" format="integer">
|
||||
<enum name="height" value="0" />
|
||||
<enum name="width" value="1" />
|
||||
@@ -260,12 +260,9 @@
|
||||
<attr name="maxAvailableSize" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SizeSpec">
|
||||
<attr name="fixedSize" format="dimension" />
|
||||
<attr name="ofAvailableSpace" format="float" />
|
||||
<attr name="ofRemainderSpace" format="float" />
|
||||
<attr name="matchWorkspace" format="boolean" />
|
||||
<attr name="maxSize" format="dimension" />
|
||||
<declare-styleable name="WorkspaceSpec">
|
||||
<attr name="specType" />
|
||||
<attr name="maxAvailableSize" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="FolderSpec">
|
||||
@@ -278,6 +275,14 @@
|
||||
<attr name="maxAvailableSize" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SizeSpec">
|
||||
<attr name="fixedSize" format="dimension" />
|
||||
<attr name="ofAvailableSpace" format="float" />
|
||||
<attr name="ofRemainderSpace" format="float" />
|
||||
<attr name="matchWorkspace" format="boolean" />
|
||||
<attr name="maxSize" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ProfileDisplayOption">
|
||||
<attr name="name" />
|
||||
<attr name="minWidthDps" format="float" />
|
||||
|
||||
@@ -56,15 +56,15 @@ import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.responsive.AllAppsSpecs;
|
||||
import com.android.launcher3.responsive.CalculatedAllAppsSpec;
|
||||
import com.android.launcher3.responsive.CalculatedFolderSpec;
|
||||
import com.android.launcher3.responsive.CalculatedWorkspaceSpec;
|
||||
import com.android.launcher3.responsive.FolderSpecs;
|
||||
import com.android.launcher3.responsive.WorkspaceSpecs;
|
||||
import com.android.launcher3.uioverrides.ApiWrapper;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.DisplayController.Info;
|
||||
import com.android.launcher3.util.IconSizeSteps;
|
||||
import com.android.launcher3.util.ResourceHelper;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.launcher3.workspace.CalculatedWorkspaceSpec;
|
||||
import com.android.launcher3.workspace.WorkspaceSpecs;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Locale;
|
||||
@@ -115,13 +115,10 @@ public class DeviceProfile {
|
||||
|
||||
// Responsive grid
|
||||
private final boolean mIsResponsiveGrid;
|
||||
private WorkspaceSpecs mWorkspaceSpecs;
|
||||
private CalculatedWorkspaceSpec mResponsiveWidthSpec;
|
||||
private CalculatedWorkspaceSpec mResponsiveHeightSpec;
|
||||
private AllAppsSpecs mAllAppsSpecs;
|
||||
private CalculatedAllAppsSpec mAllAppsResponsiveWidthSpec;
|
||||
private CalculatedAllAppsSpec mAllAppsResponsiveHeightSpec;
|
||||
private FolderSpecs mFolderSpecs;
|
||||
private CalculatedFolderSpec mResponsiveFolderWidthSpec;
|
||||
private CalculatedFolderSpec mResponsiveFolderHeightSpec;
|
||||
|
||||
@@ -545,29 +542,31 @@ public class DeviceProfile {
|
||||
// Needs to be calculated after hotseatBarSizePx is correct,
|
||||
// for the available height to be correct
|
||||
if (mIsResponsiveGrid) {
|
||||
mWorkspaceSpecs = new WorkspaceSpecs(new ResourceHelper(context, inv.workspaceSpecsId));
|
||||
WorkspaceSpecs workspaceSpecs = WorkspaceSpecs.create(
|
||||
new ResourceHelper(context, inv.workspaceSpecsId));
|
||||
int availableResponsiveWidth =
|
||||
availableWidthPx - (isVerticalBarLayout() ? hotseatBarSizePx : 0);
|
||||
// don't use availableHeightPx because it subtracts bottom padding,
|
||||
// but the workspace go behind it
|
||||
int availableResponsiveHeight =
|
||||
heightPx - mInsets.top - (isVerticalBarLayout() ? 0 : hotseatBarSizePx);
|
||||
mResponsiveWidthSpec = mWorkspaceSpecs.getCalculatedWidthSpec(inv.numColumns,
|
||||
mResponsiveWidthSpec = workspaceSpecs.getCalculatedWidthSpec(inv.numColumns,
|
||||
availableResponsiveWidth);
|
||||
mResponsiveHeightSpec = mWorkspaceSpecs.getCalculatedHeightSpec(inv.numRows,
|
||||
mResponsiveHeightSpec = workspaceSpecs.getCalculatedHeightSpec(inv.numRows,
|
||||
availableResponsiveHeight);
|
||||
|
||||
mAllAppsSpecs = new AllAppsSpecs(new ResourceHelper(context, inv.allAppsSpecsId));
|
||||
mAllAppsResponsiveWidthSpec = mAllAppsSpecs.getCalculatedWidthSpec(inv.numColumns,
|
||||
AllAppsSpecs allAppsSpecs = AllAppsSpecs.create(
|
||||
new ResourceHelper(context, inv.allAppsSpecsId));
|
||||
mAllAppsResponsiveWidthSpec = allAppsSpecs.getCalculatedWidthSpec(inv.numColumns,
|
||||
mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec);
|
||||
mAllAppsResponsiveHeightSpec = mAllAppsSpecs.getCalculatedHeightSpec(inv.numRows,
|
||||
mResponsiveHeightSpec.getAvailableSpace(),
|
||||
mResponsiveHeightSpec);
|
||||
mAllAppsResponsiveHeightSpec = allAppsSpecs.getCalculatedHeightSpec(inv.numRows,
|
||||
mResponsiveHeightSpec.getAvailableSpace(), mResponsiveHeightSpec);
|
||||
|
||||
mFolderSpecs = new FolderSpecs(new ResourceHelper(context, inv.folderSpecsId));
|
||||
mResponsiveFolderWidthSpec = mFolderSpecs.getWidthSpec(inv.numFolderColumns,
|
||||
FolderSpecs folderSpecs = FolderSpecs.create(
|
||||
new ResourceHelper(context, inv.folderSpecsId));
|
||||
mResponsiveFolderWidthSpec = folderSpecs.getCalculatedWidthSpec(inv.numFolderColumns,
|
||||
mResponsiveWidthSpec.getAvailableSpace(), mResponsiveWidthSpec);
|
||||
mResponsiveFolderHeightSpec = mFolderSpecs.getHeightSpec(inv.numFolderRows,
|
||||
mResponsiveFolderHeightSpec = folderSpecs.getCalculatedHeightSpec(inv.numFolderRows,
|
||||
mResponsiveHeightSpec.getAvailableSpace(), mResponsiveHeightSpec);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,277 +16,89 @@
|
||||
|
||||
package com.android.launcher3.responsive
|
||||
|
||||
import android.content.res.XmlResourceParser
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.util.Xml
|
||||
import android.content.res.TypedArray
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.responsive.ResponsiveSpec.SpecType
|
||||
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(widthSpecs: List<AllAppsSpec>, heightSpecs: List<AllAppsSpec>) :
|
||||
ResponsiveSpecs<AllAppsSpec>(widthSpecs, heightSpecs) {
|
||||
|
||||
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 }
|
||||
check(calculatedWorkspaceSpec.spec.specType == SpecType.WIDTH) {
|
||||
"Invalid specType for CalculatedWorkspaceSpec. " +
|
||||
"Expected: ${SpecType.WIDTH} - " +
|
||||
"Found: ${calculatedWorkspaceSpec.spec.specType}}"
|
||||
}
|
||||
|
||||
return CalculatedAllAppsSpec(availableWidth, columns, widthSpec, calculatedWorkspaceSpec)
|
||||
val spec = getWidthSpec(availableWidth)
|
||||
return CalculatedAllAppsSpec(availableWidth, columns, spec, 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 }
|
||||
check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) {
|
||||
"Invalid specType for CalculatedWorkspaceSpec. " +
|
||||
"Expected: ${SpecType.HEIGHT} - " +
|
||||
"Found: ${calculatedWorkspaceSpec.spec.specType}}"
|
||||
}
|
||||
|
||||
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()
|
||||
val spec = getHeightSpec(availableHeight)
|
||||
return CalculatedAllAppsSpec(availableHeight, rows, spec, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "CalculatedAllAppsSpec(availableSpace=$availableSpace, " +
|
||||
"cells=$cells, startPaddingPx=$startPaddingPx, endPaddingPx=$endPaddingPx, " +
|
||||
"gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
|
||||
"AllAppsSpec.maxAvailableSize=${allAppsSpec.maxAvailableSize})"
|
||||
companion object {
|
||||
private const val XML_ALL_APPS_SPEC = "allAppsSpec"
|
||||
|
||||
@JvmStatic
|
||||
fun create(resourceHelper: ResourceHelper): AllAppsSpecs {
|
||||
val parser = ResponsiveSpecsParser(resourceHelper)
|
||||
val specs = parser.parseXML(XML_ALL_APPS_SPEC, ::AllAppsSpec)
|
||||
val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH }
|
||||
return AllAppsSpecs(widthSpecs, heightSpecs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class AllAppsSpec(
|
||||
val maxAvailableSize: Int,
|
||||
val specType: SpecType,
|
||||
val startPadding: SizeSpec,
|
||||
val endPadding: SizeSpec,
|
||||
val gutter: SizeSpec,
|
||||
val cellSize: SizeSpec
|
||||
) {
|
||||
override val maxAvailableSize: Int,
|
||||
override val specType: SpecType,
|
||||
override val startPadding: SizeSpec,
|
||||
override val endPadding: SizeSpec,
|
||||
override val gutter: SizeSpec,
|
||||
override val cellSize: SizeSpec
|
||||
) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) {
|
||||
|
||||
enum class SpecType {
|
||||
HEIGHT,
|
||||
WIDTH
|
||||
init {
|
||||
check(isValid()) { "Invalid AllAppsSpec found." }
|
||||
}
|
||||
|
||||
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()
|
||||
constructor(
|
||||
attrs: TypedArray,
|
||||
specs: Map<String, SizeSpec>
|
||||
) : this(
|
||||
maxAvailableSize =
|
||||
attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
|
||||
specType =
|
||||
SpecType.values()[
|
||||
attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)],
|
||||
startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
|
||||
endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
|
||||
gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
|
||||
cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
|
||||
)
|
||||
}
|
||||
|
||||
class CalculatedAllAppsSpec(
|
||||
availableSpace: Int,
|
||||
cells: Int,
|
||||
spec: AllAppsSpec,
|
||||
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
|
||||
) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec)
|
||||
|
||||
@@ -1,280 +1,105 @@
|
||||
/*
|
||||
* 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 android.content.res.TypedArray
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.responsive.FolderSpec.*
|
||||
import com.android.launcher3.responsive.ResponsiveSpec.SpecType
|
||||
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(widthSpecs: List<FolderSpec>, heightSpecs: List<FolderSpec>) :
|
||||
ResponsiveSpecs<FolderSpec>(widthSpecs, heightSpecs) {
|
||||
|
||||
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(
|
||||
fun getCalculatedWidthSpec(
|
||||
columns: Int,
|
||||
availableWidth: Int,
|
||||
workspaceSpec: CalculatedWorkspaceSpec
|
||||
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
|
||||
): CalculatedFolderSpec {
|
||||
check(workspaceSpec.workspaceSpec.specType == WorkspaceSpec.SpecType.WIDTH) {
|
||||
check(calculatedWorkspaceSpec.spec.specType == SpecType.WIDTH) {
|
||||
"Invalid specType for CalculatedWorkspaceSpec. " +
|
||||
"Expected: ${WorkspaceSpec.SpecType.WIDTH} - " +
|
||||
"Found: ${workspaceSpec.workspaceSpec.specType}}"
|
||||
"Expected: ${SpecType.WIDTH} - " +
|
||||
"Found: ${calculatedWorkspaceSpec.spec.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)
|
||||
val spec = getWidthSpec(availableWidth)
|
||||
return CalculatedFolderSpec(availableWidth, columns, spec, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [CalculatedFolderSpec] for height, based on the available height, FolderSpecs and
|
||||
* WorkspaceSpecs.
|
||||
*/
|
||||
fun getHeightSpec(
|
||||
fun getCalculatedHeightSpec(
|
||||
rows: Int,
|
||||
availableHeight: Int,
|
||||
workspaceSpec: CalculatedWorkspaceSpec
|
||||
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
|
||||
): CalculatedFolderSpec {
|
||||
check(workspaceSpec.workspaceSpec.specType == WorkspaceSpec.SpecType.HEIGHT) {
|
||||
check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) {
|
||||
"Invalid specType for CalculatedWorkspaceSpec. " +
|
||||
"Expected: ${WorkspaceSpec.SpecType.HEIGHT} - " +
|
||||
"Found: ${workspaceSpec.workspaceSpec.specType}}"
|
||||
"Expected: ${SpecType.HEIGHT} - " +
|
||||
"Found: ${calculatedWorkspaceSpec.spec.specType}}"
|
||||
}
|
||||
|
||||
val heightSpec = _heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
|
||||
check(heightSpec != null) { "No FolderSpec for height spec found with $availableHeight." }
|
||||
val spec = getHeightSpec(availableHeight)
|
||||
return CalculatedFolderSpec(availableHeight, rows, spec, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
return convertToCalculatedFolderSpec(heightSpec, availableHeight, rows, workspaceSpec)
|
||||
companion object {
|
||||
|
||||
private const val XML_FOLDER_SPEC = "folderSpec"
|
||||
|
||||
@JvmStatic
|
||||
fun create(resourceHelper: ResourceHelper): FolderSpecs {
|
||||
val parser = ResponsiveSpecsParser(resourceHelper)
|
||||
val specs = parser.parseXML(XML_FOLDER_SPEC, ::FolderSpec)
|
||||
val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH }
|
||||
return FolderSpecs(widthSpecs, heightSpecs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class CalculatedFolderSpec(
|
||||
val availableSpace: Int,
|
||||
val cells: Int,
|
||||
val startPaddingPx: Int,
|
||||
val endPaddingPx: Int,
|
||||
val gutterPx: Int,
|
||||
val cellSizePx: 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
|
||||
) {
|
||||
override val maxAvailableSize: Int,
|
||||
override val specType: SpecType,
|
||||
override val startPadding: SizeSpec,
|
||||
override val endPadding: SizeSpec,
|
||||
override val gutter: SizeSpec,
|
||||
override val cellSize: SizeSpec
|
||||
) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) {
|
||||
|
||||
enum class SpecType {
|
||||
HEIGHT,
|
||||
WIDTH
|
||||
init {
|
||||
check(isValid()) { "Invalid FolderSpec found." }
|
||||
}
|
||||
|
||||
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(
|
||||
availableSpace = availableSpace,
|
||||
cells = cells,
|
||||
startPaddingPx = startPaddingPx,
|
||||
endPaddingPx = endPaddingPx,
|
||||
gutterPx = gutterPx,
|
||||
cellSizePx = cellSizePx
|
||||
constructor(
|
||||
attrs: TypedArray,
|
||||
specs: Map<String, SizeSpec>
|
||||
) : this(
|
||||
maxAvailableSize =
|
||||
attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
|
||||
specType =
|
||||
SpecType.values()[
|
||||
attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)],
|
||||
startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
|
||||
endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
|
||||
gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
|
||||
cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
|
||||
)
|
||||
}
|
||||
|
||||
class CalculatedFolderSpec(
|
||||
availableSpace: Int,
|
||||
cells: Int,
|
||||
spec: FolderSpec,
|
||||
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
|
||||
) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec)
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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.util.Log
|
||||
|
||||
/**
|
||||
* Base class for responsive specs that holds a list of width and height specs.
|
||||
*
|
||||
* @param widthSpecs List of width responsive specifications
|
||||
* @param heightSpecs List of height responsive specifications
|
||||
*/
|
||||
abstract class ResponsiveSpecs<T : ResponsiveSpec>(
|
||||
val widthSpecs: List<T>,
|
||||
val heightSpecs: List<T>
|
||||
) {
|
||||
|
||||
init {
|
||||
check(widthSpecs.isNotEmpty() && heightSpecs.isNotEmpty()) {
|
||||
"${this::class.simpleName} is incomplete - " +
|
||||
"width list size = ${widthSpecs.size}; " +
|
||||
"height list size = ${heightSpecs.size}."
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a [ResponsiveSpec] for width within the breakpoint.
|
||||
*
|
||||
* @param availableWidth The width breakpoint for the spec
|
||||
* @return A [ResponsiveSpec] for width.
|
||||
*/
|
||||
fun getWidthSpec(availableWidth: Int): T {
|
||||
val spec = widthSpecs.firstOrNull { availableWidth <= it.maxAvailableSize }
|
||||
check(spec != null) { "No available width spec found within $availableWidth." }
|
||||
return spec
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a [ResponsiveSpec] for height within the breakpoint.
|
||||
*
|
||||
* @param availableHeight The height breakpoint for the spec
|
||||
* @return A [ResponsiveSpec] for height.
|
||||
*/
|
||||
fun getHeightSpec(availableHeight: Int): T {
|
||||
val spec = heightSpecs.firstOrNull { availableHeight <= it.maxAvailableSize }
|
||||
check(spec != null) { "No available height spec found within $availableHeight." }
|
||||
return spec
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for a responsive specification that is used to calculate the paddings, gutter and cell
|
||||
* size.
|
||||
*
|
||||
* @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].
|
||||
*/
|
||||
abstract class ResponsiveSpec(
|
||||
open val maxAvailableSize: Int,
|
||||
open val specType: SpecType,
|
||||
open val startPadding: SizeSpec,
|
||||
open val endPadding: SizeSpec,
|
||||
open val gutter: SizeSpec,
|
||||
open val cellSize: SizeSpec
|
||||
) {
|
||||
open fun isValid(): Boolean {
|
||||
if (maxAvailableSize <= 0) {
|
||||
Log.e(LOG_TAG, "${this::class.simpleName}#isValid - maxAvailableSize <= 0")
|
||||
return false
|
||||
}
|
||||
|
||||
// All specs need to be individually valid
|
||||
if (!allSpecsAreValid()) {
|
||||
Log.e(LOG_TAG, "${this::class.simpleName}#isValid - !allSpecsAreValid()")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun allSpecsAreValid(): Boolean {
|
||||
return startPadding.isValid() &&
|
||||
endPadding.isValid() &&
|
||||
gutter.isValid() &&
|
||||
cellSize.isValid()
|
||||
}
|
||||
|
||||
enum class SpecType {
|
||||
HEIGHT,
|
||||
WIDTH
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LOG_TAG = "ResponsiveSpec"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculated responsive specs contains the final paddings, gutter and cell size in pixels after
|
||||
* they are calculated from the available space, cells and workspace specs.
|
||||
*/
|
||||
sealed class CalculatedResponsiveSpec {
|
||||
var availableSpace: Int = 0
|
||||
private set
|
||||
|
||||
var cells: Int = 0
|
||||
private set
|
||||
|
||||
var startPaddingPx: Int = 0
|
||||
private set
|
||||
|
||||
var endPaddingPx: Int = 0
|
||||
private set
|
||||
|
||||
var gutterPx: Int = 0
|
||||
private set
|
||||
|
||||
var cellSizePx: Int = 0
|
||||
private set
|
||||
|
||||
var spec: ResponsiveSpec
|
||||
private set
|
||||
|
||||
constructor(
|
||||
availableSpace: Int,
|
||||
cells: Int,
|
||||
spec: ResponsiveSpec,
|
||||
calculatedWorkspaceSpec: CalculatedWorkspaceSpec
|
||||
) {
|
||||
this.availableSpace = availableSpace
|
||||
this.cells = cells
|
||||
this.spec = spec
|
||||
|
||||
// Map if is fixedSize, ofAvailableSpace or matchWorkspace
|
||||
startPaddingPx =
|
||||
spec.startPadding.getCalculatedValue(
|
||||
availableSpace,
|
||||
calculatedWorkspaceSpec.startPaddingPx
|
||||
)
|
||||
endPaddingPx =
|
||||
spec.endPadding.getCalculatedValue(availableSpace, calculatedWorkspaceSpec.endPaddingPx)
|
||||
gutterPx = spec.gutter.getCalculatedValue(availableSpace, calculatedWorkspaceSpec.gutterPx)
|
||||
cellSizePx =
|
||||
spec.cellSize.getCalculatedValue(availableSpace, calculatedWorkspaceSpec.cellSizePx)
|
||||
|
||||
updateRemainderSpaces(availableSpace, cells, spec)
|
||||
}
|
||||
|
||||
constructor(availableSpace: Int, cells: Int, spec: ResponsiveSpec) {
|
||||
this.availableSpace = availableSpace
|
||||
this.cells = cells
|
||||
this.spec = spec
|
||||
|
||||
// Map if is fixedSize or ofAvailableSpace
|
||||
startPaddingPx = spec.startPadding.getCalculatedValue(availableSpace)
|
||||
endPaddingPx = spec.endPadding.getCalculatedValue(availableSpace)
|
||||
gutterPx = spec.gutter.getCalculatedValue(availableSpace)
|
||||
cellSizePx = spec.cellSize.getCalculatedValue(availableSpace)
|
||||
|
||||
updateRemainderSpaces(availableSpace, cells, spec)
|
||||
}
|
||||
|
||||
private fun updateRemainderSpaces(availableSpace: Int, cells: Int, spec: ResponsiveSpec) {
|
||||
val gutters = cells - 1
|
||||
val usedSpace = startPaddingPx + endPaddingPx + (gutterPx * gutters) + (cellSizePx * cells)
|
||||
val remainderSpace = availableSpace - usedSpace
|
||||
|
||||
startPaddingPx = spec.startPadding.getRemainderSpaceValue(remainderSpace, startPaddingPx)
|
||||
endPaddingPx = spec.endPadding.getRemainderSpaceValue(remainderSpace, endPaddingPx)
|
||||
gutterPx = spec.gutter.getRemainderSpaceValue(remainderSpace, gutterPx)
|
||||
cellSizePx = spec.cellSize.getRemainderSpaceValue(remainderSpace, cellSizePx)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = availableSpace.hashCode()
|
||||
result = 31 * result + cells.hashCode()
|
||||
result = 31 * result + startPaddingPx.hashCode()
|
||||
result = 31 * result + endPaddingPx.hashCode()
|
||||
result = 31 * result + gutterPx.hashCode()
|
||||
result = 31 * result + cellSizePx.hashCode()
|
||||
result = 31 * result + spec.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is CalculatedResponsiveSpec &&
|
||||
availableSpace == other.availableSpace &&
|
||||
cells == other.cells &&
|
||||
startPaddingPx == other.startPaddingPx &&
|
||||
endPaddingPx == other.endPaddingPx &&
|
||||
gutterPx == other.gutterPx &&
|
||||
cellSizePx == other.cellSizePx &&
|
||||
spec == other.spec
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${this::class.simpleName}(" +
|
||||
"availableSpace=$availableSpace, cells=$cells, startPaddingPx=$startPaddingPx, " +
|
||||
"endPaddingPx=$endPaddingPx, gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
|
||||
"${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
|
||||
")"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.TypedArray
|
||||
import android.content.res.XmlResourceParser
|
||||
import android.util.Xml
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.util.ResourceHelper
|
||||
import java.io.IOException
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
|
||||
class ResponsiveSpecsParser(private val resourceHelper: ResourceHelper) {
|
||||
|
||||
private fun parseSizeSpecs(parser: XmlResourceParser): Map<String, SizeSpec> {
|
||||
val parentName = parser.name
|
||||
parser.next()
|
||||
|
||||
val result = mutableMapOf<String, SizeSpec>()
|
||||
while (parser.eventType != XmlPullParser.END_DOCUMENT && parser.name != parentName) {
|
||||
if (parser.eventType == XmlResourceParser.START_TAG) {
|
||||
result[parser.name] = SizeSpec.create(resourceHelper, Xml.asAttributeSet(parser))
|
||||
}
|
||||
parser.next()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
fun <T> parseXML(
|
||||
tagName: String,
|
||||
map: (attributes: TypedArray, sizeSpecs: Map<String, SizeSpec>) -> T
|
||||
): List<T> {
|
||||
val parser: XmlResourceParser = resourceHelper.getXml()
|
||||
|
||||
try {
|
||||
val list = mutableListOf<T>()
|
||||
|
||||
var eventType = parser.eventType
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (eventType == XmlResourceParser.START_TAG && parser.name == tagName) {
|
||||
val attrs =
|
||||
resourceHelper.obtainStyledAttributes(
|
||||
Xml.asAttributeSet(parser),
|
||||
R.styleable.ResponsiveSpec
|
||||
)
|
||||
|
||||
val sizeSpecs = parseSizeSpecs(parser)
|
||||
list += map(attrs, sizeSpecs)
|
||||
attrs.recycle()
|
||||
}
|
||||
|
||||
eventType = parser.next()
|
||||
}
|
||||
|
||||
parser.close()
|
||||
|
||||
return list
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is NoSuchFieldException,
|
||||
is IOException,
|
||||
is XmlPullParserException ->
|
||||
throw RuntimeException("Failure parsing specs file.", e)
|
||||
else -> throw e
|
||||
}
|
||||
} finally {
|
||||
parser.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Map<String, SizeSpec>.getOrError(key: String): SizeSpec {
|
||||
return this.getOrElse(key) { error("Attr '$key' must be defined.") }
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.TypedArray
|
||||
@@ -26,7 +42,7 @@ data class SizeSpec(
|
||||
) {
|
||||
|
||||
/** Retrieves the correct value for [SizeSpec]. */
|
||||
fun getCalculatedValue(availableSpace: Int, workspaceValue: Int): Int {
|
||||
fun getCalculatedValue(availableSpace: Int, workspaceValue: Int = 0): Int {
|
||||
val calculatedValue =
|
||||
when {
|
||||
fixedSize > 0 -> fixedSize.roundToInt()
|
||||
@@ -91,6 +107,13 @@ data class SizeSpec(
|
||||
return true
|
||||
}
|
||||
|
||||
object XmlTags {
|
||||
const val START_PADDING = "startPadding"
|
||||
const val END_PADDING = "endPadding"
|
||||
const val GUTTER = "gutter"
|
||||
const val CELL_SIZE = "cellSize"
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SizeSpec"
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.TypedArray
|
||||
import android.util.Log
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.responsive.ResponsiveSpec.SpecType
|
||||
import com.android.launcher3.util.ResourceHelper
|
||||
|
||||
private const val TAG = "WorkspaceSpecs"
|
||||
|
||||
class WorkspaceSpecs(widthSpecs: List<WorkspaceSpec>, heightSpecs: List<WorkspaceSpec>) :
|
||||
ResponsiveSpecs<WorkspaceSpec>(widthSpecs, heightSpecs) {
|
||||
|
||||
fun getCalculatedWidthSpec(columns: Int, availableWidth: Int): CalculatedWorkspaceSpec {
|
||||
val spec = getWidthSpec(availableWidth)
|
||||
return CalculatedWorkspaceSpec(availableWidth, columns, spec)
|
||||
}
|
||||
|
||||
fun getCalculatedHeightSpec(rows: Int, availableHeight: Int): CalculatedWorkspaceSpec {
|
||||
val spec = getHeightSpec(availableHeight)
|
||||
return CalculatedWorkspaceSpec(availableHeight, rows, spec)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val XML_WORKSPACE_SPEC = "workspaceSpec"
|
||||
|
||||
@JvmStatic
|
||||
fun create(resourceHelper: ResourceHelper): WorkspaceSpecs {
|
||||
val parser = ResponsiveSpecsParser(resourceHelper)
|
||||
val specs = parser.parseXML(XML_WORKSPACE_SPEC, ::WorkspaceSpec)
|
||||
val (widthSpecs, heightSpecs) = specs.partition { it.specType == SpecType.WIDTH }
|
||||
return WorkspaceSpecs(widthSpecs, heightSpecs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WorkspaceSpec(
|
||||
override val maxAvailableSize: Int,
|
||||
override val specType: SpecType,
|
||||
override val startPadding: SizeSpec,
|
||||
override val endPadding: SizeSpec,
|
||||
override val gutter: SizeSpec,
|
||||
override val cellSize: SizeSpec
|
||||
) : ResponsiveSpec(maxAvailableSize, specType, startPadding, endPadding, gutter, cellSize) {
|
||||
|
||||
init {
|
||||
check(isValid()) { "Invalid WorkspaceSpec found." }
|
||||
}
|
||||
|
||||
constructor(
|
||||
attrs: TypedArray,
|
||||
specs: Map<String, SizeSpec>
|
||||
) : this(
|
||||
maxAvailableSize =
|
||||
attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
|
||||
specType =
|
||||
SpecType.values()[
|
||||
attrs.getInt(R.styleable.ResponsiveSpec_specType, SpecType.HEIGHT.ordinal)],
|
||||
startPadding = specs.getOrError(SizeSpec.XmlTags.START_PADDING),
|
||||
endPadding = specs.getOrError(SizeSpec.XmlTags.END_PADDING),
|
||||
gutter = specs.getOrError(SizeSpec.XmlTags.GUTTER),
|
||||
cellSize = specs.getOrError(SizeSpec.XmlTags.CELL_SIZE)
|
||||
)
|
||||
|
||||
override fun isValid(): Boolean {
|
||||
// Workspace spec should not match workspace
|
||||
if (
|
||||
startPadding.matchWorkspace ||
|
||||
endPadding.matchWorkspace ||
|
||||
gutter.matchWorkspace ||
|
||||
cellSize.matchWorkspace
|
||||
) {
|
||||
Log.e(TAG, "WorkspaceSpec#isValid - workspace shouldn't contain matchWorkspace!")
|
||||
return false
|
||||
}
|
||||
|
||||
return super.isValid()
|
||||
}
|
||||
}
|
||||
|
||||
class CalculatedWorkspaceSpec(availableSpace: Int, cells: Int, spec: WorkspaceSpec) :
|
||||
CalculatedResponsiveSpec(availableSpace, cells, spec)
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* 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.workspace
|
||||
|
||||
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.SizeSpec
|
||||
import com.android.launcher3.util.ResourceHelper
|
||||
import java.io.IOException
|
||||
import kotlin.math.roundToInt
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserException
|
||||
|
||||
private const val TAG = "WorkspaceSpecs"
|
||||
|
||||
class WorkspaceSpecs(resourceHelper: ResourceHelper) {
|
||||
object XmlTags {
|
||||
const val WORKSPACE_SPECS = "workspaceSpecs"
|
||||
|
||||
const val WORKSPACE_SPEC = "workspaceSpec"
|
||||
const val START_PADDING = "startPadding"
|
||||
const val END_PADDING = "endPadding"
|
||||
const val GUTTER = "gutter"
|
||||
const val CELL_SIZE = "cellSize"
|
||||
}
|
||||
|
||||
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()
|
||||
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.WORKSPACE_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.WORKSPACE_SPEC == parser.name
|
||||
) {
|
||||
val attrs =
|
||||
resourceHelper.obtainStyledAttributes(
|
||||
Xml.asAttributeSet(parser),
|
||||
R.styleable.WorkspaceSpec
|
||||
)
|
||||
val maxAvailableSize =
|
||||
attrs.getDimensionPixelSize(
|
||||
R.styleable.WorkspaceSpec_maxAvailableSize,
|
||||
0
|
||||
)
|
||||
val specType =
|
||||
WorkspaceSpec.SpecType.values()[
|
||||
attrs.getInt(
|
||||
R.styleable.WorkspaceSpec_specType,
|
||||
WorkspaceSpec.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 workspaceSpec must be defined"
|
||||
)
|
||||
}
|
||||
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize,
|
||||
specType,
|
||||
startPadding,
|
||||
endPadding,
|
||||
gutter,
|
||||
cellSize
|
||||
)
|
||||
if (workspaceSpec.isValid()) {
|
||||
if (workspaceSpec.specType == WorkspaceSpec.SpecType.HEIGHT)
|
||||
workspaceHeightSpecList.add(workspaceSpec)
|
||||
else workspaceWidthSpecList.add(workspaceSpec)
|
||||
} else {
|
||||
throw IllegalStateException("Invalid workspaceSpec found.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (workspaceWidthSpecList.isEmpty() || workspaceHeightSpecList.isEmpty()) {
|
||||
throw IllegalStateException(
|
||||
"WorkspaceSpecs is incomplete - " +
|
||||
"height list size = ${workspaceHeightSpecList.size}; " +
|
||||
"width list size = ${workspaceWidthSpecList.size}."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
parser.close()
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is IOException,
|
||||
is XmlPullParserException -> {
|
||||
throw RuntimeException("Failure parsing workspaces specs file.", e)
|
||||
}
|
||||
else -> throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CalculatedWorkspaceSpec for width, based on the available width and the
|
||||
* WorkspaceSpecs.
|
||||
*/
|
||||
fun getCalculatedWidthSpec(columns: Int, availableWidth: Int): CalculatedWorkspaceSpec {
|
||||
val widthSpec = workspaceWidthSpecList.first { availableWidth <= it.maxAvailableSize }
|
||||
|
||||
return CalculatedWorkspaceSpec(availableWidth, columns, widthSpec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the CalculatedWorkspaceSpec for height, based on the available height and the
|
||||
* WorkspaceSpecs.
|
||||
*/
|
||||
fun getCalculatedHeightSpec(rows: Int, availableHeight: Int): CalculatedWorkspaceSpec {
|
||||
val heightSpec = workspaceHeightSpecList.first { availableHeight <= it.maxAvailableSize }
|
||||
|
||||
return CalculatedWorkspaceSpec(availableHeight, rows, heightSpec)
|
||||
}
|
||||
}
|
||||
|
||||
class CalculatedWorkspaceSpec(
|
||||
val availableSpace: Int,
|
||||
val cells: Int,
|
||||
val workspaceSpec: WorkspaceSpec
|
||||
) {
|
||||
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 {
|
||||
// Calculate all fixed size first
|
||||
if (workspaceSpec.startPadding.fixedSize > 0)
|
||||
startPaddingPx = workspaceSpec.startPadding.fixedSize.roundToInt()
|
||||
if (workspaceSpec.endPadding.fixedSize > 0)
|
||||
endPaddingPx = workspaceSpec.endPadding.fixedSize.roundToInt()
|
||||
if (workspaceSpec.gutter.fixedSize > 0)
|
||||
gutterPx = workspaceSpec.gutter.fixedSize.roundToInt()
|
||||
if (workspaceSpec.cellSize.fixedSize > 0)
|
||||
cellSizePx = workspaceSpec.cellSize.fixedSize.roundToInt()
|
||||
|
||||
// Calculate all available space next
|
||||
if (workspaceSpec.startPadding.ofAvailableSpace > 0)
|
||||
startPaddingPx =
|
||||
(workspaceSpec.startPadding.ofAvailableSpace * availableSpace).roundToInt()
|
||||
if (workspaceSpec.endPadding.ofAvailableSpace > 0)
|
||||
endPaddingPx = (workspaceSpec.endPadding.ofAvailableSpace * availableSpace).roundToInt()
|
||||
if (workspaceSpec.gutter.ofAvailableSpace > 0)
|
||||
gutterPx = (workspaceSpec.gutter.ofAvailableSpace * availableSpace).roundToInt()
|
||||
if (workspaceSpec.cellSize.ofAvailableSpace > 0)
|
||||
cellSizePx = (workspaceSpec.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 (workspaceSpec.startPadding.ofRemainderSpace > 0)
|
||||
startPaddingPx =
|
||||
(workspaceSpec.startPadding.ofRemainderSpace * remainderSpace).roundToInt()
|
||||
if (workspaceSpec.endPadding.ofRemainderSpace > 0)
|
||||
endPaddingPx = (workspaceSpec.endPadding.ofRemainderSpace * remainderSpace).roundToInt()
|
||||
if (workspaceSpec.gutter.ofRemainderSpace > 0)
|
||||
gutterPx = (workspaceSpec.gutter.ofRemainderSpace * remainderSpace).roundToInt()
|
||||
if (workspaceSpec.cellSize.ofRemainderSpace > 0)
|
||||
cellSizePx = (workspaceSpec.cellSize.ofRemainderSpace * remainderSpace).roundToInt()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "CalculatedWorkspaceSpec(availableSpace=$availableSpace, " +
|
||||
"cells=$cells, startPaddingPx=$startPaddingPx, endPaddingPx=$endPaddingPx, " +
|
||||
"gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
|
||||
"workspaceSpec.maxAvailableSize=${workspaceSpec.maxAvailableSize})"
|
||||
}
|
||||
}
|
||||
|
||||
data class WorkspaceSpec(
|
||||
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(TAG, "WorkspaceSpec#isValid - maxAvailableSize <= 0")
|
||||
return false
|
||||
}
|
||||
|
||||
// All specs need to be individually valid
|
||||
if (!allSpecsAreValid()) {
|
||||
Log.e(TAG, "WorkspaceSpec#isValid - !allSpecsAreValid()")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun allSpecsAreValid(): Boolean =
|
||||
startPadding.isValid() &&
|
||||
endPadding.isValid() &&
|
||||
gutter.isValid() &&
|
||||
cellSize.isValid() &&
|
||||
!startPadding.matchWorkspace &&
|
||||
!endPadding.matchWorkspace &&
|
||||
!gutter.matchWorkspace &&
|
||||
!cellSize.matchWorkspace
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
<!-- Attributes have to be copied to test for correct parsing of files -->
|
||||
<resources>
|
||||
<!-- Responsive grids attributes -->
|
||||
<declare-styleable name="WorkspaceSpec">
|
||||
<declare-styleable name="ResponsiveSpec">
|
||||
<attr name="specType" format="integer">
|
||||
<enum name="height" value="0" />
|
||||
<enum name="width" value="1" />
|
||||
@@ -26,12 +26,9 @@
|
||||
<attr name="maxAvailableSize" format="dimension" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SizeSpec">
|
||||
<attr name="fixedSize" format="dimension" />
|
||||
<attr name="ofAvailableSpace" format="float" />
|
||||
<attr name="ofRemainderSpace" format="float" />
|
||||
<attr name="matchWorkspace" format="boolean" />
|
||||
<attr name="maxSize" format="dimension" />
|
||||
<declare-styleable name="WorkspaceSpec">
|
||||
<attr name="specType" />
|
||||
<attr name="maxAvailableSize" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="FolderSpec">
|
||||
@@ -43,4 +40,12 @@
|
||||
<attr name="specType" />
|
||||
<attr name="maxAvailableSize" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SizeSpec">
|
||||
<attr name="fixedSize" format="dimension" />
|
||||
<attr name="ofAvailableSpace" format="float" />
|
||||
<attr name="ofRemainderSpace" format="float" />
|
||||
<attr name="matchWorkspace" format="boolean" />
|
||||
<attr name="maxSize" format="dimension" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -41,9 +41,9 @@ class AllAppsSpecsTest : AbstractDeviceProfileTest() {
|
||||
@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())
|
||||
AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file))
|
||||
assertThat(allAppsSpecs.heightSpecs.size).isEqualTo(1)
|
||||
assertThat(allAppsSpecs.heightSpecs[0].toString())
|
||||
.isEqualTo(
|
||||
"AllAppsSpec(" +
|
||||
"maxAvailableSize=26247, " +
|
||||
@@ -71,8 +71,8 @@ class AllAppsSpecsTest : AbstractDeviceProfileTest() {
|
||||
")"
|
||||
)
|
||||
|
||||
assertThat(allAppsSpecs.allAppsWidthSpecList.size).isEqualTo(1)
|
||||
assertThat(allAppsSpecs.allAppsWidthSpecList[0].toString())
|
||||
assertThat(allAppsSpecs.widthSpecs.size).isEqualTo(1)
|
||||
assertThat(allAppsSpecs.widthSpecs[0].toString())
|
||||
.isEqualTo(
|
||||
"AllAppsSpec(" +
|
||||
"maxAvailableSize=26247, " +
|
||||
@@ -103,16 +103,16 @@ class AllAppsSpecsTest : AbstractDeviceProfileTest() {
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_missingTag_throwsError() {
|
||||
AllAppsSpecs(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_1))
|
||||
AllAppsSpecs.create(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))
|
||||
AllAppsSpecs.create(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))
|
||||
AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.invalid_all_apps_file_case_3))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ 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
|
||||
@@ -49,12 +48,12 @@ class CalculatedAllAppsSpecTest : AbstractDeviceProfileTest() {
|
||||
val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495
|
||||
|
||||
val workspaceSpecs =
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
WorkspaceSpecs.create(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))
|
||||
AllAppsSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_all_apps_file))
|
||||
|
||||
with(allAppsSpecs.getCalculatedWidthSpec(4, availableWidth, widthSpec)) {
|
||||
assertThat(availableSpace).isEqualTo(availableWidth)
|
||||
|
||||
@@ -24,7 +24,6 @@ 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
|
||||
@@ -48,11 +47,11 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
|
||||
// Loading workspace specs
|
||||
val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file)
|
||||
val workspaceSpecs = WorkspaceSpecs(resourceHelperWorkspace)
|
||||
val workspaceSpecs = WorkspaceSpecs.create(resourceHelperWorkspace)
|
||||
|
||||
// Loading folders specs
|
||||
val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs)
|
||||
val folderSpecs = FolderSpecs(resourceHelperFolder)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelperFolder)
|
||||
|
||||
assertThat(folderSpecs.widthSpecs.size).isEqualTo(2)
|
||||
assertThat(folderSpecs.widthSpecs[0].cellSize.matchWorkspace).isEqualTo(true)
|
||||
@@ -62,7 +61,7 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
var availableWidth = deviceSpec.naturalSize.first
|
||||
var calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth)
|
||||
var calculatedWidthFolderSpec =
|
||||
folderSpecs.getWidthSpec(columns, availableWidth, calculatedWorkspace)
|
||||
folderSpecs.getCalculatedWidthSpec(columns, availableWidth, calculatedWorkspace)
|
||||
with(calculatedWidthFolderSpec) {
|
||||
assertThat(availableSpace).isEqualTo(availableWidth)
|
||||
assertThat(cells).isEqualTo(columns)
|
||||
@@ -76,7 +75,7 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
availableWidth = 2000.dpToPx()
|
||||
calculatedWorkspace = workspaceSpecs.getCalculatedWidthSpec(columns, availableWidth)
|
||||
calculatedWidthFolderSpec =
|
||||
folderSpecs.getWidthSpec(columns, availableWidth, calculatedWorkspace)
|
||||
folderSpecs.getCalculatedWidthSpec(columns, availableWidth, calculatedWorkspace)
|
||||
with(calculatedWidthFolderSpec) {
|
||||
assertThat(availableSpace).isEqualTo(availableWidth)
|
||||
assertThat(cells).isEqualTo(columns)
|
||||
@@ -97,11 +96,11 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
|
||||
// Loading workspace specs
|
||||
val resourceHelperWorkspace = TestResourceHelper(context!!, R.xml.valid_workspace_file)
|
||||
val workspaceSpecs = WorkspaceSpecs(resourceHelperWorkspace)
|
||||
val workspaceSpecs = WorkspaceSpecs.create(resourceHelperWorkspace)
|
||||
|
||||
// Loading folders specs
|
||||
val resourceHelperFolder = TestResourceHelper(context!!, R.xml.valid_folders_specs)
|
||||
val folderSpecs = FolderSpecs(resourceHelperFolder)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelperFolder)
|
||||
|
||||
assertThat(folderSpecs.heightSpecs.size).isEqualTo(1)
|
||||
assertThat(folderSpecs.heightSpecs[0].cellSize.matchWorkspace).isEqualTo(true)
|
||||
@@ -109,7 +108,7 @@ class CalculatedFolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
// Validate height spec
|
||||
val calculatedWorkspace = workspaceSpecs.getCalculatedHeightSpec(rows, availableHeight)
|
||||
val calculatedFolderSpec =
|
||||
folderSpecs.getHeightSpec(rows, availableHeight, calculatedWorkspace)
|
||||
folderSpecs.getCalculatedHeightSpec(rows, availableHeight, calculatedWorkspace)
|
||||
with(calculatedFolderSpec) {
|
||||
assertThat(availableSpace).isEqualTo(availableHeight)
|
||||
assertThat(cells).isEqualTo(rows)
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.workspace
|
||||
package com.android.launcher3.responsive
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
@@ -49,7 +49,7 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() {
|
||||
val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 495
|
||||
|
||||
val workspaceSpecs =
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth)
|
||||
val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight)
|
||||
|
||||
@@ -86,7 +86,7 @@ class CalculatedWorkspaceSpecTest : AbstractDeviceProfileTest() {
|
||||
val availableHeight = deviceSpec.naturalSize.second - deviceSpec.statusBarNaturalPx - 640
|
||||
|
||||
val workspaceSpecs =
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
val widthSpec = workspaceSpecs.getCalculatedWidthSpec(4, availableWidth)
|
||||
val heightSpec = workspaceSpecs.getCalculatedHeightSpec(5, availableHeight)
|
||||
|
||||
@@ -21,11 +21,10 @@ 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.responsive.ResponsiveSpec.SpecType
|
||||
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
|
||||
@@ -44,14 +43,14 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
@Test
|
||||
fun parseValidFile() {
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
|
||||
val folderSpecs = FolderSpecs(resourceHelper)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
|
||||
val sizeSpec16 = SizeSpec(16f.dpToPx())
|
||||
val widthSpecsExpected =
|
||||
listOf(
|
||||
FolderSpec(
|
||||
maxAvailableSize = 800.dpToPx(),
|
||||
specType = FolderSpec.SpecType.WIDTH,
|
||||
specType = SpecType.WIDTH,
|
||||
startPadding = sizeSpec16,
|
||||
endPadding = sizeSpec16,
|
||||
gutter = sizeSpec16,
|
||||
@@ -59,7 +58,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
),
|
||||
FolderSpec(
|
||||
maxAvailableSize = 9999.dpToPx(),
|
||||
specType = FolderSpec.SpecType.WIDTH,
|
||||
specType = SpecType.WIDTH,
|
||||
startPadding = sizeSpec16,
|
||||
endPadding = sizeSpec16,
|
||||
gutter = sizeSpec16,
|
||||
@@ -70,7 +69,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val heightSpecsExpected =
|
||||
FolderSpec(
|
||||
maxAvailableSize = 9999.dpToPx(),
|
||||
specType = FolderSpec.SpecType.HEIGHT,
|
||||
specType = SpecType.HEIGHT,
|
||||
startPadding = SizeSpec(24f.dpToPx()),
|
||||
endPadding = SizeSpec(64f.dpToPx()),
|
||||
gutter = sizeSpec16,
|
||||
@@ -88,25 +87,25 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_missingTag_throwsError() {
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_1)
|
||||
FolderSpecs(resourceHelper)
|
||||
FolderSpecs.create(resourceHelper)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_moreThanOneValuePerTag_throwsError() {
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_2)
|
||||
FolderSpecs(resourceHelper)
|
||||
FolderSpecs.create(resourceHelper)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_valueBiggerThan1_throwsError() {
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_3)
|
||||
FolderSpecs(resourceHelper)
|
||||
FolderSpecs.create(resourceHelper)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_missingSpecs_throwsError() {
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.invalid_folders_specs_4)
|
||||
FolderSpecs(resourceHelper)
|
||||
FolderSpecs.create(resourceHelper)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
@@ -117,7 +116,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize = availableSpace,
|
||||
specType = WorkspaceSpec.SpecType.WIDTH,
|
||||
specType = SpecType.WIDTH,
|
||||
startPadding = SizeSpec(fixedSize = 10f),
|
||||
endPadding = SizeSpec(fixedSize = 10f),
|
||||
gutter = SizeSpec(fixedSize = 10f),
|
||||
@@ -126,8 +125,8 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
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)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
@@ -138,7 +137,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize = availableSpace,
|
||||
specType = WorkspaceSpec.SpecType.HEIGHT,
|
||||
specType = SpecType.HEIGHT,
|
||||
startPadding = SizeSpec(fixedSize = 10f),
|
||||
endPadding = SizeSpec(fixedSize = 10f),
|
||||
gutter = SizeSpec(fixedSize = 10f),
|
||||
@@ -147,8 +146,8 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
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)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -159,7 +158,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize = availableSpace,
|
||||
specType = WorkspaceSpec.SpecType.WIDTH,
|
||||
specType = SpecType.WIDTH,
|
||||
startPadding = SizeSpec(fixedSize = 10f),
|
||||
endPadding = SizeSpec(fixedSize = 10f),
|
||||
gutter = SizeSpec(fixedSize = 10f),
|
||||
@@ -167,21 +166,17 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
)
|
||||
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 folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
val calculatedWidthSpec =
|
||||
folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
assertThat(calculatedWidthSpec).isEqualTo(expectedResult)
|
||||
folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
|
||||
assertThat(calculatedWidthSpec.cells).isEqualTo(cells)
|
||||
assertThat(calculatedWidthSpec.availableSpace).isEqualTo(availableSpace)
|
||||
assertThat(calculatedWidthSpec.startPaddingPx).isEqualTo(16.dpToPx())
|
||||
assertThat(calculatedWidthSpec.endPaddingPx).isEqualTo(16.dpToPx())
|
||||
assertThat(calculatedWidthSpec.gutterPx).isEqualTo(16.dpToPx())
|
||||
assertThat(calculatedWidthSpec.cellSizePx).isEqualTo(calculatedWorkspaceSpec.cellSizePx)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
@@ -192,7 +187,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize = availableSpace,
|
||||
specType = WorkspaceSpec.SpecType.HEIGHT,
|
||||
specType = SpecType.HEIGHT,
|
||||
startPadding = SizeSpec(fixedSize = 10f),
|
||||
endPadding = SizeSpec(fixedSize = 10f),
|
||||
gutter = SizeSpec(fixedSize = 10f),
|
||||
@@ -201,8 +196,8 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
|
||||
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
|
||||
val folderSpecs = FolderSpecs(resourceHelper)
|
||||
folderSpecs.getWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
folderSpecs.getCalculatedWidthSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -213,7 +208,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize = availableSpace,
|
||||
specType = WorkspaceSpec.SpecType.HEIGHT,
|
||||
specType = SpecType.HEIGHT,
|
||||
startPadding = SizeSpec(fixedSize = 10f),
|
||||
endPadding = SizeSpec(fixedSize = 10f),
|
||||
gutter = SizeSpec(fixedSize = 10f),
|
||||
@@ -221,21 +216,17 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
)
|
||||
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 folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
val calculatedHeightSpec =
|
||||
folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
assertThat(calculatedHeightSpec).isEqualTo(expectedResult)
|
||||
folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
|
||||
assertThat(calculatedHeightSpec.cells).isEqualTo(cells)
|
||||
assertThat(calculatedHeightSpec.availableSpace).isEqualTo(availableSpace)
|
||||
assertThat(calculatedHeightSpec.startPaddingPx).isEqualTo(24.dpToPx())
|
||||
assertThat(calculatedHeightSpec.endPaddingPx).isEqualTo(64.dpToPx())
|
||||
assertThat(calculatedHeightSpec.gutterPx).isEqualTo(16.dpToPx())
|
||||
assertThat(calculatedHeightSpec.cellSizePx).isEqualTo(calculatedWorkspaceSpec.cellSizePx)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
@@ -246,7 +237,7 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val workspaceSpec =
|
||||
WorkspaceSpec(
|
||||
maxAvailableSize = availableSpace,
|
||||
specType = WorkspaceSpec.SpecType.WIDTH,
|
||||
specType = SpecType.WIDTH,
|
||||
startPadding = SizeSpec(fixedSize = 10f),
|
||||
endPadding = SizeSpec(fixedSize = 10f),
|
||||
gutter = SizeSpec(fixedSize = 10f),
|
||||
@@ -255,8 +246,8 @@ class FolderSpecsTest : AbstractDeviceProfileTest() {
|
||||
val calculatedWorkspaceSpec = CalculatedWorkspaceSpec(availableSpace, cells, workspaceSpec)
|
||||
|
||||
val resourceHelper = TestResourceHelper(context!!, R.xml.valid_folders_specs)
|
||||
val folderSpecs = FolderSpecs(resourceHelper)
|
||||
folderSpecs.getHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
val folderSpecs = FolderSpecs.create(resourceHelper)
|
||||
folderSpecs.getCalculatedHeightSpec(cells, availableSpace, calculatedWorkspaceSpec)
|
||||
}
|
||||
|
||||
private fun Float.dpToPx(): Float {
|
||||
|
||||
+20
-12
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.workspace
|
||||
package com.android.launcher3.responsive
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
@@ -41,9 +41,9 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
@Test
|
||||
fun parseValidFile() {
|
||||
val workspaceSpecs =
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
assertThat(workspaceSpecs.workspaceHeightSpecList.size).isEqualTo(3)
|
||||
assertThat(workspaceSpecs.workspaceHeightSpecList[0].toString())
|
||||
WorkspaceSpecs.create(TestResourceHelper(context!!, TestR.xml.valid_workspace_file))
|
||||
assertThat(workspaceSpecs.heightSpecs.size).isEqualTo(3)
|
||||
assertThat(workspaceSpecs.heightSpecs[0].toString())
|
||||
.isEqualTo(
|
||||
"WorkspaceSpec(" +
|
||||
"maxAvailableSize=1533, " +
|
||||
@@ -70,7 +70,7 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"maxSize=2147483647)" +
|
||||
")"
|
||||
)
|
||||
assertThat(workspaceSpecs.workspaceHeightSpecList[1].toString())
|
||||
assertThat(workspaceSpecs.heightSpecs[1].toString())
|
||||
.isEqualTo(
|
||||
"WorkspaceSpec(" +
|
||||
"maxAvailableSize=1607, " +
|
||||
@@ -97,7 +97,7 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"maxSize=2147483647)" +
|
||||
")"
|
||||
)
|
||||
assertThat(workspaceSpecs.workspaceHeightSpecList[2].toString())
|
||||
assertThat(workspaceSpecs.heightSpecs[2].toString())
|
||||
.isEqualTo(
|
||||
"WorkspaceSpec(" +
|
||||
"maxAvailableSize=26247, " +
|
||||
@@ -124,8 +124,8 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
"maxSize=2147483647)" +
|
||||
")"
|
||||
)
|
||||
assertThat(workspaceSpecs.workspaceWidthSpecList.size).isEqualTo(1)
|
||||
assertThat(workspaceSpecs.workspaceWidthSpecList[0].toString())
|
||||
assertThat(workspaceSpecs.widthSpecs.size).isEqualTo(1)
|
||||
assertThat(workspaceSpecs.widthSpecs[0].toString())
|
||||
.isEqualTo(
|
||||
"WorkspaceSpec(" +
|
||||
"maxAvailableSize=26247, " +
|
||||
@@ -156,21 +156,29 @@ class WorkspaceSpecsTest : AbstractDeviceProfileTest() {
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_missingTag_throwsError() {
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_1))
|
||||
WorkspaceSpecs.create(
|
||||
TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_1)
|
||||
)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_moreThanOneValuePerTag_throwsError() {
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_2))
|
||||
WorkspaceSpecs.create(
|
||||
TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_2)
|
||||
)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun parseInvalidFile_valueBiggerThan1_throwsError() {
|
||||
WorkspaceSpecs(TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_3))
|
||||
WorkspaceSpecs.create(
|
||||
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))
|
||||
WorkspaceSpecs.create(
|
||||
TestResourceHelper(context!!, TestR.xml.invalid_workspace_file_case_4)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user