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.translators
18
19 import android.app.PendingIntent
20 import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
21 import android.app.PendingIntent.FLAG_MUTABLE
22 import android.app.PendingIntent.FLAG_UPDATE_CURRENT
23 import android.content.Intent
24 import android.content.Intent.FILL_IN_COMPONENT
25 import android.os.Build
26 import android.widget.RemoteViews
27 import androidx.core.widget.RemoteViewsCompat.setGridViewColumnWidth
28 import androidx.glance.appwidget.InsertedViewInfo
29 import androidx.glance.appwidget.LayoutType
30 import androidx.glance.appwidget.RemoteCollectionItems
31 import androidx.glance.appwidget.TopLevelLayoutsCount
32 import androidx.glance.appwidget.TranslationContext
33 import androidx.glance.appwidget.applyModifiers
34 import androidx.glance.appwidget.insertView
35 import androidx.glance.appwidget.lazy.EmittableLazyVerticalGrid
36 import androidx.glance.appwidget.lazy.EmittableLazyVerticalGridListItem
37 import androidx.glance.appwidget.lazy.GridCells
38 import androidx.glance.appwidget.lazy.ReservedItemIdRangeEnd
39 import androidx.glance.appwidget.setRemoteAdapter
40 import androidx.glance.appwidget.toSizeString
41 import androidx.glance.appwidget.translateChild
42 import androidx.glance.appwidget.translateComposition
43 import androidx.glance.layout.Alignment
44
45 /** Translates an EmittableLazyVerticalGrid and its children to a EmittableLazyList. */
46 internal fun RemoteViews.translateEmittableLazyVerticalGrid(
47 translationContext: TranslationContext,
48 element: EmittableLazyVerticalGrid,
49 ) {
50
51 val viewDef = insertView(translationContext, element.gridCells.toLayout(), element.modifier)
52 translateEmittableLazyVerticalGrid(
53 translationContext,
54 element,
55 viewDef,
56 )
57 }
58
RemoteViewsnull59 private fun RemoteViews.translateEmittableLazyVerticalGrid(
60 translationContext: TranslationContext,
61 element: EmittableLazyVerticalGrid,
62 viewDef: InsertedViewInfo,
63 ) {
64 check(!translationContext.isLazyCollectionDescendant) {
65 "Glance does not support nested list views."
66 }
67
68 val gridCells = element.gridCells
69 if (gridCells is GridCells.Fixed) {
70 require(gridCells.count in 1..5) { "Only counts from 1 to 5 are supported." }
71 }
72 setPendingIntentTemplate(
73 viewDef.mainViewId,
74 PendingIntent.getActivity(
75 translationContext.context,
76 0,
77 Intent(),
78 FILL_IN_COMPONENT or
79 FLAG_MUTABLE or
80 FLAG_UPDATE_CURRENT or
81 FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
82 element.activityOptions,
83 )
84 )
85 val items =
86 RemoteCollectionItems.Builder()
87 .apply {
88 val childContext = translationContext.forLazyCollection(viewDef.mainViewId)
89 element.children
90 .foldIndexed(false) { position, previous, itemEmittable ->
91 itemEmittable as EmittableLazyVerticalGridListItem
92 val itemId = itemEmittable.itemId
93 addItem(
94 itemId,
95 translateComposition(
96 childContext.forLazyViewItem(
97 position,
98 LazyVerticalGridItemStartingViewId
99 ),
100 listOf(itemEmittable),
101 translationContext.layoutConfiguration?.addLayout(itemEmittable)
102 ?: -1,
103 )
104 )
105 // If the user specifies any explicit ids, we assume the list to be stable
106 previous || (itemId > ReservedItemIdRangeEnd)
107 }
108 .let { setHasStableIds(it) }
109 setViewTypeCount(TopLevelLayoutsCount)
110 }
111 .build()
112 setRemoteAdapter(
113 translationContext,
114 viewDef.mainViewId,
115 translationContext.layoutSize.toSizeString(),
116 items
117 )
118 if (Build.VERSION.SDK_INT >= 31 && gridCells is GridCells.Adaptive) {
119 setGridViewColumnWidth(
120 viewId = viewDef.mainViewId,
121 value = gridCells.minSize.value,
122 unit = android.util.TypedValue.COMPLEX_UNIT_DIP
123 )
124 }
125 applyModifiers(translationContext, this, element.modifier, viewDef)
126 }
127
128 /**
129 * Translates a list item either to its immediate only child, or a column layout wrapping all its
130 * children.
131 */
translateEmittableLazyVerticalGridListItemnull132 internal fun RemoteViews.translateEmittableLazyVerticalGridListItem(
133 translationContext: TranslationContext,
134 element: EmittableLazyVerticalGridListItem
135 ) {
136 require(element.children.size == 1 && element.alignment == Alignment.CenterStart) {
137 "Lazy vertical grid items can only have a single child align at the center start of the " +
138 "view. The normalization of the composition tree failed."
139 }
140 translateChild(translationContext, element.children.first())
141 }
142
143 // All the lazy list items should use the same ids, to ensure the layouts can be re-used.
144 // Using a very high number to avoid collision with the main app widget ids.
145 private const val LazyVerticalGridItemStartingViewId: Int = 0x00100000
146
GridCellsnull147 private fun GridCells.toLayout(): LayoutType =
148 when (this) {
149 GridCells.Fixed(1) -> LayoutType.VerticalGridOneColumn
150 GridCells.Fixed(2) -> LayoutType.VerticalGridTwoColumns
151 GridCells.Fixed(3) -> LayoutType.VerticalGridThreeColumns
152 GridCells.Fixed(4) -> LayoutType.VerticalGridFourColumns
153 GridCells.Fixed(5) -> LayoutType.VerticalGridFiveColumns
154 else -> LayoutType.VerticalGridAutoFit
155 }
156