Widgets in Android 12

Ken Baldauf

Senior Software Engineer @ Acorns

Android Widget Architecture

  • Consists of four components
    • XML layout
    • Configuration Activity (optional)
    • AppWidgetProvider
    • AppWidgetProviderInfo

Configuration Activity

// Find the widget id from the intent.
intent.extras?.let { extras ->
    appWidgetId = extras.getInt(
        AppWidgetManager.EXTRA_APPWIDGET_ID,
        AppWidgetManager.INVALID_APPWIDGET_ID
    )
}

// If this activity was started without an app widget ID, finish with an error.
if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
    setResult(RESULT_CANCELED)
    finish()
    return
}
// Configuration activity updates the app widget
val appWidgetManager = AppWidgetManager.getInstance(context)
updateAppWidget(context, appWidgetManager, appWidgetId)

// Make sure we pass back the original appWidgetId
val resultValue = Intent()
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
setResult(RESULT_OK, resultValue)
finish()

AppWidgetProvider

  • Extension of BroadcastReceiver
  • Widget lifecycle events
    • onEnabled
    • onDisabled
    • onDeleted
    • onUpdate

onUpdate

override fun onUpdate(
    context: Context,
    appWidgetManager: AppWidgetManager,
    appWidgetIds: IntArray
) {
    // There may be multiple widgets active, so update all of them
    for (appWidgetId in appWidgetIds) {
        updateAppWidget(context, appWidgetManager, appWidgetId)
    }
}

internal fun updateAppWidget(
    context: Context,
    appWidgetManager: AppWidgetManager,
    appWidgetId: Int
) {
    val widgetText = loadTitlePref(context, appWidgetId)
    // Construct the RemoteViews object
    val views = RemoteViews(context.packageName, R.layout.test_widget)
    views.setTextViewText(R.id.appwidget_text, widgetText)

    // Instruct the widget manager to update the widget
    appWidgetManager.updateAppWidget(appWidgetId, views)
}

RemoteViews

  • Parceable data structure
    • Capture an entire view structure
    • Encapsulate the application's permissions
  • Don't support all Android views and view methods
  • Drawn by homescreen or lockscreen that contains it

AppWidgetProviderInfo

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:configure="org.ocandroid.widgetplayground.widget.TestWidgetConfigureActivity"
    android:description="@string/app_widget_description"
    android:initialKeyguardLayout="@layout/test_widget"
    android:initialLayout="@layout/test_widget"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:previewImage="@drawable/example_appwidget_preview"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="86400000"
    android:widgetCategory="home_screen" />
  • Defines essential widget information

Changes in Android 12

  • Stylistic changes
  • Configuration improvements
  • RemoteViews expansion

Stylistic Changes

  • Rounded corners on devices running Android 12
    • Default radius may be dictated by device manufacturers or 3rd party launchers
    • R.dimen.system_app_widget_background_radius
      • Manually set radius of widget's rounded corners

Stylistic Changes

  • Widgets can use device theme colors in Android 12
    • @android:style/Theme.DeviceDefault.DayNight
  • Improved widget picker experience
    • Widget previews were a static drawable resource
    • In Android 12, previews can more accurately depict widget via a XML layout set to widget's default size.
    • Android 12 adds optional widget descriptions

Configuration Improvements

  • Adds the ability to
    • Reconfigure already placed widgets
    • Bypass configuration activity when adding new widget
      • Uses pre-defined default configuration
      • User can still reconfigure widget later
  • Improved sizing control
    • Define size in launcher grid cells instead of dp
    • Support for responsive layouts
      • Provide RemoteViews a mapping of size to layout
  • These options are only available in Android 12
    • Ignored on older OS versions.

RemoteViews Expansion

  • Adds support for stateful buttons
    • CheckBox, Switch & RadioButton
    • Widget is still stateless
    • App must store state & register for state change events
// Listen for check changes. The intent will have an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

RemoteViews Expansion

  • Simplified RemoteViews collections
    • Pass a collection directly to populate a ListView
    • Previously needed to define a RemoteViewsService & RemoteViewsFactory
  • Additional support for runtime modification of RemoteViews attributes
remoteView.setRemoteAdapter(
    R.id.list_view,
    RemoteViews.RemoteCollectionItems.Builder()
        .addItem(/* id= */ ID_1, 
            RemoteViews(R.layout.item_type_1))
        .addItem(/* id= */ ID_2,
            RemoteViews(R.layout.item_type_2))
        ...
        .setViewTypeCount(itemLayouts.count())
        .build()
)
// Set the colors of a progress bar.
remoteView.setColorStateList(
    R.id.progress,
    "setProgressTintList",
    createProgressColorStateList()
)

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(
    R.id.text,
    RemoteViews.MARGIN_END,
    8f, 
    TypedValue.COMPLEX_UNIT_DP
)

Widgets in Android 12

By Kenneth Baldauf

Widgets in Android 12

A look at the Widget API changes coming in Android 12

  • 283