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 android.os.Handler
21 import android.widget.Toast
22 import androidx.compose.runtime.Composable
23 import androidx.compose.ui.graphics.Color
24 import androidx.compose.ui.unit.DpSize
25 import androidx.compose.ui.unit.dp
26 import androidx.glance.Button
27 import androidx.glance.ButtonDefaults
28 import androidx.glance.GlanceId
29 import androidx.glance.GlanceModifier
30 import androidx.glance.LocalContext
31 import androidx.glance.LocalSize
32 import androidx.glance.action.ActionParameters
33 import androidx.glance.appwidget.GlanceAppWidget
34 import androidx.glance.appwidget.GlanceAppWidgetReceiver
35 import androidx.glance.appwidget.SizeMode
36 import androidx.glance.appwidget.provideContent
37 import androidx.glance.background
38 import androidx.glance.layout.Alignment
39 import androidx.glance.layout.Box
40 import androidx.glance.layout.Column
41 import androidx.glance.layout.Row
42 import androidx.glance.layout.fillMaxSize
43 import androidx.glance.layout.padding
44 import androidx.glance.layout.size
45 import androidx.glance.text.TextAlign
46 import androidx.glance.text.TextStyle
47 import androidx.glance.unit.ColorProvider
48
49 /**
50 * Sample AppWidget that showcase the Responsive SizeMode changing its content to Row, Column or Box
51 * based on the available space. In addition to shows how alignment and default weight works on
52 * these components.
53 */
54 class ResponsiveAppWidget : GlanceAppWidget() {
55
56 companion object {
57 private val SMALL_BOX = DpSize(90.dp, 90.dp)
58 private val BIG_BOX = DpSize(180.dp, 180.dp)
59 private val VERY_BIG_BOX = DpSize(300.dp, 300.dp)
60 private val ROW = DpSize(180.dp, 48.dp)
61 private val LARGE_ROW = DpSize(300.dp, 48.dp)
62 private val COLUMN = DpSize(48.dp, 180.dp)
63 private val LARGE_COLUMN = DpSize(48.dp, 300.dp)
64 }
65
66 override val sizeMode =
67 SizeMode.Responsive(setOf(SMALL_BOX, BIG_BOX, ROW, LARGE_ROW, COLUMN, LARGE_COLUMN))
68
<lambda>null69 override suspend fun provideGlance(context: Context, id: GlanceId) = provideContent {
70 Content()
71 }
72
73 override val previewSizeMode =
74 SizeMode.Responsive(setOf(SMALL_BOX, BIG_BOX, ROW, LARGE_ROW, COLUMN, LARGE_COLUMN))
75
<lambda>null76 override suspend fun providePreview(context: Context, widgetCategory: Int) = provideContent {
77 Content()
78 }
79
80 @Composable
Contentnull81 private fun Content() {
82 // Content will be called for each of the provided sizes
83 when (LocalSize.current) {
84 COLUMN -> ResponsiveColumn(numItems = 3)
85 ROW -> ResponsiveRow(numItems = 3)
86 LARGE_COLUMN -> ResponsiveColumn(numItems = 5)
87 LARGE_ROW -> ResponsiveRow(numItems = 5)
88 SMALL_BOX -> ResponsiveBox(numItems = 1)
89 BIG_BOX -> ResponsiveBox(numItems = 3)
90 VERY_BIG_BOX -> ResponsiveBox(numItems = 5)
91 else -> throw IllegalArgumentException("Invalid size not matching the provided ones")
92 }
93 }
94 }
95
96 private val ItemClickedKey = ActionParameters.Key<String>("name")
97
98 private val parentModifier =
99 GlanceModifier.fillMaxSize().padding(8.dp).background(R.color.default_widget_background)
100
101 private val columnColors = listOf(Color(0xff70D689), Color(0xffB2E5BF))
102 private val rowColors = listOf(Color(0xff5087EF), Color(0xffA2BDF2))
103 private val boxColors = listOf(Color(0xffF7A998), Color(0xffFA5F3D))
104
105 /** Displays a column with three items that share evenly the available space */
106 @Composable
ResponsiveColumnnull107 private fun ResponsiveColumn(numItems: Int) {
108 Column(parentModifier) {
109 val modifier = GlanceModifier.fillMaxSize().padding(4.dp).defaultWeight()
110 (1..numItems).forEach {
111 val color = columnColors[(it - 1) % columnColors.size]
112 ContentItem("$it", color, modifier)
113 }
114 }
115 }
116
117 /** Displays a row with three items that share evenly the available space */
118 @Composable
ResponsiveRownull119 private fun ResponsiveRow(numItems: Int) {
120 Row(parentModifier) {
121 val modifier = GlanceModifier.fillMaxSize().padding(4.dp).defaultWeight()
122 (1..numItems).forEach {
123 val color = rowColors[(it - 1) % rowColors.size]
124 ContentItem("$it", color, modifier)
125 }
126 }
127 }
128
129 /** Displays a Box with three items on top of each other */
130 @Composable
ResponsiveBoxnull131 private fun ResponsiveBox(numItems: Int) {
132 val size = LocalSize.current
133 Box(modifier = parentModifier, contentAlignment = Alignment.Center) {
134 (1..numItems).forEach {
135 val index = numItems - it + 1
136 val color = boxColors[(index - 1) % boxColors.size]
137 val boxSize = (size.width * index) / numItems
138 ContentItem(
139 "$index",
140 color,
141 GlanceModifier.size(boxSize),
142 textStyle = TextStyle(textAlign = TextAlign.End).takeIf { numItems != 1 }
143 )
144 }
145 }
146 }
147
148 @Composable
ContentItemnull149 private fun ContentItem(
150 text: String,
151 color: Color,
152 modifier: GlanceModifier,
153 textStyle: TextStyle? = null
154 ) {
155 Box(modifier = modifier) {
156 val context = LocalContext.current
157 Button(
158 text = text,
159 modifier = GlanceModifier.fillMaxSize().padding(8.dp).background(color),
160 colors =
161 ButtonDefaults.buttonColors(
162 backgroundColor = ColorProvider(color),
163 contentColor = ColorProvider(Color.White)
164 ),
165 style = textStyle ?: TextStyle(textAlign = TextAlign.Center),
166 onClick = {
167 Handler(context.mainLooper).post {
168 Toast.makeText(context, "Item clicked: $text", Toast.LENGTH_SHORT).show()
169 }
170 }
171 )
172 }
173 }
174
175 class ResponsiveAppWidgetReceiver : GlanceAppWidgetReceiver() {
176 override val glanceAppWidget: GlanceAppWidget = ResponsiveAppWidget()
177 }
178