diff --git a/compose/snippets/build.gradle.kts b/compose/snippets/build.gradle.kts
index e9f1e227..c2e5d9d7 100644
--- a/compose/snippets/build.gradle.kts
+++ b/compose/snippets/build.gradle.kts
@@ -77,6 +77,7 @@ android {
}
dependencies {
+ implementation(libs.androidx.work.runtime.ktx)
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
androidTestImplementation(composeBom)
@@ -106,6 +107,9 @@ dependencies {
implementation(libs.androidx.coordinator.layout)
implementation(libs.google.android.material)
+ implementation(libs.androidx.glance.appwidget)
+ implementation(libs.androidx.glance.material3)
+
implementation(libs.accompanist.theme.adapter.appcompat)
implementation(libs.accompanist.theme.adapter.material3)
implementation(libs.accompanist.theme.adapter.material)
diff --git a/compose/snippets/src/main/AndroidManifest.xml b/compose/snippets/src/main/AndroidManifest.xml
index 5f111328..4c4ec51a 100644
--- a/compose/snippets/src/main/AndroidManifest.xml
+++ b/compose/snippets/src/main/AndroidManifest.xml
@@ -46,6 +46,18 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/glance/GlanceSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/glance/GlanceSnippets.kt
new file mode 100644
index 00000000..e28eadb6
--- /dev/null
+++ b/compose/snippets/src/main/java/com/example/compose/snippets/glance/GlanceSnippets.kt
@@ -0,0 +1,994 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.compose.snippets.glance
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.app.Service
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.IBinder
+import android.widget.RemoteViews
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.material3.ColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.glance.Button
+import androidx.glance.ColorFilter
+import androidx.glance.GlanceId
+import androidx.glance.GlanceModifier
+import androidx.glance.GlanceTheme
+import androidx.glance.Image
+import androidx.glance.ImageProvider
+import androidx.glance.LocalContext
+import androidx.glance.LocalSize
+import androidx.glance.action.ActionParameters
+import androidx.glance.action.actionParametersOf
+import androidx.glance.action.actionStartActivity
+import androidx.glance.action.clickable
+import androidx.glance.appwidget.AndroidRemoteViews
+import androidx.glance.appwidget.CheckBox
+import androidx.glance.appwidget.CheckboxDefaults
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetManager
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.RadioButton
+import androidx.glance.appwidget.RadioButtonDefaults
+import androidx.glance.appwidget.SizeMode
+import androidx.glance.appwidget.Switch
+import androidx.glance.appwidget.SwitchDefaults
+import androidx.glance.appwidget.action.ActionCallback
+import androidx.glance.appwidget.action.actionRunCallback
+import androidx.glance.appwidget.action.actionSendBroadcast
+import androidx.glance.appwidget.action.actionStartService
+import androidx.glance.appwidget.provideContent
+import androidx.glance.appwidget.updateAll
+import androidx.glance.appwidget.updateIf
+import androidx.glance.background
+import androidx.glance.color.ColorProvider
+import androidx.glance.layout.Alignment
+import androidx.glance.layout.Column
+import androidx.glance.layout.Row
+import androidx.glance.layout.RowScope
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.fillMaxWidth
+import androidx.glance.layout.padding
+import androidx.glance.material3.ColorProviders
+import androidx.glance.text.Text
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import com.example.compose.snippets.R
+import com.example.compose.snippets.layouts.MainActivity
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+lateinit var LightColors: ColorScheme
+lateinit var DarkColors: ColorScheme
+
+private object GlanceCreateAppWidgetSnippet01 {
+ // [START android_compose_glance_receiver01]
+ class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
+ override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
+ }
+ // [END android_compose_glance_receiver01]
+}
+
+private object GlanceCreateAppWidgetSnippet02 {
+ // [START android_compose_glance_receiver02]
+ class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
+
+ // Let MyAppWidgetReceiver know which GlanceAppWidget to use
+ override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
+ }
+ // [END android_compose_glance_receiver02]
+
+ // [START android_compose_glance_widget]
+ class MyAppWidget : GlanceAppWidget() {
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+
+ // In this method, load data needed to render the AppWidget.
+ // Use `withContext` to switch to another thread for long running
+ // operations.
+
+ provideContent {
+ // create your AppWidget here
+ Text("Hello World")
+ }
+ }
+ }
+ // [END android_compose_glance_widget]
+}
+
+private object CreateUI {
+ // [START android_compose_glance_createui]
+ /* Import Glance Composables
+ In the event there is a name clash with the Compose classes of the same name,
+ you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
+ using the as keyword.
+
+ import androidx.glance.Button
+ import androidx.glance.layout.Column
+ import androidx.glance.layout.Row
+ import androidx.glance.text.Text
+ */
+ class MyAppWidget : GlanceAppWidget() {
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ // Load data needed to render the AppWidget.
+ // Use `withContext` to switch to another thread for long running
+ // operations.
+
+ provideContent {
+ // create your AppWidget here
+ MyContent()
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+ Column(
+ modifier = GlanceModifier.fillMaxSize(),
+ verticalAlignment = Alignment.Top,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
+ Row(horizontalAlignment = Alignment.CenterHorizontally) {
+ Button(
+ text = "Home",
+ onClick = actionStartActivity()
+ )
+ Button(
+ text = "Work",
+ onClick = actionStartActivity()
+ )
+ }
+ }
+ }
+ }
+ // [END android_compose_glance_createui]
+}
+
+private object ActionLaunchActivity {
+
+ // [START android_compose_glance_launchactivity]
+ @Composable
+ fun MyContent() {
+ // ..
+ Button(
+ text = "Go Home",
+ onClick = actionStartActivity()
+ )
+ }
+ // [END android_compose_glance_launchactivity]
+}
+
+private object ActionLaunchService {
+
+ // [START android_compose_glance_launchservice]
+ @Composable
+ fun MyButton() {
+ // ..
+ Button(
+ text = "Sync",
+ onClick = actionStartService(
+ isForegroundService = true // define how the service is launched
+ )
+ )
+ }
+ // [END android_compose_glance_launchservice]
+}
+
+private object ActionLaunchSendBroadcastEvent {
+
+ // [START android_compose_glance_sendbroadcastevent]
+ @Composable
+ fun MyButton() {
+ // ..
+ Button(
+ text = "Send",
+ onClick = actionSendBroadcast()
+ )
+ }
+ // [END android_compose_glance_sendbroadcastevent]
+}
+
+private object ActionLambda {
+ @Composable
+ fun actionLambda() {
+ // [START android_compose_glance_lambda01]
+ Text(
+ text = "Submit",
+ modifier = GlanceModifier.clickable {
+ submitData()
+ }
+ )
+ // [END android_compose_glance_lambda01]
+ }
+
+ @Composable
+ fun actionLambda2() {
+ // [START android_compose_glance_lambda02]
+ Button(
+ text = "Submit",
+ onClick = {
+ submitData()
+ }
+ )
+ // [END android_compose_glance_lambda02]
+ }
+}
+
+private object ActionCallbackSnippet02 {
+ // [START android_compose_glance_actioncallback02]
+ class RefreshAction : ActionCallback {
+ override suspend fun onAction(
+ context: Context,
+ glanceId: GlanceId,
+ parameters: ActionParameters
+ ) {
+ // do some work but offset long-term tasks (e.g a Worker)
+ MyAppWidget().update(context, glanceId)
+ }
+ }
+
+ // [END android_compose_glance_actioncallback02]
+ /*dummy class*/
+ class MyAppWidget : GlanceAppWidget() {
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ TODO("Not yet implemented")
+ }
+ }
+}
+
+private object ActionCallbackSnippet01 {
+ // [START android_compose_glance_actioncallback01]
+ @Composable
+ private fun MyContent() {
+ // ..
+ Image(
+ provider = ImageProvider(R.drawable.ic_hourglass_animated),
+ modifier = GlanceModifier.clickable(
+ onClick = actionRunCallback()
+ ),
+ contentDescription = "Refresh"
+ )
+ }
+
+ class RefreshAction : ActionCallback {
+ override suspend fun onAction(
+ context: Context,
+ glanceId: GlanceId,
+ parameters: ActionParameters
+ ) {
+ // TODO implement
+ }
+ }
+ // [END android_compose_glance_actioncallback01]
+}
+
+private object ActionParameters {
+ // [START android_compose_glance_actionparameters01]
+ private val destinationKey = ActionParameters.Key(
+ NavigationActivity.KEY_DESTINATION
+ )
+
+ class MyAppWidget : GlanceAppWidget() {
+
+ // ..
+
+ @Composable
+ private fun MyContent() {
+ // ..
+ Button(
+ text = "Home",
+ onClick = actionStartActivity(
+ actionParametersOf(destinationKey to "home")
+ )
+ )
+ Button(
+ text = "Work",
+ onClick = actionStartActivity(
+ actionParametersOf(destinationKey to "work")
+ )
+ )
+ }
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ provideContent { MyContent() }
+ }
+ }
+ // [END android_compose_glance_actionparameters01]
+
+ abstract class ActionParametersActivity : Activity() {
+ val KEY_DESTINATION = "destination"
+
+ // [START android_compose_glance_actionparameters02]
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val destination = intent.extras?.getString(KEY_DESTINATION) ?: return
+ // ...
+ }
+ }
+ // [END android_compose_glance_actionparameters02]
+
+ // [START android_compose_glance_actionparameters03]
+ class RefreshAction : ActionCallback {
+
+ private val destinationKey = ActionParameters.Key(
+ NavigationActivity.KEY_DESTINATION
+ )
+
+ override suspend fun onAction(
+ context: Context,
+ glanceId: GlanceId,
+ parameters: ActionParameters
+ ) {
+ val destination: String = parameters[destinationKey] ?: return
+ // ...
+ }
+ }
+ // [END android_compose_glance_actionparameters03]
+}
+
+@SuppressLint("RememberReturnType")
+object ManageAndUpdate {
+ abstract
+ // [START android_compose_glance_manageupdate01]
+ class DestinationAppWidget : GlanceAppWidget() {
+
+ // ...
+
+ @Composable
+ fun MyContent() {
+ val repository = remember { DestinationsRepository.getInstance() }
+ // Retrieve the cache data everytime the content is refreshed
+ val destinations by repository.destinations.collectAsState(State.Loading)
+
+ when (destinations) {
+ is State.Loading -> {
+ // show loading content
+ }
+
+ is State.Error -> {
+ // show widget error content
+ }
+
+ is State.Completed -> {
+ // show the list of destinations
+ }
+ }
+ }
+ }
+ // [END android_compose_glance_manageupdate01]
+
+ suspend fun update02(context: Context, glanceId: GlanceId) {
+ // [START android_compose_glance_manageupdate02]
+ MyAppWidget().update(context, glanceId)
+ // [END android_compose_glance_manageupdate02]
+
+ // [START android_compose_glance_manageupdate03]
+ val manager = GlanceAppWidgetManager(context)
+ val widget = GlanceSizeModeWidget()
+ val glanceIds = manager.getGlanceIds(widget.javaClass)
+ glanceIds.forEach { glanceId ->
+ widget.update(context, glanceId)
+ }
+ // [END android_compose_glance_manageupdate03]
+
+ // [START android_compose_glance_manageupdate04]
+ // Updates all placed instances of MyAppWidget
+ MyAppWidget().updateAll(context)
+
+ // Iterate over all placed instances of MyAppWidget and update if the state of
+ // the instance matches the given predicate
+ MyAppWidget().updateIf(context) { state ->
+ state == State.Completed
+ }
+ // [END android_compose_glance_manageupdate04]
+ }
+
+ // [START android_compose_glance_manageupdate05]
+ class DataSyncWorker(
+ val context: Context,
+ val params: WorkerParameters,
+ ) : CoroutineWorker(context, params) {
+
+ override suspend fun doWork(): Result {
+ // Fetch data or do some work and then update all instance of your widget
+ MyAppWidget().updateAll(context)
+ return Result.success()
+ }
+ }
+// [END android_compose_glance_manageupdate05]
+}
+
+object BuildUIWithGlance {
+
+ @Composable
+ fun example1() {
+ // [START android_compose_glance_buildUI01]
+ Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) {
+ val modifier = GlanceModifier.defaultWeight()
+ Text("first", modifier)
+ Text("second", modifier)
+ Text("third", modifier)
+ }
+ // [END android_compose_glance_buildUI01]
+ }
+
+ @Composable
+ fun example2() {
+
+ // [START android_compose_glance_buildUI02]
+ // Remember to import Glance Composables
+ // import androidx.glance.appwidget.layout.LazyColumn
+
+ LazyColumn {
+ items(10) { index: Int ->
+ Text(
+ text = "Item $index",
+ modifier = GlanceModifier.fillMaxWidth()
+ )
+ }
+ }
+ // [END android_compose_glance_buildUI02]
+ }
+
+ @Composable
+ fun example3() {
+ // [START android_compose_glance_buildUI03]
+ LazyColumn {
+ item {
+ Text("First Item")
+ }
+ item {
+ Text("Second Item")
+ }
+ }
+ // [END android_compose_glance_buildUI03]
+ }
+
+ @Composable
+ fun example4() {
+ val peopleNameList = arrayListOf()
+ val peopleList = arrayListOf()
+
+ // [START android_compose_glance_buildUI04]
+ LazyColumn {
+ items(peopleNameList) { name ->
+ Text(name)
+ }
+ }
+ // [END android_compose_glance_buildUI04]
+
+ // [START android_compose_glance_buildUI05]
+ LazyColumn {
+ item {
+ Text("Names:")
+ }
+ items(peopleNameList) { name ->
+ Text(name)
+ }
+
+ // or in case you need the index:
+ itemsIndexed(peopleNameList) { index, person ->
+ Text("$person at index $index")
+ }
+ }
+ // [END android_compose_glance_buildUI05]
+
+ LazyColumn {
+ // [START android_compose_glance_buildUI06]
+ items(items = peopleList, key = { person -> person.id }) { person ->
+ Text(person.name)
+ }
+ // [END android_compose_glance_buildUI06]
+ }
+ }
+}
+
+object SizeModeSnippets {
+
+ // [START android_compose_glance_buildUI07]
+ class MyAppWidget : GlanceAppWidget() {
+
+ override val sizeMode = SizeMode.Single
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ // ...
+
+ provideContent {
+ MyContent()
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+ // Size will be the minimum size or resizable
+ // size defined in the App Widget metadata
+ val size = LocalSize.current
+ // ...
+ }
+ }
+ // [END android_compose_glance_buildUI07]
+}
+
+object SizeModeSnippets2 {
+ // [START android_compose_glance_buildUI08]
+ class MyAppWidget : GlanceAppWidget() {
+
+ companion object {
+ private val SMALL_SQUARE = DpSize(100.dp, 100.dp)
+ private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp)
+ private val BIG_SQUARE = DpSize(250.dp, 250.dp)
+ }
+
+ override val sizeMode = SizeMode.Responsive(
+ setOf(
+ SMALL_SQUARE,
+ HORIZONTAL_RECTANGLE,
+ BIG_SQUARE
+ )
+ )
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ // ...
+
+ provideContent {
+ MyContent()
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+ // Size will be one of the sizes defined above.
+ val size = LocalSize.current
+ Column {
+ if (size.height >= BIG_SQUARE.height) {
+ Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
+ }
+ Row(horizontalAlignment = Alignment.CenterHorizontally) {
+ Button()
+ Button()
+ if (size.width >= HORIZONTAL_RECTANGLE.width) {
+ Button("School")
+ }
+ }
+ if (size.height >= BIG_SQUARE.height) {
+ Text(text = "provided by X")
+ }
+ }
+ }
+ }
+
+ // [END android_compose_glance_buildUI08]
+}
+
+object SizeModeSnippets3 {
+ // [START android_compose_glance_buildUI09]
+
+ class MyAppWidget : GlanceAppWidget() {
+
+ override val sizeMode = SizeMode.Exact
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ // ...
+
+ provideContent {
+ MyContent()
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+ // Size will be the max available size for the AppWidget
+ val size = LocalSize.current
+ Column {
+ Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
+ Row(horizontalAlignment = Alignment.CenterHorizontally) {
+ Button()
+ Button()
+ if (size.width > 250.dp) {
+ Button("School")
+ }
+ }
+ }
+ }
+ }
+ // [END android_compose_glance_buildUI09]
+}
+
+object AccessResources {
+ @Composable
+ fun example1() {
+ // [START android_compose_glance_buildUI10]
+ LocalContext.current.getString(R.string.glance_title)
+ // [END android_compose_glance_buildUI10]
+
+ // [START android_compose_glance_buildUI11]
+ Column(
+ modifier = GlanceModifier.background(R.color.default_widget_background)
+ ) { /**...*/ }
+
+ Image(
+ provider = ImageProvider(R.drawable.ic_logo),
+ contentDescription = "My image",
+ )
+ // [END android_compose_glance_buildUI11]
+ }
+}
+
+object CompoundButton {
+ @Composable
+ fun example1() {
+ // [START android_compose_glance_buildUI12]
+ var isApplesChecked by remember { mutableStateOf(false) }
+ var isEnabledSwitched by remember { mutableStateOf(false) }
+ var isRadioChecked by remember { mutableStateOf(0) }
+
+ CheckBox(
+ checked = isApplesChecked,
+ onCheckedChange = { isApplesChecked = !isApplesChecked },
+ text = "Apples"
+ )
+
+ Switch(
+ checked = isEnabledSwitched,
+ onCheckedChange = { isEnabledSwitched = !isEnabledSwitched },
+ text = "Enabled"
+ )
+
+ RadioButton(
+ checked = isRadioChecked == 1,
+ onClick = { isRadioChecked = 1 },
+ text = "Checked"
+ )
+ // [END android_compose_glance_buildUI12]
+ }
+ // [START android_compose_glance_buildUI13]
+ class MyAppWidget : GlanceAppWidget() {
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ val myRepository = MyRepository.getInstance()
+
+ provideContent {
+ val scope = rememberCoroutineScope()
+
+ val saveApple: (Boolean) -> Unit =
+ { scope.launch { myRepository.saveApple(it) } }
+ MyContent(saveApple)
+ }
+ }
+
+ @Composable
+ private fun MyContent(saveApple: (Boolean) -> Unit) {
+
+ var isAppleChecked by remember { mutableStateOf(false) }
+
+ Button(
+ text = "Save",
+ onClick = { saveApple(isAppleChecked) }
+ )
+ }
+ }
+ // [END android_compose_glance_buildUI13]
+
+ @Composable
+ fun example3() {
+ val colorAccentDay = Color.Blue
+ val colorAccentNight = Color.Blue
+ var isChecked by remember { mutableStateOf(false) }
+
+ // [START android_compose_glance_buildUI14]
+ CheckBox(
+ // ...
+ colors = CheckboxDefaults.colors(
+ checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight),
+ uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray)
+ ),
+ checked = isChecked,
+ onCheckedChange = { isChecked = !isChecked }
+ )
+
+ Switch(
+ // ...
+ colors = SwitchDefaults.colors(
+ checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan),
+ uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta),
+ checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow),
+ uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green)
+ ),
+ checked = isChecked,
+ onCheckedChange = { isChecked = !isChecked },
+ text = "Enabled"
+ )
+
+ RadioButton(
+ // ...
+ colors = RadioButtonDefaults.colors(
+ checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow),
+ uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue)
+ ),
+
+ )
+ // [END android_compose_glance_buildUI14]
+ }
+
+ private fun RadioButton(colors: Any) {
+ }
+}
+
+object GlanceTheming {
+
+ class ExampleAppWidget : GlanceAppWidget() {
+ // [START android_compose_glance_glancetheming01]
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+
+ provideContent {
+ GlanceTheme {
+ MyContent()
+ }
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+
+ Image(
+ colorFilter = ColorFilter.tint(GlanceTheme.colors.secondary),
+ // [START_EXCLUDE]
+ contentDescription = "Example Image",
+ provider = ImageProvider(R.drawable.ic_logo)
+ // [END_EXCLUDE]
+
+ )
+ }
+ // [END android_compose_glance_glancetheming01]
+ }
+
+ class ExampleAppWidget2 : GlanceAppWidget() {
+
+ // [START android_compose_glance_glancetheming06]
+ // Remember, use the Glance imports
+ // import androidx.glance.material3.ColorProviders
+
+ // Example Imports from your own app
+ // import com.example.myapp.ui.theme.DarkColors
+ // import com.example.myapp.ui.theme.LightColors
+
+ object MyAppWidgetGlanceColorScheme {
+
+ val colors = ColorProviders(
+ light = LightColors,
+ dark = DarkColors
+ )
+ }
+ // [END android_compose_glance_glancetheming06]
+ // [START android_compose_glance_glancetheming02]
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ // ...
+
+ provideContent {
+ GlanceTheme(colors = MyAppWidgetGlanceColorScheme.colors) {
+ MyContent()
+ }
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+
+ Image(
+ colorFilter = ColorFilter.tint(GlanceTheme.colors.secondary),
+ // [START_EXCLUDE]
+ provider = ImageProvider(R.drawable.ic_logo),
+ contentDescription = "Example"
+ // [END_EXCLUDE]
+ )
+ }
+ // [END android_compose_glance_glancetheming02]
+ }
+
+ class ExampleAppWidget3 : GlanceAppWidget() {
+
+ object MyAppWidgetGlanceColorScheme {
+ val colors = ColorProviders(
+ light = LightColors,
+ dark = DarkColors
+ )
+ }
+
+ // [START android_compose_glance_glancetheming03]
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+
+ provideContent {
+ GlanceTheme(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+ GlanceTheme.colors
+ else
+ MyAppWidgetGlanceColorScheme.colors
+ ) {
+ MyContent()
+ }
+ }
+ }
+
+ @Composable
+ private fun MyContent() {
+ // ...
+ Image(
+ colorFilter = ColorFilter.tint(GlanceTheme.colors.secondary),
+ // [START_EXCLUDE]
+ provider = ImageProvider(R.drawable.ic_logo),
+ contentDescription = "Example"
+ // [END_EXCLUDE]
+ )
+ }
+ // [END android_compose_glance_glancetheming03]
+ }
+
+ @Composable
+ fun shapeExample() {
+ // Note : android_compose_glance_glancetheming04 is found in button_outline.xml
+ // [START android_compose_glance_glancetheming05]
+ GlanceModifier.background(
+ imageProvider = ImageProvider(R.drawable.button_outline)
+ )
+ // [END android_compose_glance_glancetheming05]
+ }
+}
+
+object GlanceInteroperability {
+ @Composable
+ fun example01() {
+ // [START android_compose_glance_glanceinteroperability01]
+ val packageName = LocalContext.current.packageName
+ Column(modifier = GlanceModifier.fillMaxSize()) {
+ Text("Isn't that cool?")
+ AndroidRemoteViews(RemoteViews(packageName, R.layout.example_layout))
+ }
+ // [END android_compose_glance_glanceinteroperability01]
+ }
+
+ @Composable
+ fun example02() {
+ val packageName = null
+
+ // [START android_compose_glance_glanceinteroperability02]
+
+ AndroidRemoteViews(
+ remoteViews = RemoteViews(packageName, R.layout.my_container_view),
+ containerViewId = R.id.example_view
+ ) {
+ Column(modifier = GlanceModifier.fillMaxSize()) {
+ Text("My title")
+ Text("Maybe a long content...")
+ }
+ }
+ // [END android_compose_glance_glanceinteroperability02]
+ }
+}
+
+/**
+ * Dummy interface
+ */
+interface MyRepository {
+
+ suspend fun saveApple(a: Any)
+
+ companion object {
+ fun getInstance(): MyRepository {
+ TODO("Example")
+ }
+ }
+}
+
+/**dummy function*/
+private fun RowScope.Button(s: String = "") {
+ TODO("Not yet implemented")
+}
+
+/** dummy class */
+data class Person(val id: String, val name: String)
+
+/** dummy class */
+class GlanceSizeModeWidget : GlanceAppWidget() {
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ TODO("Not yet implemented")
+ }
+}
+
+/** dummy class */
+class MyAppWidget : GlanceAppWidget() {
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ TODO("Not yet implemented")
+ }
+}
+
+/**
+ * Dummy Interface
+ */
+sealed interface State {
+
+ object Loading : State
+ object Error : State
+ object Completed : State
+}
+
+/**
+ * Dummy class
+ */
+class DestinationsRepository {
+
+ lateinit var destinations: StateFlow
+
+ companion object {
+ fun getInstance(): DestinationsRepository {
+ TODO("Not yet implemented")
+ }
+ }
+}
+
+/**
+ * Dummy activity for snippet
+ */
+class NavigationActivity : AppCompatActivity() {
+ companion object {
+ val KEY_DESTINATION: String = "destination"
+ }
+}
+
+/**
+ * Dummy lambda
+ */
+private fun submitData() {
+ TODO("Not yet implemented")
+}
+
+/**
+ * Dummy broadcast receiver for snippets
+ */
+class MyReceiver : BroadcastReceiver() {
+ override fun onReceive(p0: Context?, p1: Intent?) {
+ TODO("Not yet implemented")
+ }
+}
+
+/**
+ * Dummy service for snippets
+ */
+class SyncService : Service() {
+ override fun onBind(p0: Intent?): IBinder? {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/compose/snippets/src/main/res/drawable/button_outline.xml b/compose/snippets/src/main/res/drawable/button_outline.xml
new file mode 100644
index 00000000..f4eb0229
--- /dev/null
+++ b/compose/snippets/src/main/res/drawable/button_outline.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/compose/snippets/src/main/res/layout/my_container_view.xml b/compose/snippets/src/main/res/layout/my_container_view.xml
new file mode 100644
index 00000000..d829e291
--- /dev/null
+++ b/compose/snippets/src/main/res/layout/my_container_view.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/compose/snippets/src/main/res/values-es/strings.xml b/compose/snippets/src/main/res/values-es/strings.xml
index e75f5ffa..0a8ab24b 100644
--- a/compose/snippets/src/main/res/values-es/strings.xml
+++ b/compose/snippets/src/main/res/values-es/strings.xml
@@ -42,4 +42,5 @@
- %1$d minutes
Hola mundo
+ Example String for Glance
\ No newline at end of file
diff --git a/compose/snippets/src/main/res/values/colors.xml b/compose/snippets/src/main/res/values/colors.xml
index 67b5e9ab..0205675f 100644
--- a/compose/snippets/src/main/res/values/colors.xml
+++ b/compose/snippets/src/main/res/values/colors.xml
@@ -23,4 +23,6 @@
#FF018786
#FF000000
#FFFFFFFF
+ #FFF
+ #FFF
\ No newline at end of file
diff --git a/compose/snippets/src/main/res/values/strings.xml b/compose/snippets/src/main/res/values/strings.xml
index 79a9875b..42772c35 100644
--- a/compose/snippets/src/main/res/values/strings.xml
+++ b/compose/snippets/src/main/res/values/strings.xml
@@ -42,4 +42,5 @@
- %1$d minutes
Hello world
+ Example String for Glance
\ No newline at end of file
diff --git a/compose/snippets/src/main/res/xml/my_app_widget_info.xml b/compose/snippets/src/main/res/xml/my_app_widget_info.xml
new file mode 100644
index 00000000..a1a7f5da
--- /dev/null
+++ b/compose/snippets/src/main/res/xml/my_app_widget_info.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b1e858e7..875425fb 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -17,6 +17,7 @@ androidx-coordinator-layout = "1.2.0"
androidx-corektx = "1.9.0"
androidx-emoji2-views = "1.4.0-beta05"
androidx-fragment-ktx = "1.6.0"
+androidx-glance-appwidget = "1.0.0-rc01"
androidx-lifecycle-compose = "2.6.1"
androidx-lifecycle-runtime-compose = "2.6.1"
androidx-navigation = "2.6.0"
@@ -30,6 +31,7 @@ androidx-test-ext-truth = "1.5.0"
androidx-window = "1.2.0-alpha02"
androidxHiltNavigationCompose = "1.0.0"
androix-test-uiautomator = "2.2.0"
+
coil = "2.4.0"
# @keep
compileSdk = "34"
@@ -100,6 +102,8 @@ androidx-coordinator-layout = { module = "androidx.coordinatorlayout:coordinator
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-corektx" }
androidx-emoji2-views = { module = "androidx.emoji2:emoji2-views", version.ref = "androidx-emoji2-views" }
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidx-fragment-ktx" }
+androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "androidx-glance-appwidget" }
+androidx-glance-material3 = { module = "androidx.glance:glance-material3", version.ref = "androidx-glance-appwidget" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-compose" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle-compose" }
@@ -143,6 +147,7 @@ okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
rometools-modules = { module = "com.rometools:rome-modules", version.ref = "rome" }
rometools-rome = { module = "com.rometools:rome", version.ref = "rome" }
+androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version = "2.8.1" }
[plugins]
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }