Skip to content
This repository was archived by the owner on Jul 11, 2025. It is now read-only.
Merged
425 changes: 55 additions & 370 deletions AppWidget/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ object GlanceTheme {
internal val LocalColorProviders = staticCompositionLocalOf { dynamicThemeColorProviders() }

/**
* Temporary implementation of Material3 theme for Glance. It still requires to manually set the
* colors to all Glance components
* Temporary implementation of Material3 theme for Glance.
*
* Note: This still requires manually setting the colors for all Glance components.
*/
@Composable
fun GlanceTheme(colors: ColorProviders = GlanceTheme.colors, content: @Composable () -> Unit) {
Expand All @@ -49,8 +50,7 @@ fun GlanceTheme(colors: ColorProviders = GlanceTheme.colors, content: @Composabl
}

/**
* Holds a set of Glance specific [ColorProvider] that can be used to represent a Material 3 color
* scheme.
* Holds a set of Glance-specific [ColorProvider] following Material naming conventions.
*/
data class ColorProviders(
val primary: ColorProvider,
Expand Down Expand Up @@ -86,9 +86,9 @@ data class ColorProviders(
)

/**
* Creates a set of color providers that represents a Material 3 style dynamic color theme. On
* devices that support it, this theme is derived from the user specific platform colors, on other
* devices this falls back to the Material baseline theme.
* Creates a set of color providers that represents a Material3 style dynamic color theme. On
* devices that support it, the theme is derived from the user specific platform colors, on other
* devices this falls back to the Material3 baseline theme.
*/
fun dynamicThemeColorProviders(): ColorProviders {
return ColorProviders(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class ImageGlanceWidget : GlanceAppWidget() {
}

/**
* Called when the widget instance is deleted. We can then cleanup any ongoing task.
* Called when the widget instance is deleted. We can then clean up any ongoing task.
*/
override suspend fun onDelete(context: Context, glanceId: GlanceId) {
super.onDelete(context, glanceId)
Expand All @@ -154,8 +154,9 @@ class ImageGlanceWidget : GlanceAppWidget() {
/**
* Get the bitmap from the cache file
*
* Note: since it's a single image resized to the available space we probably won't reach the
* memory limit, otherwise you need to generate a URI granting permissions to the launcher.
* Note: Because it's a single image resized to the available space, you
* probably won't reach the memory limit. If you do reach the memory limit,
* you'll need to generate a URI granting permissions to the launcher.
*
* More info:
* https://developer.android.com/training/secure-file-sharing/share-file#GrantPermissions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ class ImageWorker(
}

/**
* Using Coil and Picsum Photos to randomly load images into the cache based on the provided
* size. This method returns the path of the cached image so it can be send to the widget.
* Use Coil and Picsum Photos to randomly load images into the cache based on the provided
* size. This method returns the path of the cached image, which you can send to the widget.
*/
@OptIn(ExperimentalCoilApi::class)
private suspend fun getRandomImage(width: Float, height: Float, force: Boolean): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class WeatherGlanceWidget : GlanceAppWidget() {
private val largeMode = DpSize(260.dp, 280.dp)
}

// Override the state definition to use our custom one using Koltin serialization
// Override the state definition to use our custom one using Kotlin serialization
override val stateDefinition = WeatherInfoStateDefinition

// Define the supported sizes for this widget.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import android.content.Context
import androidx.glance.appwidget.GlanceAppWidgetReceiver

/**
* Handle system events for AppWidgets, providing the GlanceAppWidget instance.
* Handle system events for AppWidgets with the provided GlanceAppWidget instance.
*
* Use this class to handle widget lifecycle specific events like onEnable/Disable.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ object WeatherInfoStateDefinition : GlanceStateDefinition<WeatherInfo> {
private const val DATA_STORE_FILENAME = "weatherInfo"

/**
* Using the same file name regardless of the instance to share data between them
* Use the same file name regardless of the widget instance to share data between them
*
* If you need different state/data for each instance, create a store using the fileKey.
* If you need different state/data for each instance, create a store using the provided fileKey
*/
private val Context.datastore by dataStore(DATA_STORE_FILENAME, WeatherInfoSerializer)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ import kotlin.random.Random

object WeatherRepo {

// TODO get by place instead of hardcoded
/**
* Request the WeatherInfo of a given location
*/
suspend fun getWeatherInfo(): WeatherInfo {
// Simulate network loading
delay(Random.nextInt(1, 3) * 1000L)
return WeatherInfo.Available(
placeName = "Tokyo",
Expand All @@ -39,6 +42,9 @@ object WeatherRepo {
)
}

/**
* Fake the weather data
*/
private fun getRandomWeatherData(instant: Instant): WeatherData {
val dateTime = instant.atZone(ZoneId.systemDefault())
return WeatherData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,14 @@ class WeatherWorker(
/**
* Update the state of all widgets and then force update UI
*/
private suspend fun setWidgetState(glanceIds: List<GlanceId>, state: WeatherInfo) {
private suspend fun setWidgetState(glanceIds: List<GlanceId>, newState: WeatherInfo) {
glanceIds.forEach { glanceId ->
updateAppWidgetState(context, WeatherInfoStateDefinition, glanceId) {
state
}
updateAppWidgetState(
context = context,
definition = WeatherInfoStateDefinition,
glanceId = glanceId,
updateState = { newState }
)
}
WeatherGlanceWidget().updateAll(context)
}
Expand Down
8 changes: 1 addition & 7 deletions AppWidget/app/src/main/res/xml/app_widget_buttons_glance.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -14,11 +13,6 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<!--
Intentionally omitting adding android:previewLayout because it consists of ListView and doesn't
reflect the inflated contents of ListView in a widget preview.
Thus, only supplying a static image through android:previewImage
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/buttons_glance_widget_description"
android:initialLayout="@layout/widget_buttons"
Expand Down
Binary file added AppWidget/screenshots/image-gif.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added AppWidget/screenshots/run-widget-config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added AppWidget/screenshots/todo-gif.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added AppWidget/screenshots/weather-gif.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.