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