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