Android Widget

Nur Özkaya
4 min readAug 24, 2022

App widgets are miniature application views that can be embedded in other applications (such as the home screen) and receive periodic updates.

Simply choose New > Widget > App Widget.

Android Studio automatically creates a set of AppWidgetProviderInfo, AppWidgetProvider, and view layout files.

Widget components

AppWidgetProviderInfo object
Describes the metadata for a widget, such as the widget’s layout, update frequency, and the AppWidgetProvider class.

AppWidgetProvider class
Defines the basic methods that allow you to programmatically interface with the widget. Through it, you will receive broadcasts when the widget is updated, enabled, disabled, or deleted.

View layout
Defines the initial layout for the widget.

Use the AppWidgetProvider class to handle widget broadcasts.

The AppWidgetProvider class handles widget broadcasts and updates the widget in response to widget lifecycle events. The following sections describe how to declare AppWidgetProvider in the manifest and then implement it.

<receiver android:name="ExampleAppWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/example_appwidget_info" />
</receiver>

Declare the AppWidgetProviderInfo XML
The AppWidgetProviderInfo defines the essential qualities of a widget. Define the AppWidgetProviderInfo object in an XML resource file using a single <appwidget-provider> element and save it in the project’s res/xml/ folder.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/example_loading_appwidget"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

Customizing the User Interface
In order to customize the UI for the Widget, app_widget.xml in the app\res\layout folder. The Android Studio wizard generated the following layout that you need to update:

Note: You can only use FrameLayout, LinearLayout, RelativeLayout and GridLayout. You cannot use ConstraintLayout for widget layout. You also don’t aloud to use some components like View, Space inside Widget layout.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#09C"
android:padding="@dimen/widget_margin">

<TextView
android:id="@+id/appwidget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_margin="8dp"
android:background="#09C"
android:contentDescription="@string/appwidget_text"
android:text="@string/appwidget_text"
android:textColor="#ffffff"
android:textSize="24sp"
android:textStyle="bold|italic" />

</RelativeLayout>

Refreshing the widget
The data the Widget displays must always be up to date without wasting system resources. This means that the UI should be updated only when the data changes, and this can happen for different reasons. If the user interacts with the Widget, you need a way to update the UI and then send the event to the main app. If something is happening in the main app, you need a way to tell the Widget to refresh.

The Android platform also provides a third way, an automatic refresh of the Widget at an interval that can be set using the configuration file. Performance limitations don’t allow an update frequency greater than 30 minutes.

How to send the “I need a refresh!” message to the Widget.

When the update frequency you need is longer than 30 minutes, you don’t need to write any code and you can simply rely on the configuration file app_widget_info.xml Android Studio generated in the res\xml folder.
The Widget refresh rate is the one defined in the attribute android:updatePeriodMillis. The default value is one day in milliseconds.

Update the widget manually
If your app needs to update the data in the Widget more frequently, you already have the solution: you can simply periodically launch the same Intent the Android system does.

Open MainActivity and add the following code:

// Send a broadcast so that the Operating system updates the widget
// 1
val man = AppWidgetManager.getInstance(this)
// 2
val ids = man.getAppWidgetIds(ComponentName(this,AppWidget::class.java))
// 3
val updateIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
// 4
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
// 5
sendBroadcast(updateIntent)

  1. Get the AppWidgetManager instance, which is responsible for all the installed Widgets.
  2. Ask for the identifiers of all the instances of your widget (you could add more than one to your homescreen).
  3. Create an Intent with the android.appwidget.action.APPWIDGET_UPDATE action asking for an update.
  4. Add the ids of the widgets you are sending the Intent to as extras of the Intent for the AppWidgetManager.EXTRA_APPWIDGET_IDS key.
  5. Finally, send the broadcast message.

Communicating via Service
Not all the updates needed for Widgets are a consequence of an action from the user. Typical cases are data from a server through periodic polling and push notification events. In cases like these, the request has to come from a different component, which you usually implement as an Android Service.

Choose File\New\Service\Service

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val appWidgetManager = AppWidgetManager.getInstance(this)
val allWidgetIds = intent?.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS)
//1
if (allWidgetIds != null) {
//2
for (appWidgetId in allWidgetIds) {
//3
AppWidget.updateAppWidget(this, appWidgetManager, appWidgetId)
}
}
return super.onStartCommand(intent, flags, startId)
}

  1. Check that the array of allWidgetIds was in the Intent.
  2. Loop through the allWidgetIds list.
  3. Update each widget.

Now, you need to call this service instead of directly updating the widget. In order to start the Service: (AppWidget, onUpdate())

val intent = Intent(context.applicationContext, WidgetQuotesService::class.java)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
context.startService(intent)

This creates an Intent, puts the Widget ids in the intent, and starts the Service.

private fun getRandomQuote(context: Context): String {
//1
val quotes = context.resources.getStringArray(R.array.widget_texts)
//2
val rand = Math.random() * quotes.size
//3
return quotes[rand.toInt()].toString()
}

This function generates a random quote:

  1. It takes a quote array from the strings file
  2. It picks a random number
  3. Finally, it returns the string at the random position

After you have the string, update the widget. In updateAppWidget() add this before the last call:

views.setTextViewText(R.id.widget_quote, getRandomQuote(context))

That’s it. Every time the widget updates, you get a new quote!

Some final advice before you start adventuring into the world of Widgets:

👾 Design the smallest Widget size you can. Be aware that the user might resize it into a bigger area.
👾 Don’t refresh the Widget too often because it will drain the battery. On the other hand, don’t refresh it too rarely because it won’t be useful on the screen.
👾 Think of Widgets as a shortcut window into your app. Provide the most important information and actions in it.

--

--