Last December, Google announced the alpha version of Jetpack Glance library for making App Widgets. This new tools allow us to use Jetpack Compose like style, making it faster and easier to create expressive and responsible widgets for our application.
In this article, I will share some key information about this amazing new API as well as some insights and points of attention during its usage. Please keep in mind that at the time this article was written, the latest version is 1.0.0-alpha01, so it is likely that some fixes and improvements mentioned here may be already implemented by Google at the time of your reading.
How does it work?
Glance provides its own set of Composables to be used in your GlanceAppWidget
and coverts it automatically into RemoteViews
in order to render the app widgets using the existing App Widgets APIs.
After an initial setup similar to the default widgets implementation, all we have to do is implement our own GlanceAppWidget
and GlanceAppWidgetReceiver
. Here is an example of a “Hello World” widget implemented in Compose:
class HelloWorldWidget: GlanceAppWidget() { | |
@Composable | |
override fun Content() { | |
Text(text = "Hello world!") | |
} | |
} | |
class HelloWorldWidgetReceiver : GlanceAppWidgetReceiver() { | |
override val glanceAppWidget = HelloWorldWidget() | |
} |
It uses its own Compose implementation
As mentioned in the previous section, Glance uses its own set of Composables meaning that they are not interoperable with the default Jetpack Compose libraries. This is required because since Glance needs to convert Composables to RemoteViews
, not every component supports this transformation.
When implementing your App Widget with Glance, pay attention to the imports. We need to implement Column
, Row
, Image
etc from androidx.glance.*
rather than androidx.compose.*
. If the Composables used are not the ones from Glance API, the following error will be shown when the widget is added in the screen:
Glance App Widget Error example
And a similar stack trace on Logcat:
E/GlanceAppWidget: Error in Glance App Widget java.lang.IllegalStateException: CompositionLocal LocalDensity not present at androidx.compose.ui.platform.CompositionLocalsKt.noLocalProvidedFor(CompositionLocals.kt:170)
In this alpha version some of the Composable and Modifiers options are not fully available, which is expected from an early version. So, keep it in mind before starting implementing it.
Updating is different from Jetpack Compose
Another point that worth mentioning when using this library is that the recomposition process is a little different from the traditional Jetpack Compose. In Glance’s Compose it is not possible, for example, to collect a Flow
of states and make it recompose after each change.
This happens because the existing update mechanisms in Jetpack Compose relies on the process staying alive in between updates. But for the App Widgets API, the resulting composition (RemoteView
) is sent to the launcher process instead of keep running on our own application process. Allowing recompose widgets similar to Jetpack Compose would consume a lot of battery.
At the moment, the better way to keep the data updated is to manually trigger the App Widget update when the data is updated in the app. For example, if you have a list of tasks to be shown in the widget, when the user adds new one then the widget update function is called as well.
For more information on this particular behavior, I recommend this insightful Issue Tracker discussion, as well as this pull request in the amazing project PeopleInSpace which has a great implementation to handle this app widget limitation.
Job Offers
User interactions are easier to handle
Another great addition to this new library is making user interactions easier to handle. There is no more need to use PendingIntent
to handle calls in a different process from your app. Now, with Glance, you can provide an “action” and all the communication complexity is done automatically for us!
In the example below, we can set actionStartActivity
action to be open the HomeActivity
when the user clicks the “Home” button. Simple as that!
Button( | |
text = "Home", | |
modifier = GlanceModifier.clickable(actionStartActivity<HomeActivity>()) | |
) |
It is also possible to create custom actions to better fit your user experience needs:
Button( | |
text = "Update", | |
modifier = GlanceModifier.clickable(actionRunCallback<UpdateAction>()) | |
) | |
class UpdateAction : ActionCallback { | |
override suspend fun onRun(context: Context, glanceId: GlanceId, parameters: ActionParameters) { | |
// Update! | |
} | |
} |
It is even possible to pass parameters through your actions and Activities, start Services and BroadcastReceivers. For more information about how to handle user interactions, please access the official docs.
Make sure to test on Android 12 and below
Although this does not apply only when using Glance, it is a good idea to test your App Widgets in different Android versions, specially prior and after Android 12.
The latest Android version brought significant changes and improvements in the widget APIs which makes it behavior different in this versions. So, make sure that your layout, dimensions, previews and actions are working seamlessly in all versions!
What’s next?
Widgets are part of the Android ecosystem since the beginning but it didn’t receive much love during the expansion of the OS. In the recent years, Google is releasing a lot of new documentation, support and exciting features to design beautiful and responsive widgets across multiple screens and formats. Personally, I’m very excited in the direction that this library is heading, even in a early stage it is possible to see the dedication behind it.
As usual, I would love to share some code from Alkaa with a more complex approach in a real-world scenario. This pull request implements a very simple widget to show the latest user tasks.
Alkaa Task List Widget
I also want to share the official video from Android Dev Summit 2021, “Modern Android app widgets” which focus both in the great new inclusions in Android 12 as well as some information about the Glance API.
If you have any feedback regarding the library, feel free to share with Google. They are always ready to listen and support us based on our opinions and findings about their tools. 😊
A huge thanks to Marcel Pintó for reviewing this article.
Thanks a lot for reading my article! ❤️
Thanks to Marcel Pintó and Bruno Kenji Tiba.