A modern approach to building android app widgets.
Before diving into the topic, let’s quickly take a history lesson to see why this Glance is an important and long-awaited feature.
Android app widgets were released as part of Android 1.5 (Cupcake).
Code inception in 2009.
In 2012 support added to display widgets on the lock screen, and provided third party apps to act as widget host (custom launcher application) and etc.
Over the period of time android system grew and received a lot of updates on the other part of the system but got very few updates over the widget framework.
Android 12 introduces Glance framework to create app widgets using jetpack-compose (declarative style) and much more additional updates to the widget framework itself.
What’s new in Android 12
Jetpack Glance for app widgets
Declarative API to build app widget UI.
Stateful widgets.
New clean api to handle user interaction.
Custom error UI (from XML).
Other updates on the widget framework
Scalable widget preview
Better theming support
New compound buttons
New API’s added to allow runtime modification of RemoteViews
Jetpack Glance for app widgets
What is Jetpack Glance
Jetpack Glance is a new framework built on top of the Jetpack Compose runtime.
Glance offers similar modern declarative Kotlin APIs that come with Jetpack Compose which helps to build responsive app widgets.
As it builds on top of Jetpack Compose runtime it’s not directly interoperable with other existing Jetpack Compose UI elements but interoperable with existing RemoteViews.
Glance provides a base set of composables to help build “glanceable” experiences.
Using the Jetpack Compose runtime Glance can translate composables into actual RemoteViews and display them in an app widget.
Glance provides a more intuitive API to handle user interactions. It abstracts away the complexities we would encounter while using RemoteViews and PendingIntent. It provides the following predefined actions for handling user interactions.
1) actionRunCallback
2) actionStartActivity
3) actionStartService
4) actionSendBroadcastInbuilt support for different size UI by defining SizeMode.Single, SizeMode.Exact or SizeMode.Responsive.
State management using GlanceStateDefinition.
LocalContext
,LocalState
,LocalGlanceId
,LocalSize
.LocalAppWidgetOptions
are provided to thecontent()
method usingCompositionLocalProvider
.
Even though the Glance framework seems like a fundamental change to the app widget. The creation of the app widget is pretty much the same and the only change is in how we represent the UI and maintain the state.
Create GlanceAppWidget
Step 0: Add dependency
dependencies {
// For AppWidgets support
implementation "androidx.glance:glance-appwidget:1.0.0-alpha04"
// For Wear-Tiles support
implementation "androidx.glance:glance-wear-tiles:1.0.0-alpha04"
}
android {
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.1.0-beta03"
}
kotlinOptions {
jvmTarget = "1.8"
}
}
Step 1: Create GlanceAppWidget
package com.gandiva.glance.resizeable.widget
import androidx.compose.runtime.Composable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.text.Text
class SimpleGlanceAppWidget : GlanceAppWidget() {
@Composable
override fun Content() {
Text(text = "Hello!")
}
}
Step 2: Attach with GlanceAppWidgetReceiver
package com.gandiva.glance.resizeable.widget
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
class SimpleGlanceAppWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget
get() = SimpleGlanceAppWidget()
}
Step 3: Add widget meta-data
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/app_widget_description"
android:initialLayout="@layout/simple_widget"
android:previewLayout="@layout/simple_widget"
android:minWidth="250dp"
android:minHeight="110dp"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:previewImage="@drawable/appwidget_preview"
android:maxResizeWidth="1050dp"
android:maxResizeHeight="787dp"
android:targetCellWidth="5"
android:targetCellHeight="3"
/>
Final step: Register component in AndroidManifest.xml
<receiver
android:name=".resizeable.widget.SimpleGlanceAppWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/simple_glance_widget_info" />
</receiver>
Stateful widget
Each GlanceAppWidget
maintains its own preference to store data as key-value pair using Datastore preference.
To read the widget state use currentState
method from local composition (i.e LocalState.current
). currentState
has two overloaded methods:
currentState
with no argument returns a Preferences.
currentState
with key as argument(i.e currentState(key)
) returns a value associated with the key.
To update data into the preference we can use updateAppWidgetState()
Once the preference is updated using
updateAppWidgetState()
we have to manually callGlanceAppWidget.update()
method to recompose the UI with latest data.
Let’s look at the below code to see how we are updating the boolean preference value to toggle the widget theme.
Stateful widget code
Stateful widget demo
User interaction
As we discussed earlier Glance provides more intuitive APIs to handle user interaction and actionRunCallback
is one of them.
actionRunCallback
method returns an Action that can be attached to the onClick method of a button or any other Glance component withonClick
attribute.actionRunCallback
method takesActionCallback
class as a type parameter and optionalActionParameters
as method argument.
Let’s look at the below code whenever the button is clicked an instance of ToastActionCallback
will be created and onRun
method will be called on that with context
, glanceId
and the parameters we passed (i.e ActionParameters
).
PS: glanceId
is a unique id assigned to each GlanceAppWidget.
Handler user interaction with actionRunCallback (with action parameters)
Just like actionRunCallback
we have other API to start a service (i.e actionStartService
), start an activity (i.e actionStartActivity
) or send a broadcast (i.e actionSendBroadcast
) also.
Different size
SizeMode.Single
When the widget size mode is SizeMode.Single
then the GlanceAppWidget
provides a single UI. The width and height of the app widget will be the minimum width and height given in app widget info.
SizeMode.Exact
When the widget size mode is SizeMode.Exact
then the GlanceAppWidget provides a UI for each size the App Widget can be. (i.e any one of the possible size from supported grid. click here for more detail).
SizeMode.Responsive
When the widget size mode is SizeMode.Responsive then the GlanceAppWidget provides a UI for a fixed set of sizes.
SizeMode.Responsive takes a set of fixed sizes from it's constructor.
Responsive size mode widget demo
Other updates on the widget framework
Apart from the Glance framework the existing widget framework also got some really good updates. lets see some of those.
Scalable widget preview
In Android 11 or below
android:previewImage
is used to show how the widget would look likeSince it’s an image, every time we update the widget design we have to change the image as well which would require some design effort.
Starting from Android 12 the widget preview can be derived from an XML layout, which results in better accuracy in reflecting how the actual widget will look like
<appwidget-provider
...
android:description="@string/app_widget_description"
android:targetCellWidth="3"
android:targetCellHeight="3"
android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>
Ref::https://developer.android.com/develop/ui/views/appwidgets/enhance
Other updates to widget framework
Take a look here to enhance your app-widgets and explore more updates on the widget framework starting from Android 12.
Repo with all the code samples is available here:
https://github.com/sridhar-sp/android-playground/tree/main/
An important thing to notice here is that even though this change seems like. a breaking change none of the existing android widget frameworks was modified this entire Glance framework works on top of the existing android widget framework.
Top comments (0)