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.demos 18 19 import android.content.Context 20 import android.content.Intent 21 import android.os.Bundle 22 import android.util.Log 23 import androidx.activity.ComponentActivity 24 import androidx.activity.compose.setContent 25 import androidx.compose.material.Text as ComposeText 26 import androidx.compose.runtime.Composable 27 import androidx.compose.runtime.getValue 28 import androidx.compose.runtime.mutableStateOf 29 import androidx.compose.runtime.remember 30 import androidx.compose.runtime.setValue 31 import androidx.compose.ui.graphics.Color 32 import androidx.compose.ui.unit.DpSize 33 import androidx.compose.ui.unit.dp 34 import androidx.compose.ui.unit.sp 35 import androidx.glance.Button 36 import androidx.glance.GlanceId 37 import androidx.glance.GlanceModifier 38 import androidx.glance.Image 39 import androidx.glance.ImageProvider 40 import androidx.glance.LocalContext 41 import androidx.glance.LocalSize 42 import androidx.glance.action.clickable 43 import androidx.glance.appwidget.CheckBox 44 import androidx.glance.appwidget.GlanceAppWidget 45 import androidx.glance.appwidget.GlanceAppWidgetReceiver 46 import androidx.glance.appwidget.SizeMode 47 import androidx.glance.appwidget.action.actionStartActivity 48 import androidx.glance.appwidget.lazy.GridCells 49 import androidx.glance.appwidget.lazy.LazyColumn 50 import androidx.glance.appwidget.lazy.itemsIndexed 51 import androidx.glance.appwidget.provideContent 52 import androidx.glance.background 53 import androidx.glance.layout.Alignment 54 import androidx.glance.layout.Column 55 import androidx.glance.layout.Row 56 import androidx.glance.layout.Spacer 57 import androidx.glance.layout.fillMaxHeight 58 import androidx.glance.layout.fillMaxSize 59 import androidx.glance.layout.fillMaxWidth 60 import androidx.glance.layout.padding 61 import androidx.glance.layout.size 62 import androidx.glance.layout.width 63 import androidx.glance.semantics.contentDescription 64 import androidx.glance.semantics.semantics 65 import androidx.glance.text.FontWeight 66 import androidx.glance.text.Text 67 import androidx.glance.text.TextAlign 68 import androidx.glance.text.TextDecoration 69 import androidx.glance.text.TextStyle 70 71 /** Sample AppWidget that showcase scrollable layouts using the LazyColumn */ 72 class ScrollableAppWidget : GlanceAppWidget() { 73 74 companion object { 75 private const val TAG = "ScrollableAppWidget" 76 77 private val singleColumn = DpSize(100.dp, 48.dp) 78 private val doubleColumn = DpSize(200.dp, 48.dp) 79 private val tripleColumn = DpSize(300.dp, 48.dp) 80 } 81 82 override val sizeMode: SizeMode = 83 SizeMode.Responsive(setOf(singleColumn, doubleColumn, tripleColumn)) 84 85 override suspend fun provideGlance(context: Context, id: GlanceId) = provideContent { 86 Content() 87 } 88 89 override suspend fun providePreview(context: Context, widgetCategory: Int) = provideContent { 90 Content() 91 } 92 93 @Composable 94 private fun Content() { 95 Column( 96 modifier = GlanceModifier.fillMaxSize().background(R.color.default_widget_background) 97 ) { 98 val localSize = LocalSize.current 99 Text( 100 text = "Fix header, LocalSize: ${localSize.width}x${localSize.height}", 101 modifier = 102 GlanceModifier.fillMaxWidth().padding(16.dp).background(Color(0x0a000000)) 103 ) 104 val width = localSize.width 105 when { 106 width <= singleColumn.width -> ScrollColumn(GlanceModifier.fillMaxSize()) 107 width <= doubleColumn.width -> 108 Row { 109 val modifier = GlanceModifier.fillMaxHeight().defaultWeight() 110 ScrollColumn(modifier) 111 ScrollColumn(modifier) 112 } 113 else -> SampleGrid(cells = GridCells.Fixed(3)) 114 } 115 } 116 } 117 118 @Composable 119 private fun ScrollColumn(modifier: GlanceModifier) { 120 val localSize = LocalSize.current 121 LazyColumn(modifier) { 122 item { SectionHeading(title = "LocalSize", description = "inside lazyColumn") } 123 item { 124 Text( 125 text = "${localSize.width}x${localSize.height}", 126 modifier = GlanceModifier.padding(10.dp) 127 ) 128 } 129 item { 130 SectionHeading( 131 title = "Activities", 132 description = "Click the buttons to open activities" 133 ) 134 } 135 136 itemsIndexed( 137 listOf( 138 GlanceAppWidgetDemoActivity::class.java, 139 ListClickDestinationActivity::class.java 140 ) 141 ) { index, activityClass -> 142 Row( 143 GlanceModifier.fillMaxWidth(), 144 horizontalAlignment = Alignment.Horizontal.CenterHorizontally 145 ) { 146 Button( 147 text = "Activity ${index + 1}", 148 onClick = 149 actionStartActivity( 150 Intent(LocalContext.current, activityClass).apply { 151 // Move this activity to the top of the stack, so it's obvious 152 // in this 153 // demo that the button has launched this activity. Otherwise, 154 // if 155 // another activity was opened on top, the target activity might 156 // be 157 // buried in the stack. 158 flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 159 } 160 ) 161 ) 162 } 163 } 164 165 item { 166 SectionHeading( 167 title = "Callbacks", 168 description = "Click the list items to invoke a callback" 169 ) 170 } 171 172 items(10) { index: Int -> 173 Text( 174 text = "Item $index", 175 modifier = 176 GlanceModifier.fillMaxWidth() 177 .padding(horizontal = 16.dp, vertical = 8.dp) 178 .clickable { Log.i(TAG, "Click from list item $index") } 179 ) 180 } 181 item { 182 // A11y services read out the contents as description of the row and call out 183 // "double tap to activate" as it is clickable. 184 Row( 185 verticalAlignment = Alignment.CenterVertically, 186 modifier = 187 GlanceModifier.padding(horizontal = 16.dp, vertical = 8.dp).clickable { 188 Log.i(TAG, "Click from an item row") 189 }, 190 ) { 191 Image( 192 provider = ImageProvider(R.drawable.compose), 193 contentDescription = "Compose logo", 194 modifier = GlanceModifier.size(20.dp) 195 ) 196 Spacer(modifier = GlanceModifier.width(5.dp)) 197 Text( 198 text = "Item with click on parent row", 199 modifier = GlanceModifier.fillMaxWidth() 200 ) 201 } 202 } 203 item { 204 // A11y services read out the semantics description of the row and call out 205 // "double tap to activate" as it is clickable. 206 Row( 207 modifier = 208 GlanceModifier.padding(horizontal = 16.dp, vertical = 8.dp) 209 .clickable { Log.i(TAG, "Click from an item row with semantics set") } 210 .semantics { 211 contentDescription = "A row with semantics description set" 212 } 213 ) { 214 Image( 215 provider = ImageProvider(R.drawable.compose), 216 contentDescription = "Compose logo", 217 modifier = GlanceModifier.size(20.dp) 218 ) 219 Spacer(modifier = GlanceModifier.width(5.dp)) 220 Text( 221 text = "Item with click on parent row with contentDescription set", 222 modifier = GlanceModifier.fillMaxWidth() 223 ) 224 } 225 } 226 item { SectionHeading(title = "Compound buttons", description = "Check buttons below") } 227 item { 228 var checked by remember { mutableStateOf(false) } 229 CheckBox( 230 checked = checked, 231 onCheckedChange = { checked = !checked }, 232 text = "Checkbox" 233 ) 234 } 235 } 236 } 237 238 @Composable 239 private fun SectionHeading(title: String, description: String) { 240 Column { 241 Text( 242 modifier = GlanceModifier.fillMaxWidth().padding(top = 8.dp), 243 text = title, 244 style = 245 TextStyle( 246 fontSize = 16.sp, 247 textDecoration = TextDecoration.Underline, 248 fontWeight = FontWeight.Bold, 249 textAlign = TextAlign.Center 250 ) 251 ) 252 Text( 253 text = description, 254 style = TextStyle(fontSize = 12.sp), 255 modifier = GlanceModifier.fillMaxWidth().padding(16.dp) 256 ) 257 } 258 } 259 } 260 261 /** Activity opened by clicking a list adapter item in [ScrollableAppWidget]. */ 262 class ListClickDestinationActivity : ComponentActivity() { onCreatenull263 override fun onCreate(savedInstanceState: Bundle?) { 264 super.onCreate(savedInstanceState) 265 setContent { ComposeText("Activity started from lazy list adapter item click.") } 266 } 267 } 268 269 class ScrollableAppWidgetReceiver : GlanceAppWidgetReceiver() { 270 override val glanceAppWidget: GlanceAppWidget = ScrollableAppWidget() 271 } 272