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

Commit f8bfb036 authored by Thales Lima's avatar Thales Lima
Browse files

Create specs for hotseat

Some attributes of hotseat change depending on the size of the device. In the future more attributes could be moved to the spec, e.g. hotseat icons.

Fix: 292204436
Test: CalculatedHotseatSpecTest
Test: HotseatSpecsTest
Test: SizeSpecTest
Test: DeviceProfileResponsiveDumpTest
Test: DeviceProfileResponsiveAlternativeDisplaysDumpTest
Flag: ENABLE_RESPONSIVE_WORKSPACE
Change-Id: I6a4e05d75af819dbf1444a5ca45c2080f55dc203
parent 8648f5f7
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -202,6 +202,7 @@
        <attr name="demoModeLayoutId" format="reference" />
        <attr name="isScalable" format="boolean" />
        <attr name="devicePaddingId" format="reference" />

        <!-- File that contains the specs for the workspace.
        Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
        <attr name="workspaceSpecsId" format="reference" />
@@ -210,11 +211,14 @@
        Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
        <attr name="allAppsSpecsId" format="reference" />
        <attr name="allAppsSpecsTwoPanelId" format="reference" />

        <!-- File that contains the specs for the workspace.
        Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
        <attr name="folderSpecsId" format="reference" />
        <attr name="folderSpecsTwoPanelId" format="reference" />
        <!-- File that contains the specs for hotseat bar.
        Needs FeatureFlags.ENABLE_RESPONSIVE_WORKSPACE enabled -->
        <attr name="hotseatSpecsId" format="reference" />
        <attr name="hotseatSpecsTwoPanelId" format="reference" />

        <!-- By default all categories are enabled -->
        <attr name="deviceCategory" format="integer">
@@ -278,6 +282,11 @@
        <attr name="maxAvailableSize" />
    </declare-styleable>

    <declare-styleable name="HotseatSpec">
        <attr name="specType" />
        <attr name="maxAvailableSize" />
    </declare-styleable>

    <declare-styleable name="SizeSpec">
        <attr name="fixedSize" format="dimension" />
        <attr name="ofAvailableSpace" format="float" />
+17 −2
Original line number Diff line number Diff line
@@ -56,8 +56,10 @@ 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.CalculatedHotseatSpec;
import com.android.launcher3.responsive.CalculatedWorkspaceSpec;
import com.android.launcher3.responsive.FolderSpecs;
import com.android.launcher3.responsive.HotseatSpecs;
import com.android.launcher3.responsive.WorkspaceSpecs;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.DisplayController;
@@ -121,6 +123,7 @@ public class DeviceProfile {
    private CalculatedAllAppsSpec mAllAppsResponsiveHeightSpec;
    private CalculatedFolderSpec mResponsiveFolderWidthSpec;
    private CalculatedFolderSpec mResponsiveFolderHeightSpec;
    private CalculatedHotseatSpec mResponsiveHotseatSpec;

    /**
     * The maximum amount of left/right workspace padding as a percentage of the screen width.
@@ -316,7 +319,8 @@ public class DeviceProfile {
        // TODO(b/241386436): shouldn't change any launcher behaviour
        mIsResponsiveGrid = inv.workspaceSpecsId != INVALID_RESOURCE_HANDLE
                && inv.allAppsSpecsId != INVALID_RESOURCE_HANDLE
                && inv.folderSpecsId != INVALID_RESOURCE_HANDLE;
                && inv.folderSpecsId != INVALID_RESOURCE_HANDLE
                && inv.hotseatSpecsId != INVALID_RESOURCE_HANDLE;

        mIsScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
        // Determine device posture.
@@ -495,7 +499,17 @@ public class DeviceProfile {

        int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
        int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin);

        if (mIsResponsiveGrid) {
            HotseatSpecs hotseatSpecs =
                    HotseatSpecs.create(new ResourceHelper(context,
                            isTwoPanels ? inv.hotseatSpecsTwoPanelId : inv.hotseatSpecsId));
            mResponsiveHotseatSpec = hotseatSpecs.getCalculatedHeightSpec(heightPx);
            hotseatQsbSpace = mResponsiveHotseatSpec.getHotseatQsbSpace();
        } else {
            hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics);
        }

        // Have a little space between the inset and the QSB
        if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
            int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
@@ -1952,6 +1966,7 @@ public class DeviceProfile {
                    + mAllAppsResponsiveWidthSpec.toString());
            writer.println(prefix + "\tmResponsiveFolderHeightSpec:" + mResponsiveFolderHeightSpec);
            writer.println(prefix + "\tmResponsiveFolderWidthSpec:" + mResponsiveFolderWidthSpec);
            writer.println(prefix + "\tmResponsiveHotseatSpec:" + mResponsiveHotseatSpec);
        }
    }

+13 −0
Original line number Diff line number Diff line
@@ -189,6 +189,8 @@ public class InvariantDeviceProfile {
    public int folderSpecsId = INVALID_RESOURCE_HANDLE;
    @XmlRes
    public int folderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
    public int hotseatSpecsId = INVALID_RESOURCE_HANDLE;
    public int hotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;

    public String dbFile;
    public int defaultLayoutId;
@@ -368,6 +370,8 @@ public class InvariantDeviceProfile {
        allAppsSpecsTwoPanelId = closestProfile.mAllAppsSpecsTwoPanelId;
        folderSpecsId = closestProfile.mFolderSpecsId;
        folderSpecsTwoPanelId = closestProfile.mFolderSpecsTwoPanelId;
        hotseatSpecsId = closestProfile.mHotseatSpecsId;
        hotseatSpecsTwoPanelId = closestProfile.mHotseatSpecsTwoPanelId;
        this.deviceType = deviceType;

        inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
@@ -819,6 +823,8 @@ public class InvariantDeviceProfile {
        private final int mAllAppsSpecsTwoPanelId;
        private final int mFolderSpecsId;
        private final int mFolderSpecsTwoPanelId;
        private final int mHotseatSpecsId;
        private final int mHotseatSpecsTwoPanelId;

        public GridOption(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(
@@ -896,6 +902,11 @@ public class InvariantDeviceProfile {
                mFolderSpecsTwoPanelId = a.getResourceId(
                        R.styleable.GridDisplayOption_folderSpecsTwoPanelId,
                        INVALID_RESOURCE_HANDLE);
                mHotseatSpecsId = a.getResourceId(
                        R.styleable.GridDisplayOption_hotseatSpecsId, INVALID_RESOURCE_HANDLE);
                mHotseatSpecsTwoPanelId = a.getResourceId(
                        R.styleable.GridDisplayOption_hotseatSpecsTwoPanelId,
                        INVALID_RESOURCE_HANDLE);
            } else {
                mWorkspaceSpecsId = INVALID_RESOURCE_HANDLE;
                mWorkspaceSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
@@ -903,6 +914,8 @@ public class InvariantDeviceProfile {
                mAllAppsSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
                mFolderSpecsId = INVALID_RESOURCE_HANDLE;
                mFolderSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
                mHotseatSpecsId = INVALID_RESOURCE_HANDLE;
                mHotseatSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
            }

            int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
+122 −0
Original line number Diff line number Diff line
/*
 * 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.util.ResourceHelper

class HotseatSpecs(val specs: List<HotseatSpec>) {

    fun getCalculatedHeightSpec(availableHeight: Int): CalculatedHotseatSpec {
        val spec = specs.firstOrNull { availableHeight <= it.maxAvailableSize }
        check(spec != null) { "No available height spec found within $availableHeight." }
        return CalculatedHotseatSpec(availableHeight, spec)
    }

    companion object {
        private const val XML_HOTSEAT_SPEC = "hotseatSpec"

        @JvmStatic
        fun create(resourceHelper: ResourceHelper): HotseatSpecs {
            val parser = ResponsiveSpecsParser(resourceHelper)
            val specs = parser.parseXML(XML_HOTSEAT_SPEC, ::HotseatSpec)
            return HotseatSpecs(specs.filter { it.specType == ResponsiveSpec.SpecType.HEIGHT })
        }
    }
}

data class HotseatSpec(
    val maxAvailableSize: Int,
    val specType: ResponsiveSpec.SpecType,
    val hotseatQsbSpace: SizeSpec
) {

    init {
        check(isValid()) { "Invalid HotseatSpec found." }
    }

    constructor(
        attrs: TypedArray,
        specs: Map<String, SizeSpec>
    ) : this(
        maxAvailableSize =
            attrs.getDimensionPixelSize(R.styleable.ResponsiveSpec_maxAvailableSize, 0),
        specType =
            ResponsiveSpec.SpecType.values()[
                    attrs.getInt(
                        R.styleable.ResponsiveSpec_specType,
                        ResponsiveSpec.SpecType.HEIGHT.ordinal
                    )],
        hotseatQsbSpace = specs.getOrError(SizeSpec.XmlTags.HOTSEAT_QSB_SPACE)
    )

    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 hotseatQsbSpace.isValid() && hotseatQsbSpace.onlyFixedSize()
    }

    companion object {
        private const val LOG_TAG = "HotseatSpec"
    }
}

class CalculatedHotseatSpec(val availableSpace: Int, val spec: HotseatSpec) {

    var hotseatQsbSpace: Int = 0
        private set

    init {
        hotseatQsbSpace = spec.hotseatQsbSpace.getCalculatedValue(availableSpace)
    }

    override fun hashCode(): Int {
        var result = availableSpace.hashCode()
        result = 31 * result + hotseatQsbSpace.hashCode()
        result = 31 * result + spec.hashCode()
        return result
    }

    override fun equals(other: Any?): Boolean {
        return other is CalculatedHotseatSpec &&
            availableSpace == other.availableSpace &&
            hotseatQsbSpace == other.hotseatQsbSpace &&
            spec == other.spec
    }

    override fun toString(): String {
        return "${this::class.simpleName}(" +
            "availableSpace=$availableSpace, hotseatQsbSpace=$hotseatQsbSpace, " +
            "${spec::class.simpleName}.maxAvailableSize=${spec.maxAvailableSize}" +
            ")"
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -107,11 +107,20 @@ data class SizeSpec(
        return true
    }

    fun onlyFixedSize(): Boolean {
        if (ofAvailableSpace > 0 || ofRemainderSpace > 0 || matchWorkspace) {
            Log.e(TAG, "SizeSpec#onlyFixedSize - only fixed size allowed for this tag")
            return false
        }
        return true
    }

    object XmlTags {
        const val START_PADDING = "startPadding"
        const val END_PADDING = "endPadding"
        const val GUTTER = "gutter"
        const val CELL_SIZE = "cellSize"
        const val HOTSEAT_QSB_SPACE = "hotseatQsbSpace"
    }

    companion object {
Loading