1 /* 2 * 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 androidx.compose.runtime.Composable 21 import androidx.compose.runtime.getValue 22 import androidx.compose.runtime.mutableStateOf 23 import androidx.compose.runtime.remember 24 import androidx.compose.runtime.setValue 25 import androidx.compose.ui.graphics.Color 26 import androidx.compose.ui.unit.dp 27 import androidx.glance.ColorFilter 28 import androidx.glance.GlanceId 29 import androidx.glance.GlanceModifier 30 import androidx.glance.GlanceTheme 31 import androidx.glance.Image 32 import androidx.glance.ImageProvider 33 import androidx.glance.action.clickable 34 import androidx.glance.appwidget.GlanceAppWidget 35 import androidx.glance.appwidget.GlanceAppWidgetReceiver 36 import androidx.glance.appwidget.appWidgetBackground 37 import androidx.glance.appwidget.cornerRadius 38 import androidx.glance.appwidget.provideContent 39 import androidx.glance.background 40 import androidx.glance.color.ColorProvider 41 import androidx.glance.layout.Alignment 42 import androidx.glance.layout.Box 43 import androidx.glance.layout.Column 44 import androidx.glance.layout.ContentScale 45 import androidx.glance.layout.Row 46 import androidx.glance.layout.Spacer 47 import androidx.glance.layout.fillMaxSize 48 import androidx.glance.layout.fillMaxWidth 49 import androidx.glance.layout.height 50 import androidx.glance.layout.padding 51 import androidx.glance.layout.size 52 import androidx.glance.text.FontWeight 53 import androidx.glance.text.Text 54 import androidx.glance.text.TextAlign 55 import androidx.glance.text.TextStyle 56 57 /** Sample AppWidget to showcase the ripples. Note: Rounded corners are supported in S+ */ 58 class RippleAppWidget : GlanceAppWidget() { 59 private val columnBgColorsA = listOf(Color(0xffA2BDF2), Color(0xff5087EF)) 60 private val columnBgColorsB = listOf(Color(0xFFBD789C), Color(0xFF880E4F)) 61 private val boxColors = listOf(Color(0xffF7A998), Color(0xffFA5F3D)) 62 <lambda>null63 override suspend fun provideGlance(context: Context, id: GlanceId) = provideContent { 64 Content() 65 } 66 <lambda>null67 override suspend fun providePreview(context: Context, widgetCategory: Int) = provideContent { 68 Content() 69 } 70 71 @Composable Contentnull72 private fun Content() { 73 @Suppress("AutoboxingStateCreation") var count by remember { mutableStateOf(0) } 74 var type by remember { mutableStateOf(ContentScale.Fit) } 75 var columnBgColors by remember { mutableStateOf(columnBgColorsA) } 76 77 Column( 78 horizontalAlignment = Alignment.CenterHorizontally, 79 modifier = 80 GlanceModifier.fillMaxSize() 81 .padding(8.dp) 82 .cornerRadius(R.dimen.corner_radius) 83 .appWidgetBackground() 84 .background(ColorProvider(day = columnBgColors[0], night = columnBgColors[1])) 85 .clickable { 86 columnBgColors = 87 when (columnBgColors[0]) { 88 columnBgColorsA[0] -> columnBgColorsB 89 else -> columnBgColorsA 90 } 91 } 92 ) { 93 Row(verticalAlignment = Alignment.CenterVertically) { 94 Text( 95 text = "Content Scale: ${type.asString()}, Image / Box click count: $count", 96 modifier = GlanceModifier.padding(5.dp).defaultWeight() 97 ) 98 // Demonstrates an icon button with circular ripple. 99 Image( 100 provider = ImageProvider(R.drawable.ic_color_reset), 101 contentDescription = "Remove background color", 102 colorFilter = ColorFilter.tint(GlanceTheme.colors.secondary), 103 modifier = 104 GlanceModifier.padding(5.dp) 105 .cornerRadius(24.dp) // To get a rounded ripple 106 .clickable { 107 columnBgColors = listOf(Color.Transparent, Color.Transparent) 108 } 109 ) 110 } 111 // A drawable image with rounded corners and a click modifier. 112 OutlinedButtonUsingImage( 113 text = "Toggle content scale", 114 onClick = { 115 type = 116 when (type) { 117 ContentScale.Crop -> ContentScale.FillBounds 118 ContentScale.FillBounds -> ContentScale.Fit 119 else -> ContentScale.Crop 120 } 121 } 122 ) 123 Spacer(GlanceModifier.size(5.dp)) 124 Text( 125 text = "Image in a clickable box with rounded corners", 126 modifier = GlanceModifier.padding(5.dp) 127 ) 128 ImageInClickableBoxWithRoundedCorners(contentScale = type, onClick = { count++ }) 129 Spacer(GlanceModifier.size(5.dp)) 130 Text( 131 text = "Rounded corner image in a clickable box", 132 modifier = GlanceModifier.padding(5.dp) 133 ) 134 RoundedImageInClickableBox(contentScale = type, onClick = { count++ }) 135 } 136 } 137 138 @Composable ImageInClickableBoxWithRoundedCornersnull139 private fun ImageInClickableBoxWithRoundedCorners( 140 contentScale: ContentScale, 141 onClick: () -> Unit 142 ) { 143 Box( 144 modifier = 145 GlanceModifier.height(100.dp) 146 .background(ColorProvider(day = boxColors[0], night = boxColors[1])) 147 .cornerRadius(25.dp) 148 .clickable(onClick) 149 ) { 150 Image( 151 provider = ImageProvider(R.drawable.compose), 152 contentDescription = "Image sample in a box with rounded corners", 153 contentScale = contentScale, 154 modifier = GlanceModifier.fillMaxSize() 155 ) 156 } 157 } 158 159 @Composable RoundedImageInClickableBoxnull160 private fun RoundedImageInClickableBox(contentScale: ContentScale, onClick: () -> Unit) { 161 Box( 162 modifier = 163 GlanceModifier.height(100.dp) 164 .background(ColorProvider(day = boxColors[0], night = boxColors[1])) 165 .clickable(onClick) 166 ) { 167 Image( 168 provider = ImageProvider(R.drawable.compose), 169 contentDescription = "Image sample with rounded corners", 170 contentScale = contentScale, 171 modifier = GlanceModifier.fillMaxSize().cornerRadius(25.dp) 172 ) 173 } 174 } 175 176 @Composable OutlinedButtonUsingImagenull177 fun OutlinedButtonUsingImage( 178 text: String, 179 onClick: () -> Unit, 180 ) { 181 Box( 182 modifier = GlanceModifier.height(40.dp).fillMaxWidth(), 183 contentAlignment = Alignment.Center 184 ) { 185 // Demonstrates a button with rounded outline using a clickable image. Alternatively, 186 // such button can also be created using Box + Text by adding background image, corner 187 // radius and click modifiers to the box. 188 Image( 189 provider = ImageProvider(R.drawable.ic_outlined_button), 190 contentDescription = "Outlined button sample", 191 // Radius value matched with the border in the outline image so that the ripple 192 // matches it (in versions that support cornerRadius modifier). 193 modifier = GlanceModifier.fillMaxSize().cornerRadius(20.dp).clickable(onClick) 194 ) 195 Text( 196 text = text, 197 style = TextStyle(fontWeight = FontWeight.Medium, textAlign = TextAlign.Center), 198 modifier = GlanceModifier.background(Color.Transparent) 199 ) 200 } 201 } 202 asStringnull203 private fun ContentScale.asString(): String = 204 when (this) { 205 ContentScale.Fit -> "Fit" 206 ContentScale.FillBounds -> "Fill Bounds" 207 ContentScale.Crop -> "Crop" 208 else -> "Unknown content scale" 209 } 210 } 211 212 class RippleAppWidgetReceiver : GlanceAppWidgetReceiver() { 213 override val glanceAppWidget: GlanceAppWidget = RippleAppWidget() 214 } 215