1 /*
2  * 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 android.content.res.TypedArray
20 import com.android.launcher3.R
21 import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType
22 import com.android.launcher3.responsive.ResponsiveSpec.DimensionType
23 
24 /**
25  * Base class for responsive specs that holds a list of width and height specs.
26  *
27  * @param widthSpecs List of width responsive specifications
28  * @param heightSpecs List of height responsive specifications
29  */
30 class ResponsiveSpecGroup<T : IResponsiveSpec>(
31     val aspectRatio: Float,
32     widthSpecs: List<T>,
33     heightSpecs: List<T>
34 ) {
35     val widthSpecs: List<T>
36     val heightSpecs: List<T>
37 
38     init {
<lambda>null39         check(aspectRatio > 0f) { "Invalid aspect ratio! Aspect ratio should be bigger than zero." }
<lambda>null40         this.widthSpecs = widthSpecs.sortedBy { it.maxAvailableSize }
<lambda>null41         this.heightSpecs = heightSpecs.sortedBy { it.maxAvailableSize }
42     }
43 
44     /**
45      * Get a [ResponsiveSpec] within the breakpoint.
46      *
47      * @param type Type of the spec to be retrieved (width or height)
48      * @param availableSize The breakpoint for the spec
49      * @return A [ResponsiveSpec].
50      */
getSpecnull51     fun getSpec(type: DimensionType, availableSize: Int): T {
52         val spec =
53             if (type == DimensionType.WIDTH) {
54                 widthSpecs.firstOrNull { availableSize <= it.maxAvailableSize }
55             } else {
56                 heightSpecs.firstOrNull { availableSize <= it.maxAvailableSize }
57             }
58         check(spec != null) { "No available $type spec found within $availableSize. $this" }
59         return spec
60     }
61 
toStringnull62     override fun toString(): String {
63         fun printSpec(spec: IResponsiveSpec) =
64             when (spec.specType) {
65                 ResponsiveSpecType.AllApps,
66                 ResponsiveSpecType.Folder,
67                 ResponsiveSpecType.Workspace -> (spec as ResponsiveSpec).toString()
68                 ResponsiveSpecType.Hotseat -> (spec as HotseatSpec).toString()
69                 ResponsiveSpecType.Cell -> (spec as CellSpec).toString()
70             }
71 
72         val widthSpecsString = widthSpecs.joinToString(", ") { printSpec(it) }
73         val heightSpecsString = heightSpecs.joinToString(", ") { printSpec(it) }
74         return "ResponsiveSpecGroup(" +
75             "aspectRatio=${aspectRatio}, " +
76             "widthSpecs=[${widthSpecsString}], " +
77             "heightSpecs=[${heightSpecsString}]" +
78             ")"
79     }
80 
81     companion object {
82         const val XML_GROUP_NAME = "specs"
83 
createnull84         fun <T : IResponsiveSpec> create(
85             attrs: TypedArray,
86             specs: List<T>
87         ): ResponsiveSpecGroup<T> {
88             val (widthSpecs, heightSpecs) =
89                 specs.partition { it.dimensionType == DimensionType.WIDTH }
90             val aspectRatio = attrs.getFloat(R.styleable.ResponsiveSpecGroup_maxAspectRatio, 0f)
91             return ResponsiveSpecGroup(aspectRatio, widthSpecs, heightSpecs)
92         }
93     }
94 }
95