diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 733f527aff..9803779bf1 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -252,7 +252,7 @@ - + @@ -260,12 +260,9 @@ - - - - - - + + + @@ -278,6 +275,14 @@ + + + + + + + + diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index f3b5155047..88dcfcda28 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -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); } diff --git a/src/com/android/launcher3/responsive/AllAppsSpecs.kt b/src/com/android/launcher3/responsive/AllAppsSpecs.kt index 85e383e3d6..8ed3ffc1bf 100644 --- a/src/com/android/launcher3/responsive/AllAppsSpecs.kt +++ b/src/com/android/launcher3/responsive/AllAppsSpecs.kt @@ -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, heightSpecs: List) : + ResponsiveSpecs(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() - val allAppsWidthSpecList = mutableListOf() - - // 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 + ) : 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) diff --git a/src/com/android/launcher3/responsive/FolderSpecs.kt b/src/com/android/launcher3/responsive/FolderSpecs.kt index f4446bc1d8..bc2db28ef1 100644 --- a/src/com/android/launcher3/responsive/FolderSpecs.kt +++ b/src/com/android/launcher3/responsive/FolderSpecs.kt @@ -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, heightSpecs: List) : + ResponsiveSpecs(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() - val heightSpecs: List - get() = _heightSpecs - - private val _widthSpecs = mutableListOf() - val widthSpecs: List - 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 + ) : 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) diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecs.kt b/src/com/android/launcher3/responsive/ResponsiveSpecs.kt new file mode 100644 index 0000000000..72a0ea4f72 --- /dev/null +++ b/src/com/android/launcher3/responsive/ResponsiveSpecs.kt @@ -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( + val widthSpecs: List, + val heightSpecs: List +) { + + 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}" + + ")" + } +} diff --git a/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt b/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt new file mode 100644 index 0000000000..a89b619c00 --- /dev/null +++ b/src/com/android/launcher3/responsive/ResponsiveSpecsParser.kt @@ -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 { + val parentName = parser.name + parser.next() + + val result = mutableMapOf() + 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 parseXML( + tagName: String, + map: (attributes: TypedArray, sizeSpecs: Map) -> T + ): List { + val parser: XmlResourceParser = resourceHelper.getXml() + + try { + val list = mutableListOf() + + 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.getOrError(key: String): SizeSpec { + return this.getOrElse(key) { error("Attr '$key' must be defined.") } +} diff --git a/src/com/android/launcher3/responsive/SizeSpec.kt b/src/com/android/launcher3/responsive/SizeSpec.kt index 3d618f989e..d3868f0c20 100644 --- a/src/com/android/launcher3/responsive/SizeSpec.kt +++ b/src/com/android/launcher3/responsive/SizeSpec.kt @@ -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" diff --git a/src/com/android/launcher3/responsive/WorkspaceSpecs.kt b/src/com/android/launcher3/responsive/WorkspaceSpecs.kt new file mode 100644 index 0000000000..0da7026bd6 --- /dev/null +++ b/src/com/android/launcher3/responsive/WorkspaceSpecs.kt @@ -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, heightSpecs: List) : + ResponsiveSpecs(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 + ) : 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) diff --git a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt b/src/com/android/launcher3/workspace/WorkspaceSpecs.kt deleted file mode 100644 index 8cc0c5936a..0000000000 --- a/src/com/android/launcher3/workspace/WorkspaceSpecs.kt +++ /dev/null @@ -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() - val workspaceWidthSpecList = mutableListOf() - - // 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 -} diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml index 0d586c2c12..e5ee06473c 100644 --- a/tests/res/values/attrs.xml +++ b/tests/res/values/attrs.xml @@ -18,7 +18,7 @@ - + @@ -26,12 +26,9 @@ - - - - - - + + + @@ -43,4 +40,12 @@ + + + + + + + + diff --git a/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt index 77ea5bacaa..cd95e99b99 100644 --- a/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/AllAppsSpecsTest.kt @@ -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)) } } diff --git a/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt index 9f981fa3e7..0f12e58a33 100644 --- a/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedAllAppsSpecTest.kt @@ -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) diff --git a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt index c14722c828..863cf76185 100644 --- a/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedFolderSpecsTest.kt @@ -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) diff --git a/tests/src/com/android/launcher3/workspace/CalculatedWorkspaceSpecTest.kt b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt similarity index 95% rename from tests/src/com/android/launcher3/workspace/CalculatedWorkspaceSpecTest.kt rename to tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt index 7f03ba2f77..0af694e000 100644 --- a/tests/src/com/android/launcher3/workspace/CalculatedWorkspaceSpecTest.kt +++ b/tests/src/com/android/launcher3/responsive/CalculatedWorkspaceSpecTest.kt @@ -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) diff --git a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt index 796bf9adce..e21af57cd5 100644 --- a/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/FolderSpecsTest.kt @@ -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 { diff --git a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt similarity index 86% rename from tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt rename to tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt index 8b99a3a72f..0364069535 100644 --- a/tests/src/com/android/launcher3/workspace/WorkspaceSpecsTest.kt +++ b/tests/src/com/android/launcher3/responsive/WorkspaceSpecsTest.kt @@ -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) + ) } }