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