GlanceAppWidget

abstract class GlanceAppWidget

Known direct subclasses
GlanceTemplateAppWidget

A GlanceAppWidget that provides template local values.


Object handling the composition and the communication with AppWidgetManager.

The UI is defined by calling provideContent from within provideGlance. When the widget is requested, the composition is run and translated into a RemoteViews which is then sent to the AppWidgetManager.

Summary

Public constructors

GlanceAppWidget(errorUiLayout: @LayoutRes Int)

Public functions

open Unit
onCompositionError(
    context: Context,
    glanceId: GlanceId,
    appWidgetId: Int,
    throwable: Throwable
)

A callback invoked when the AppWidgetSession encounters an exception.

open suspend Unit
onDelete(context: Context, glanceId: GlanceId)

Method called by the framework when an App Widget has been removed from its host.

abstract suspend Unit
provideGlance(context: Context, id: GlanceId)

Override this function to provide the Glance Composable.

suspend Unit
update(context: Context, id: GlanceId)

Run the composition in provideGlance and send the result to AppWidgetManager.

Public properties

open SizeMode

Defines the handling of sizes.

open GlanceStateDefinition<*>?

Data store for widget data specific to the view.

Extension functions

suspend RemoteViews
GlanceAppWidget.compose(
    context: Context,
    id: GlanceId,
    options: Bundle?,
    size: DpSize?,
    state: Any?
)

Creates a snapshot of the GlanceAppWidget content without running recomposition.

Flow<RemoteViews>
@ExperimentalGlanceApi
GlanceAppWidget.runComposition(
    context: Context,
    id: GlanceId,
    options: Bundle,
    sizes: List<DpSize>?,
    state: Any?
)

Returns a Flow that, on collection, starts a composition session for this GlanceAppWidget and emits RemoteViews for each result.

suspend Nothing

Provides content to the Glance host, suspending until the Glance session is shut down.

suspend Unit

Update all App Widgets managed by the GlanceAppWidget class.

suspend inline Unit
<State : Any?> GlanceAppWidget.updateIf(
    context: Context,
    predicate: (State) -> Boolean
)

Update all App Widgets managed by the GlanceAppWidget class, if they fulfill some condition.

suspend T
<T : Any?> GlanceAppWidget.getAppWidgetState(
    context: Context,
    glanceId: GlanceId
)

Get the state of an App Widget.

Public constructors

GlanceAppWidget

Added in 1.0.0
GlanceAppWidget(
    errorUiLayout: @LayoutRes Int = R.layout.glance_error_layout
)
Parameters
errorUiLayout: @LayoutRes Int = R.layout.glance_error_layout

Used by onCompositionError. When onCompositionError is called, it will, unless overridden, update the appwidget to display error UI using this layout resource ID, unless errorUiLayout is 0, in which case the error will be rethrown. If onCompositionError is overridden, errorUiLayout will not be read..

Public functions

onCompositionError

Added in 1.1.1
open fun onCompositionError(
    context: Context,
    glanceId: GlanceId,
    appWidgetId: Int,
    throwable: Throwable
): Unit

A callback invoked when the AppWidgetSession encounters an exception. At this point, the session will be closed down. The default implementation of this method creates a RemoteViews from errorUiLayout and sets this as the widget's content.

This method should be overridden if you want to log the error, create a custom error layout, or attempt to recover from or ignore the error by updating the widget's view state and then restarting composition.

Parameters
context: Context

Context.

glanceId: GlanceId

The GlanceId of the widget experiencing the error.

appWidgetId: Int

The appWidgetId of the widget experiencing the error. This is provided as a convenience in addition to GlanceId.

throwable: Throwable

The exception that was caught by AppWidgetSession

onDelete

Added in 1.0.0
open suspend fun onDelete(context: Context, glanceId: GlanceId): Unit

Method called by the framework when an App Widget has been removed from its host.

When the method returns, the state associated with the glanceId will be deleted.

provideGlance

Added in 1.0.0
abstract suspend fun provideGlance(context: Context, id: GlanceId): Unit

Override this function to provide the Glance Composable.

This is a good place to load any data needed to render the Composable. Use provideContent to provide the Composable once the data is ready.

provideGlance is run in the background as a androidx.work.CoroutineWorker in response to calls to update and updateAll, as well as requests from the Launcher. Before provideContent is called, provideGlance is subject to the typical androidx.work.WorkManager time limit (currently ten minutes). After provideContent is called, the composition continues to run and recompose for about 45 seconds. When UI interactions or update requests are received, additional time is added to process these requests.

Note: update and updateAll do not restart provideGlance if it is already running. As a result, you should load initial data before calling provideContent, and then observe your sources of data within the composition (e.g. androidx.compose.runtime.collectAsState). This ensures that your widget will continue to update while the composition is active. When you update your data source from elsewhere in the app, make sure to call update in case a Worker for this widget is not currently running.

import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.action.clickable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.appwidget.updateAll
import androidx.glance.text.Text

class MyWidget : GlanceAppWidget() {

    val Context.myWidgetStore by preferencesDataStore("MyWidget")
    val Name = stringPreferencesKey("name")

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Load initial data needed to render the AppWidget here. Prefer doing heavy work before
        // provideContent, as the provideGlance function will timeout shortly after
        // provideContent is called.
        val store = context.myWidgetStore
        val initial = store.data.first()

        provideContent {
            // Observe your sources of data, and declare your @Composable layout.
            val data by store.data.collectAsState(initial)
            val scope = rememberCoroutineScope()
            Text(
                text = "Hello ${data[Name]}",
                modifier = GlanceModifier.clickable("changeName") {
                    scope.launch {
                        store.updateData {
                            it.toMutablePreferences().apply { set(Name, "Changed") }
                        }
                    }
                }
            )
        }
    }

    // Updating the widget from elsewhere in the app:
    suspend fun changeWidgetName(context: Context, newName: String) {
        context.myWidgetStore.updateData {
            it.toMutablePreferences().apply { set(Name, newName) }
        }
        // Call update/updateAll in case a Worker for the widget is not currently running. This
        // is not necessary when updating data from inside of the composition using lambdas,
        // since a Worker will be started to run lambda actions.
        MyWidget().updateAll(context)
    }
}
import androidx.compose.runtime.collectAsState
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.glance.GlanceId
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.appwidget.updateAll
import androidx.glance.text.Text
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters

class WeatherWidget : GlanceAppWidget() {

    val Context.weatherWidgetStore by preferencesDataStore("WeatherWidget")
    val CurrentDegrees = intPreferencesKey("currentDegrees")

    suspend fun DataStore<Preferences>.loadWeather() {
        updateData { prefs ->
            prefs.toMutablePreferences().apply {
                this[CurrentDegrees] = Random.Default.nextInt()
            }
        }
    }

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        coroutineScope {
            val store = context.weatherWidgetStore
            val currentDegrees = store.data
                .map { prefs -> prefs[CurrentDegrees] }
                .stateIn(this@coroutineScope)

            // Load the current weather if there is not a current value present.
            if (currentDegrees.value == null) store.loadWeather()

            // Create unique periodic work to keep this widget updated at a regular interval.
            WorkManager.getInstance(context).enqueueUniquePeriodicWork(
                "weatherWidgetWorker",
                ExistingPeriodicWorkPolicy.KEEP,
                PeriodicWorkRequest.Builder(
                    WeatherWidgetWorker::class.java,
                    15.minutes.toJavaDuration()
                ).setInitialDelay(15.minutes.toJavaDuration()).build()
            )

            // Note: you can also set `android:updatePeriodMillis` to control how often the
            // launcher requests an update, but this does not support periods less than
            // 30 minutes.

            provideContent {
                val degrees by currentDegrees.collectAsState()
                Text("Current weather: $degrees °F")
            }
        }
    }
}

class WeatherWidgetWorker(
    appContext: Context,
    params: WorkerParameters
) : CoroutineWorker(appContext, params) {
    override suspend fun doWork(): Result {
        WeatherWidget().apply {
            applicationContext.weatherWidgetStore.loadWeather()
            // Call update/updateAll in case a Worker for the widget is not currently running.
            updateAll(applicationContext)
        }
        return Result.success()
    }
}

update

Added in 1.0.0
suspend fun update(context: Context, id: GlanceId): Unit

Run the composition in provideGlance and send the result to AppWidgetManager.

Public properties

sizeMode

Added in 1.0.0
open val sizeModeSizeMode

Defines the handling of sizes.

stateDefinition

Added in 1.0.0
open val stateDefinitionGlanceStateDefinition<*>?

Data store for widget data specific to the view.

Extension functions

suspend fun GlanceAppWidget.compose(
    context: Context,
    id: GlanceId = createFakeAppWidgetId(),
    options: Bundle? = null,
    size: DpSize? = null,
    state: Any? = null
): RemoteViews

Creates a snapshot of the GlanceAppWidget content without running recomposition.

This runs the composition one time and translates it to RemoteViews.

If a valid id is provided, this function will use the sizing values from the bound widget if using SizeMode.Exact or SizeMode.Single.

@ExperimentalGlanceApi
fun GlanceAppWidget.runComposition(
    context: Context,
    id: GlanceId = createFakeAppWidgetId(),
    options: Bundle = Bundle(),
    sizes: List<DpSize>? = null,
    state: Any? = null
): Flow<RemoteViews>

Returns a Flow that, on collection, starts a composition session for this GlanceAppWidget and emits RemoteViews for each result. The composition is closed when the flow is cancelled.

If a valid id is provided, this function will use the sizing values from the bound widget if using SizeMode.Exact or SizeMode.Single.

Lambda actions and list views in the emitted RemoteViews will continue to work while this is flow is running. This currently does not support resizing (you have to run the flow again with new sizes) or reloading the androidx.glance.state.GlanceStateDefinition state value.

suspend fun GlanceAppWidget.provideContent(
    content: @Composable @GlanceComposable () -> Unit
): Nothing

Provides content to the Glance host, suspending until the Glance session is shut down.

If this function is called concurrently with itself, the previous call will throw CancellationException and the new content will replace it. This function should only be called from GlanceAppWidget.provideGlance.

TODO: make this a protected member once b/206013293 is fixed.

suspend fun GlanceAppWidget.updateAll(context: Context): Unit

Update all App Widgets managed by the GlanceAppWidget class.

suspend inline fun <State : Any?> GlanceAppWidget.updateIf(
    context: Context,
    predicate: (State) -> Boolean
): Unit

Update all App Widgets managed by the GlanceAppWidget class, if they fulfill some condition.

getAppWidgetState

suspend fun <T : Any?> GlanceAppWidget.getAppWidgetState(
    context: Context,
    glanceId: GlanceId
): T

Get the state of an App Widget.