1 /*
<lambda>null2  * Copyright 2022 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 androidx.glance.appwidget
18 
19 import android.os.Build
20 import androidx.compose.runtime.Composable
21 import androidx.compose.runtime.CompositionLocalProvider
22 import androidx.compose.ui.unit.DpSize
23 import androidx.glance.Emittable
24 import androidx.glance.EmittableWithChildren
25 import androidx.glance.GlanceModifier
26 import androidx.glance.GlanceNode
27 import androidx.glance.LocalSize
28 import androidx.glance.layout.fillMaxSize
29 
30 /**
31  * A marker for the translator that indicates that this [EmittableSizeBox] and its children should
32  * be translated into a distinct [android.widget.RemoteViews] object.
33  *
34  * EmittableSizeBox is only functional when it is a direct child of the root [RemoteViewsRoot].
35  * Multiple EmittableSizeBox children will each be translated into a distinct RemoteViews, then
36  * combined into one multi-sized RemoteViews.
37  */
38 internal class EmittableSizeBox : EmittableWithChildren() {
39     override var modifier: GlanceModifier
40         get() = children.singleOrNull()?.modifier ?: GlanceModifier.fillMaxSize()
41         set(_) {
42             throw IllegalAccessError("You cannot set the modifier of an EmittableSizeBox")
43         }
44 
45     var size: DpSize = DpSize.Unspecified
46     var sizeMode: SizeMode = SizeMode.Single
47 
48     override fun copy(): Emittable =
49         EmittableSizeBox().also {
50             it.size = size
51             it.sizeMode = sizeMode
52             it.children.addAll(children.map { it.copy() })
53         }
54 
55     override fun toString(): String =
56         "EmittableSizeBox(" +
57             "size=$size, " +
58             "sizeMode=$sizeMode, " +
59             "children=[\n${childrenToString()}\n]" +
60             ")"
61 }
62 
63 /**
64  * This composable emits a marker that lets the translator know that this [SizeBox] and its children
65  * should be translated into a distinct RemoteViews that is then combined with its siblings to form
66  * a multi-sized RemoteViews.
67  *
68  * This should not be used directly. The correct SizeBoxes can be generated with [ForEachSize].
69  */
70 @Composable
71 internal fun SizeBox(size: DpSize, sizeMode: SizeMode, content: @Composable () -> Unit) {
<lambda>null72     CompositionLocalProvider(LocalSize provides size) {
73         GlanceNode(
74             factory = ::EmittableSizeBox,
75             update = {
76                 this.set(size) { this.size = it }
77                 this.set(sizeMode) { this.sizeMode = it }
78             },
79             content = content
80         )
81     }
82 }
83 
84 /**
85  * For each size indicated by [sizeMode], run [content] with a [SizeBox] set to the corresponding
86  * size.
87  */
88 @Composable
89 internal fun ForEachSize(sizeMode: SizeMode, minSize: DpSize, content: @Composable () -> Unit) {
90     val sizes =
91         when (sizeMode) {
92             is SizeMode.Single -> listOf(minSize)
93             is SizeMode.Exact ->
94                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
<lambda>null95                     LocalAppWidgetOptions.current.extractAllSizes { minSize }
96                 } else {
<lambda>null97                     LocalAppWidgetOptions.current.extractOrientationSizes().ifEmpty {
98                         listOf(minSize)
99                     }
100                 }
101             is SizeMode.Responsive ->
102                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
103                     sizeMode.sizes
104                 } else {
105                     val smallestSize = sizeMode.sizes.sortedBySize()[0]
106                     LocalAppWidgetOptions.current
107                         .extractOrientationSizes()
<lambda>null108                         .map { findBestSize(it, sizeMode.sizes) ?: smallestSize }
<lambda>null109                         .ifEmpty { listOf(smallestSize, smallestSize) }
110                 }
111         }
sizenull112     sizes.distinct().map { size -> SizeBox(size, sizeMode, content) }
113 }
114