1 /*
<lambda>null2 * Copyright 2021 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.widget.RemoteViews
26 import androidx.glance.appwidget.InsertedViewInfo
27 import androidx.glance.appwidget.LayoutType
28 import androidx.glance.appwidget.RemoteCollectionItems
29 import androidx.glance.appwidget.TopLevelLayoutsCount
30 import androidx.glance.appwidget.TranslationContext
31 import androidx.glance.appwidget.applyModifiers
32 import androidx.glance.appwidget.insertView
33 import androidx.glance.appwidget.lazy.EmittableLazyColumn
34 import androidx.glance.appwidget.lazy.EmittableLazyList
35 import androidx.glance.appwidget.lazy.EmittableLazyListItem
36 import androidx.glance.appwidget.lazy.ReservedItemIdRangeEnd
37 import androidx.glance.appwidget.setRemoteAdapter
38 import androidx.glance.appwidget.toSizeString
39 import androidx.glance.appwidget.translateChild
40 import androidx.glance.appwidget.translateComposition
41 import androidx.glance.layout.Alignment
42
43 internal fun RemoteViews.translateEmittableLazyColumn(
44 translationContext: TranslationContext,
45 element: EmittableLazyColumn,
46 ) {
47 val viewDef = insertView(translationContext, LayoutType.List, element.modifier)
48 translateEmittableLazyList(
49 translationContext,
50 element,
51 viewDef,
52 )
53 }
54
RemoteViewsnull55 private fun RemoteViews.translateEmittableLazyList(
56 translationContext: TranslationContext,
57 element: EmittableLazyList,
58 viewDef: InsertedViewInfo,
59 ) {
60 check(!translationContext.isLazyCollectionDescendant) {
61 "Glance does not support nested list views."
62 }
63 setPendingIntentTemplate(
64 viewDef.mainViewId,
65 PendingIntent.getActivity(
66 translationContext.context,
67 0,
68 Intent(),
69 FILL_IN_COMPONENT or
70 FLAG_MUTABLE or
71 FLAG_UPDATE_CURRENT or
72 FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT,
73 element.activityOptions,
74 )
75 )
76 val items =
77 RemoteCollectionItems.Builder()
78 .apply {
79 val childContext = translationContext.forLazyCollection(viewDef.mainViewId)
80 element.children
81 .foldIndexed(false) { position, previous, itemEmittable ->
82 itemEmittable as EmittableLazyListItem
83 val itemId = itemEmittable.itemId
84 addItem(
85 itemId,
86 translateComposition(
87 childContext.forLazyViewItem(position, LazyListItemStartingViewId),
88 listOf(itemEmittable),
89 translationContext.layoutConfiguration?.addLayout(itemEmittable)
90 ?: -1,
91 )
92 )
93 // If the user specifies any explicit ids, we assume the list to be stable
94 previous || (itemId > ReservedItemIdRangeEnd)
95 }
96 .let { setHasStableIds(it) }
97 setViewTypeCount(TopLevelLayoutsCount)
98 }
99 .build()
100 setRemoteAdapter(
101 translationContext,
102 viewDef.mainViewId,
103 translationContext.layoutSize.toSizeString(),
104 items
105 )
106 applyModifiers(translationContext, this, element.modifier, viewDef)
107 }
108
109 /**
110 * Translates a list item either to its immediate only child, or a column layout wrapping all its
111 * children.
112 */
113 // TODO(b/202382495): Use complex generated layout instead of wrapping in an emittable box to
114 // support interaction animations in immediate children, e.g. checkboxes, pre-S
translateEmittableLazyListItemnull115 internal fun RemoteViews.translateEmittableLazyListItem(
116 translationContext: TranslationContext,
117 element: EmittableLazyListItem
118 ) {
119 require(element.children.size == 1 && element.alignment == Alignment.CenterStart) {
120 "Lazy list items can only have a single child align at the center start of the view. " +
121 "The normalization of the composition tree failed."
122 }
123 translateChild(translationContext, element.children.first())
124 }
125
126 // All the lazy list items should use the same ids, to ensure the layouts can be re-used.
127 // Using a very high number to avoid collision with the main app widget ids.
128 private const val LazyListItemStartingViewId: Int = 0x00100000
129