Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6382afd6 authored by Thales Lima's avatar Thales Lima Committed by Android (Google) Code Review
Browse files

Merge "Improving responsive grid xml parser" into udc-qpr-dev

parents 82ddd8ac 8bd7af2b
Loading
Loading
Loading
Loading
+12 −7
Original line number Diff line number Diff line
@@ -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" />
+15 −16
Original line number Diff line number Diff line
@@ -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);
        }

+57 −245
Original line number Diff line number Diff line
@@ -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 }

        return CalculatedAllAppsSpec(availableHeight, rows, heightSpec, calculatedWorkspaceSpec)
    }
        check(calculatedWorkspaceSpec.spec.specType == SpecType.HEIGHT) {
            "Invalid specType for CalculatedWorkspaceSpec. " +
                "Expected: ${SpecType.HEIGHT} - " +
                "Found: ${calculatedWorkspaceSpec.spec.specType}}"
        }

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()
        val spec = getHeightSpec(availableHeight)
        return CalculatedAllAppsSpec(availableHeight, rows, spec, calculatedWorkspaceSpec)
    }

        // 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()
    companion object {
        private const val XML_ALL_APPS_SPEC = "allAppsSpec"

        // 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()
        @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)
        }

    override fun toString(): String {
        return "CalculatedAllAppsSpec(availableSpace=$availableSpace, " +
            "cells=$cells, startPaddingPx=$startPaddingPx, endPaddingPx=$endPaddingPx, " +
            "gutterPx=$gutterPx, cellSizePx=$cellSizePx, " +
            "AllAppsSpec.maxAvailableSize=${allAppsSpec.maxAvailableSize})"
    }
}

data class AllAppsSpec(
    val maxAvailableSize: Int,
    val specType: SpecType,
    val startPadding: SizeSpec,
    val endPadding: SizeSpec,
    val gutter: SizeSpec,
    val cellSize: SizeSpec
) {

    enum class SpecType {
        HEIGHT,
        WIDTH
    }

    fun isValid(): Boolean {
        if (maxAvailableSize <= 0) {
            Log.e(LOG_TAG, "AllAppsSpec#isValid - maxAvailableSize <= 0")
            return false
        }
    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) {

        // All specs need to be individually valid
        if (!allSpecsAreValid()) {
            Log.e(LOG_TAG, "AllAppsSpec#isValid - !allSpecsAreValid()")
            return false
        }

        return true
    init {
        check(isValid()) { "Invalid AllAppsSpec 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)
    )
}

    private fun allSpecsAreValid(): Boolean =
        startPadding.isValid() && endPadding.isValid() && gutter.isValid() && cellSize.isValid()
}
class CalculatedAllAppsSpec(
    availableSpace: Int,
    cells: Int,
    spec: AllAppsSpec,
    calculatedWorkspaceSpec: CalculatedWorkspaceSpec
) : CalculatedResponsiveSpec(availableSpace, cells, spec, calculatedWorkspaceSpec)
+73 −248

File changed.

Preview size limit exceeded, changes collapsed.

+222 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading