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