1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.responsive
18 
19 import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
20 import com.android.launcher3.responsive.ResponsiveSpec.DimensionType
21 import com.android.launcher3.util.ResourceHelper
22 
23 /**
24  * A class to provide responsive grid specs for workspace, folder and all apps.
25  *
26  * This class is responsible for provide width and height [CalculatedResponsiveSpec] to be used for
27  * the correct placement of the workspace, all apps and folders.
28  *
29  * @param type A [ResponsiveSpecType] to indicates the type of the spec.
30  * @param groupOfSpecs Groups of responsive specifications
31  */
32 class ResponsiveSpecsProvider(
33     val type: ResponsiveSpecType,
34     groupOfSpecs: List<ResponsiveSpecGroup<ResponsiveSpec>>
35 ) {
36     private val groupOfSpecs: List<ResponsiveSpecGroup<ResponsiveSpec>>
37 
38     init {
39         this.groupOfSpecs =
40             groupOfSpecs
41                 .onEach { group ->
42                     check(group.widthSpecs.isNotEmpty() && group.heightSpecs.isNotEmpty()) {
43                         "$LOG_TAG is incomplete - " +
44                             "width list size = ${group.widthSpecs.size}; " +
45                             "height list size = ${group.heightSpecs.size}."
46                     }
47                 }
48                 .sortedBy { it.aspectRatio }
49     }
50 
51     fun getSpecsByAspectRatio(aspectRatio: Float): ResponsiveSpecGroup<ResponsiveSpec> {
52         check(aspectRatio > 0f) { "Invalid aspect ratio! The value should be bigger than 0." }
53 
54         val specsGroup = groupOfSpecs.firstOrNull { aspectRatio <= it.aspectRatio }
55         checkNotNull(specsGroup) { "No available spec with aspectRatio within $aspectRatio." }
56 
57         return specsGroup
58     }
59 
60     /**
61      * Retrieves a responsive grid specification that matches the number of [numCells],
62      * * [availableSpace] and [aspectRatio].
63      *
64      * @param aspectRatio the device width divided by device height (aspect ratio) to filter the
65      *   specifications
66      * @param dimensionType the grid axis of the spec width is x axis, height is y axis.
67      * @param numCells number of rows/columns in the grid
68      * @param availableSpace available width to filter the specifications
69      * @return A [CalculatedResponsiveSpec] that matches the parameters provided.
70      */
71     fun getCalculatedSpec(
72         aspectRatio: Float,
73         dimensionType: DimensionType,
74         numCells: Int,
75         availableSpace: Int,
76     ): CalculatedResponsiveSpec {
77         val specsGroup = getSpecsByAspectRatio(aspectRatio)
78         val spec = specsGroup.getSpec(dimensionType, availableSpace)
79         return CalculatedResponsiveSpec(aspectRatio, availableSpace, numCells, spec)
80     }
81 
82     /**
83      * Retrieves a responsive grid specification that matches the number of [numCells],
84      * * [availableSpace] and [aspectRatio]. This function uses a [CalculatedResponsiveSpec] to
85      *   match workspace when its true.
86      *
87      * @param aspectRatio the device width divided by device height (aspect ratio) to filter the
88      *   specifications
89      * @param dimensionType the grid axis of the spec width is x axis, height is y axis.
90      * @param numCells number of rows/columns in the grid
91      * @param availableSpace available width to filter the specifications
92      * @param calculatedWorkspaceSpec the calculated workspace specification to use its values as
93      *   base when matchWorkspace is true.
94      * @return A [CalculatedResponsiveSpec] that matches the parameters provided.
95      */
96     fun getCalculatedSpec(
97         aspectRatio: Float,
98         dimensionType: DimensionType,
99         numCells: Int,
100         availableSpace: Int,
101         calculatedWorkspaceSpec: CalculatedResponsiveSpec
102     ): CalculatedResponsiveSpec {
103         check(calculatedWorkspaceSpec.spec.dimensionType == dimensionType) {
104             "Invalid specType for CalculatedWorkspaceSpec. " +
105                 "Expected: $dimensionType - " +
106                 "Found: ${calculatedWorkspaceSpec.spec.dimensionType}}"
107         }
108 
109         check(calculatedWorkspaceSpec.isResponsiveSpecType(ResponsiveSpecType.Workspace)) {
110             "Invalid specType for CalculatedWorkspaceSpec. " +
111                 "Expected: ${ResponsiveSpecType.Workspace} - " +
112                 "Found: ${calculatedWorkspaceSpec.spec.specType}}"
113         }
114 
115         val specsGroup = getSpecsByAspectRatio(aspectRatio)
116         val spec = specsGroup.getSpec(dimensionType, availableSpace)
117         return CalculatedResponsiveSpec(
118             aspectRatio,
119             availableSpace,
120             numCells,
121             spec,
122             calculatedWorkspaceSpec
123         )
124     }
125 
126     companion object {
127         private const val LOG_TAG = "ResponsiveSpecsProvider"
128         @JvmStatic
129         fun create(
130             resourceHelper: ResourceHelper,
131             type: ResponsiveSpecType
132         ): ResponsiveSpecsProvider {
133             val parser = ResponsiveSpecsParser(resourceHelper)
134             val specs = parser.parseXML(type, ::ResponsiveSpec)
135             return ResponsiveSpecsProvider(type, specs)
136         }
137     }
138 }
139