Generated banner for Glance Appwidget Updates And Memory
Generated logo for Glance Appwidget Updates And Memory

Glance Appwidget Updates And Memory

Android (Google)OfficialCreated by Google LLCDistributed by Android (Google)

  • Skill
  • Tool integration
  • Software development
  • Apache-2.0
  • Low risk
  • Has scripts

From the author

Manages Android AppWidget state updates, background syncs, and RemoteViews bitmap memory limits with Jetpack Glance (androidx.glance:glance-appwidget) and AppWidgetProvider receivers on Android 17 (API 37). Use this skill when configuring Android 17 (API level 37) compileSdk/targetSdk and AndroidManifest.xml widget receivers, refreshing frozen or stale Glance widget UI after CoroutineWorker DataStore preference updates (updateAppWidgetState), downsampling images within the WindowMetrics memory limit for SizeMode.Responsive, SizeMode.Exact, or LazyColumn, sharing content:// URIs with FileProvider, or fixing ActionCallback NoSuchMethodException and AppWidgetProvider ForegroundServiceStartNotAllowedException crashes with WorkManager. Don't use for Glance layout templates (suggest the glance-canonical-layouts skill) or in-app Compose screens.
— the description by Google LLC

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a comprehensive workflow for managing Android AppWidget state and memory, specifically addressing the strict bitmap memory limits introduced in Android 17 (API 37). It guides developers through configuring Jetpack Glance and classic AppWidgetProvider implementations to ensure stability, proper image downsampling, and efficient background synchronization. The skill covers memory-efficient bitmap handling, FileProvider integration for content URIs, and robust state management using DataStore and WorkManager to prevent common crashes and UI staleness.

What it does

  • Configures Android 17 (API 37) SDK levels and manifest requirements for widgets.
  • Implements bitmap downsampling to stay within strict RemoteViews memory limits.
  • Provides patterns for deduplicating shared bitmaps across responsive size buckets.
  • Guides the use of FileProvider for sharing content URIs securely with launchers.
  • Ensures widget UI refreshes after DataStore state mutations.
  • Fixes common crashes like NoSuchMethodException and ForegroundServiceStartNotAllowedException.

When to use it

  • When targeting Android 17 (API 37) and encountering RemoteViews memory limit exceptions.
  • When developing or maintaining Jetpack Glance widgets that display images.
  • When migrating classic AppWidgetProvider code to handle background updates safely.
  • When debugging frozen or stale widget UI after state updates.
  • When implementing responsive widget layouts that require efficient memory management.

Advantages

  • Prevents IllegalArgumentException crashes related to widget bitmap memory limits.
  • Improves widget performance by optimizing image decoding and deduplication.
  • Ensures reliable background updates without violating Android service restrictions.
  • Provides clear patterns for modern Glance and legacy AppWidget interoperability.

Usage examples

“How do I fix the RemoteViews bitmap memory limit exception on Android 17?”
The agent provides steps to calculate the memory cap using WindowMetrics and implement downsampling helpers to ensure images fit within the system-enforced budget.
“My Glance widget UI is not updating after I change DataStore preferences.”
The agent explains how to trigger an explicit update using GlanceAppWidget.update() or updateAll() after calling updateAppWidgetState.
“How can I safely update images in a classic AppWidgetProvider without causing crashes?”
The agent guides you to use AppWidgetManager.updateAppWidget instead of partiallyUpdateAppWidget and shows how to schedule background work correctly.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/android/glance-appwidget-updates-and-memory/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip glance-appwidget-updates-and-memory.zip -d .agents/skills

Documentation by Google LLC

This skill provides guidance for managing state updates and keeping Android AppWidgets that use Jetpack Glance (androidx.glance:glance-appwidget) or RemoteViews within the Android 17 widget bitmap memory limits. For widget UI layout templates, suggest the glance-canonical-layouts skill.

Prerequisites

  • Kotlin Android project (minSdk >= 26) with compileSdk = 37 (targetSdk = 37 when targeting Android 17) in build.gradle.kts.

  • Jetpack Glance (1.2.0, minimum 1.1.1+) and the Compose compiler Gradle plugin enabled on the module.

Critical mandates

  • Preserve existing package name and toolchain versions : Inspect build.gradle.kts, gradle/libs.versions.toml, and Kotlin files first. Keep existing namespace, applicationId, package, targetSdk (unless migrating to Android 17), Android Gradle Plugin (AGP), and Kotlin versions.

  • Downsample images to widget dimensions and estimate the memory cap : Android system_server computes the RemoteViews bitmap memory cap (6 * width * height bytes, or 1.5 * 4 * width * height) once at boot from the real size of the default display and does not expose this cap in a public API. Because unfolding a foldable or switching display resolution to QHD+ after boot can make active window metrics report a larger value than the boot-time cap, always downsample each image first to the widget's target dimensions (maxWidthDp x maxHeightDp in RGB_565 or ALPHA_8), and enforce the per-image byte ceiling (calculateWidgetMemoryCapBytes(context) / distinctImageCount) estimated as 6 * width * height from WindowManager.currentWindowMetrics.bounds on API 30+ (falling back to resources.displayMetrics on API 26-29 or if WindowManager is unavailable).

  • Decode shared bitmaps once outside provideContent : Both SizeMode.Exact (used by glance-canonical-layouts to compose the current span's portrait and landscape orientations) and SizeMode.Responsive (used to pre-bundle a set of DpSize breakpoints) invoke provideContent multiple times per update. Decode shared bitmaps once in provideGlance before provideContent and pass them to ImageProvider(bitmap) so RemoteViews deduplicates the shared Bitmap across all sizes in either mode. If a widget scales distinct bitmaps per LocalSize.current, divide calculateWidgetMemoryCapBytes(context) by the number of distinct bitmaps.

  • Downsample content URIs and grant launcher read permissions : On Android 17 (targetSdk >= 37), bitmap-backed Icons count toward the cap alongside Bitmaps. content:// URIs passed to ImageProvider(uri) or RemoteViews.setImageViewUri don't count toward the inter-process communication (IPC) cap, but the launcher decodes them at full resolution. Downsample the file into context.cacheDir, expose it with FileProvider.getUriForFile, and grant Intent.FLAG_GRANT_READ_URI_PERMISSION to Intent.CATEGORY_HOME packages.

  • Trigger explicit widget updates after state mutations : updateAppWidgetState writes preferences to DataStore without updating the widget UI. After you mutate state, call GlanceAppWidget.update(context, glanceId) or GlanceAppWidget.updateAll(context).

  • Use full updateAppWidget calls when you replace images : In AppWidgetProvider classes, when you replace bitmaps or Icons, call AppWidgetManager.updateAppWidget instead of partiallyUpdateAppWidget.

Reference implementation files

Read the companion reference files in references/ when implementing each step, and update the sample package declaration and class names to match your app:

Detailed steps

Step 1: Configure SDK levels, dependencies, and manifest declarations
  1. Read gradle/libs.versions.toml, app/build.gradle.kts, and app/src/main/AndroidManifest.xml first. If AGP, Kotlin, or library versions are already present, keep them; otherwise, add glance = "1.2.0" along with androidx.glance:glance-appwidget and androidx.glance:glance-material3 to gradle/libs.versions.toml.

  2. In app/build.gradle.kts, keep namespace, applicationId, and any existing minSdk >= 26, targetSdk, or library versions. Set compileSdk = 37 (and targetSdk = 37 only when explicitly migrating the app to target Android 17), enable buildFeatures { compose = true }, and add implementation(libs.androidx.glance.appwidget) and implementation(libs.androidx.glance.material3).

  3. In AndroidManifest.xml (see AndroidManifest.xml), declare <queries> with <action android:name="android.intent.action.MAIN" /> and <category android:name="android.intent.category.HOME" /> so queryIntentActivities discovers launcher packages on API 30+. Register your GlanceAppWidgetReceiver (pointing android.appwidget.provider to @xml/sample_widget_info, see sample_widget_info.xml) and register androidx.core.content.FileProvider with android:authorities="${applicationId}.fileprovider", android:exported="false", android:grantUriPermissions="true", and android.support.FILE_PROVIDER_PATHS pointing to @xml/file_paths (which declares <cache-path name="widget_cache" path="widget_images/" />, see file_paths.xml):

<br />

<receiver android:name=".glance.MyReceiver"
    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/my_app_widget_info" />
</receiver>

<br />

Step 2: Estimate the widget memory cap and downsample images

Read WidgetBitmapUtils.kt and implement the memory cap and downsampling helpers:

  1. Estimate the cap (calculateWidgetMemoryCapBytes) : On API 30+ (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R), read val bounds = context.getSystemService(WindowManager::class.java)?.currentWindowMetrics?.bounds and, when bounds != null && bounds.width() > 0 && bounds.height() > 0, return 6L * bounds.width().toLong() * bounds.height().toLong() (calling WindowManager from a non-UI widget or worker Context logs a nonfatal StrictMode violation on API 30+, which is expected for this estimate). On API 26-29 (or if WindowManager is unavailable), fall back to context.resources.displayMetrics (6L * dm.widthPixels.toLong() * dm.heightPixels.toLong()). Because system_server caches 6 * width * height once at boot, always size images to the widget's maxWidthDp x maxHeightDp first (Stage 1) rather than filling the full screen budget.

  2. Downsample in two stages (decodeDownsampledWidgetBitmap) :

    • First, probe dimensions with BitmapFactory.Options().apply { inJustDecodeBounds = true; inPreferredConfig = config } and double sampleSize *= 2 while (bounds.outWidth / (sampleSize * 2) >= targetWidthPx && bounds.outHeight / (sampleSize * 2) >= targetHeightPx) || (bounds.outWidth / sampleSize > targetWidthPx * 2) || (bounds.outHeight / sampleSize > targetHeightPx * 2) (where targetWidthPx = max(1, (maxWidthDp * density).toInt())), so wide banners or tall screenshots do not bypass downsampling when one axis already fits.

    • Second, enforce the per-image byte ceiling (perImageCapBytes = calculateWidgetMemoryCapBytes(context) / max(1, distinctImageCount)) by doubling sampleSize *= 2 while (max(1, bounds.outWidth / sampleSize).toLong() * max(1, bounds.outHeight / sampleSize).toLong() * bytesPerPixel) > perImageCapBytes. Use 2L bytes per pixel for Bitmap.Config.RGB_565 (opaque photos), 1L for Bitmap.Config.ALPHA_8 masks, and 4L when bounds.outConfig != null && bounds.outConfig != config (because BitmapFactory upgrades translucent PNG or WebP files from RGB_565 to 4-byte ARGB_8888). Decode with inSampleSize = sampleSize and inPreferredConfig = config.

  3. Choose between in-memory Bitmap and FileProvider Uri (cacheWidgetImageUri) : Use decodeDownsampledWidgetBitmap with androidx.glance.ImageProvider(bitmap) when passing bitmaps directly in memory. Use cacheWidgetImageUri when the widget passes a content:// Uri to ImageProvider(uri) or RemoteViews.setImageViewUri: it writes the downsampled bitmap into File(context.cacheDir, "widget_images"), obtains a content:// URI with FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", outFile), queries Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME), and calls context.grantUriPermission(pkg, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION).

Step 3: Deduplicate multi-size bitmaps and handle composition errors

Read SampleGlanceWidget.kt to configure multi-size bitmap deduplication and composition error recovery:

  1. Choose SizeMode for your layout : Use SizeMode.Exact (recommended when using glance-canonical-layouts to render exact LocalSize.current dimensions for the current grid span's portrait and landscape orientations), SizeMode.Responsive(RESPONSIVE_BUCKETS) (using import androidx.compose.ui.unit.DpSize and import androidx.compose.ui.unit.dp to pre-bundle fixed DpSize breakpoints such as setOf(DpSize(120.dp, 120.dp), DpSize(240.dp, 120.dp), DpSize(240.dp, 240.dp)) so resize transitions don't wake the app process), or SizeMode.Single (for a single layout).

  2. Hoist shared bitmap decodes before provideContent : Because both SizeMode.Exact (2-4 orientation sizes) and SizeMode.Responsive (N buckets) run provideContent multiple times per update, decode shared bitmaps once in provideGlance before provideContent with decodeDownsampledWidgetBitmap and Bitmap.Config.RGB_565 for opaque images (distinctImageCount = 1). Pass the resulting Bitmap to androidx.glance.ImageProvider(sharedBitmap) so RemoteViews deduplicates the shared Bitmap across all sizes.

  3. Provide a zero-bitmap error fallback : Override onCompositionError(context, glanceId, appWidgetId, throwable) and call AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, fallback) with a text-only RemoteViews layout (0 bitmaps).

Step 4: Pre-decode LazyColumn collection images and assign stable itemIds

Read FeedCollectionGlanceWidget.kt when rendering collection widgets with LazyColumn:

  1. Replace import androidx.glance.appwidget.ImageProvider (which accepts Uri) with import androidx.glance.ImageProvider (which accepts Bitmap). Remove Uri.fromFile and Icon.createWithContentUri calls.

  2. While a Glance session is active (~45 seconds after an update), .update() and .updateAll() do not restart provideGlance. Declare var cachedRevision = -1 and var cachedThumbnails = emptyMap<String, Bitmap>() in provideGlance before provideContent. Inside provideContent, read val revision = currentState<Preferences>()[KEY_REVISION] ?: 0, and only when revision != cachedRevision, update cachedRevision = revision and decode the feedItems thumbnails with cachedThumbnails = feedItems.mapNotNull { item -> decodeDownsampledWidgetBitmap(..., distinctImageCount = max(1, feedItems.size), config = Bitmap.Config.RGB_565)?.let { item.id to it } }.toMap() (use mapNotNull rather than associate so the map stays Map<String, Bitmap> instead of Map<String, Bitmap?>). This re-decodes when DataStore state updates during an active session while sharing one decoded Map<String, Bitmap> across all SizeMode.Responsive buckets.

  3. In LazyColumn, pass a stable Long itemId to items(items = feedItems, itemId = { item -> item.id.hashCode().toLong() }) (preferring a persistent numeric ID when available) and render Image(provider = ImageProvider(bitmap), contentDescription = item.title) only when bitmap != null (never fall back to Uri.fromFile in an else branch).

Step 5: Push state updates, configure ActionCallbacks, and use WorkManager

Read WidgetStateAndActions.kt, proguard-rules.pro, and ClassicWidgetImageUpdater.kt to implement these patterns:

  1. Push DataStore updates (requires androidx.datastore:datastore-preferences-core:1.2.1 and import androidx.glance.appwidget.GlanceAppWidgetManager, import androidx.glance.appwidget.state.updateAppWidgetState, and import androidx.glance.appwidget.updateAll): In pushWidgetRevisionUpdate(context) (see WidgetStateAndActions.kt), iterate over for (glanceId in GlanceAppWidgetManager(context).getGlanceIds(Widget::class.java)), mutate state inside updateAppWidgetState(context, glanceId) { prefs -> ... } (passing both context and glanceId), and immediately call widget.update(context, glanceId) (or widget.updateAll(context)).

  2. Use zero-argument ActionCallback classes with ActionParameters.Key and keep <init>() under R8 (requires androidx.work:work-runtime-ktx:2.12.0 when scheduling background work): RunCallbackAction instantiates ActionCallback classes with Class.getDeclaredConstructor().newInstance(), so a constructor parameter fails in every build. Remove constructor parameters from ActionCallback implementations, pass values with ActionParameters.Key and actionParametersOf (see RefreshWidgetAction in WidgetStateAndActions.kt), and add -keep class * implements androidx.glance.appwidget.action.ActionCallback { public <init>(); } to proguard-rules.pro (see proguard-rules.pro). Inside SyncWidgetWorker.doWork(), call pushWidgetRevisionUpdate(applicationContext) so background work updates DataStore and refreshes the widget.

  3. Avoid unconditional one-time WorkManager enqueues in AppWidgetProvider.onUpdate and use full updateAppWidget for images (see ClassicWidgetImageUpdater.kt): Never call context.startForegroundService from AppWidgetProvider.onReceive or onUpdate (which throws ForegroundServiceStartNotAllowedException). GlanceAppWidgetReceiver already keeps a 10-year delayed session worker queued internally so WorkManager's RescheduleReceiver never toggles. In a classic AppWidgetProvider, avoid enqueuing a OneTimeWorkRequest directly inside onUpdate: even with ExistingWorkPolicy.KEEP, once the one-time worker finishes (SUCCEEDED), UnfinishedWorkListener disables RescheduleReceiver, which broadcasts ACTION_PACKAGE_CHANGED and re-triggers onUpdate in a loop. Instead, schedule repeating syncs in onUpdate with enqueueUniquePeriodicWork(..., ExistingPeriodicWorkPolicy.KEEP, ...) (which stays ENQUEUED across runs), use goAsync() for short < 10s updates, or enqueue one-time work (enqueueUniqueWork(..., ExistingWorkPolicy.KEEP, ...)) only from specific action broadcasts in onReceive. When you update a Bitmap or Icon on a classic RemoteViews widget, call appWidgetManager.updateAppWidget rather than partiallyUpdateAppWidget.

Common errors and fixes

Symptom or exceptionRoot causeFix
IllegalArgumentException: RemoteViews for widget update exceeds maximum bitmap memory usageRemoteViews bitmap or icon memory exceeded 6 * W * H bytes.Downsample to the widget's maxWidthDp x maxHeightDp with inSampleSize and RGB_565 (or ALPHA_8 for masks), enforce calculateWidgetMemoryCapBytes(context) / distinctImageCount, and decode shared bitmaps once before provideContent.
SecurityException: Permission Denial: opening provider or blank content:// widget imageHome launcher lacks read permission for the FileProvider content:// URI or app passed file:// with Uri.fromFile.Expose the file with FileProvider, add <queries> for CATEGORY_HOME, and call context.grantUriPermission with Intent.FLAG_GRANT_READ_URI_PERMISSION.
Widget UI stays frozen after updateAppWidgetStateupdateAppWidgetState writes to DataStore without recomposing, or provideGlance was not re-triggered during an active session.Call .update(context, glanceId) or .updateAll(context) after updateAppWidgetState, and observe revision state inside provideContent.
NoSuchMethodException: <Class>.<init> [] on action tapActionCallback declared constructor parameters or R8 stripped <init>().Use a public zero-argument constructor, pass values with ActionParameters.Key, and keep <init>() in ProGuard.
ForegroundServiceStartNotAllowedException in receiverAppWidgetProvider called startForegroundService in the background.Schedule periodic syncs in onUpdate with enqueueUniquePeriodicWork (ExistingPeriodicWorkPolicy.KEEP) or enqueue one-time work (enqueueUniqueWork) from custom action broadcasts in onReceive.
Unresolved reference: ImageProvider, DpSize, updateAppWidgetState, or updateAllMissing or mismatched Glance and Compose package imports, or calling updateAppWidgetState(context) without glanceId.Import androidx.glance.ImageProvider (for Bitmap), androidx.compose.ui.unit.DpSize and androidx.compose.ui.unit.dp (for SizeMode.Responsive), androidx.glance.appwidget.state.updateAppWidgetState (passing both context and glanceId), and androidx.glance.appwidget.updateAll.

Antipatterns

  • NEVER decode or scale a Bitmap inside provideContent per size bucket with SizeMode.Responsive or SizeMode.Exact.

  • NEVER size widget bitmaps to fill the full screen memory cap (6 * width * height); always downsample to the widget's target maxWidthDp x maxHeightDp first so unfolding a foldable or switching display resolution after boot does not exceed the boot-time cap.

  • NEVER pass an unscaled content:// Uri or Uri.fromFile to ImageProvider(uri) or RemoteViews.setImageViewUri, or omit context.grantUriPermission for Intent.CATEGORY_HOME.

  • NEVER call updateAppWidgetState without calling GlanceAppWidget.update(context, glanceId) or GlanceAppWidget.updateAll(context).

  • NEVER call partiallyUpdateAppWidget when you replace widget images.

Files

references/android/develop/ui/views/appwidgets/index.md

Try the Compose way Jetpack Compose is the recommended UI toolkit for Android. Learn how to build widgets using Compose-style APIs. Jetpack Glance → Image: untitled

<br />

App widgets are miniature app views that you can embed in other apps---such as the home screen---and receive periodic updates. These views are referred to as widgets in the user interface, and you can publish one with an app widget provider (or widget provider ). An app component that holds other widgets is called an app widget host (or widget host). Figure 1 shows a sample music widget: Image: Example of music widget Figure 1. Example of a music widget.

This document describes how to publish a widget using a widget provider. For details about creating your own AppWidgetHost to host app widgets, see Build a widget host.

For information about how to design your widget, see App widgets overview.

Android skills

View on GitHub

Manage AppWidget updates and bitmap memory

Use the AppWidget Updates and Memory Android skill to downsample widget bitmaps, deduplicate responsive images, and stay within Android 17 RemoteViews memory limits across Glance and classic AppWidgetProvider widgets. To install the skill from the Android CLI, run:

android skills add glance-appwidget-updates-and-memory

<br />

Widget components

To create a widget, you need the following basic components:

AppWidgetProviderInfo object : Describes the metadata for a widget, such as the widget's layout, update frequency, and AppWidgetProvider class. AppWidgetProviderInfo is defined in XML, as described in this document.

AppWidgetProvider class : Defines the basic methods that let you programmatically interface with the widget. Through it, you receive broadcasts when the widget is updated, enabled, disabled, or deleted. You declare AppWidgetProvider in the manifest and then implement it, as described in this document.

View layout : Defines the initial layout for the widget. The layout is defined in XML, as described in this document.

Figure 2 shows how these components fit into the overall app widget processing flow. Image: App widget processing flow Figure 2. App widget processing flow.

[!NOTE] Note: Android Studio can automatically create a set of AppWidgetProviderInfo, AppWidgetProvider, and view layout files. Choose New > Widget > App Widget.

If your widget needs user configuration, implement the app widget configuration activity. This activity lets users modify widget settings---for example, the time zone for a clock widget.

We also recommend the following improvements: flexible widget layouts, miscellaneous enhancements, advanced widgets, collection widgets, and building a widget host.

Declare the AppWidgetProviderInfo XML

Defining the metadata settings (such as default cell sizes, resize constraints, and update frequencies) is exactly identical across both traditional Views and Glance-based widgets.

To learn how to define and configure your metadata XML file, see the Compose-first Declare the AppWidgetProviderInfo XML section in the Glance documentation.

Use the AppWidgetProvider class to handle widget broadcasts

The platform's broadcast receiver mechanics, manifest declaration filters, and lifecycle event loops are unified underneath the platform. In Compose-first development, these broadcasts are orchestrated using the GlanceAppWidgetReceiver wrapper.

To understand how to register your receiver in the manifest and implement Hilt-compatible lifecycle overrides, see the Compose-first Use the AppWidgetProvider class to handle broadcasts section in the Glance documentation.

Create the widget layout

You must define an initial layout for your widget in XML and save it in the project's res/layout/ directory. Refer to Design guidelines for details.

Creating the widget layout is straightforward if you're familiar with layouts. However, be aware that widget layouts are based on RemoteViews, which doesn't support every kind of layout or view widget. You can't use custom views or subclasses of the views that are supported by RemoteViews.

RemoteViews also supports ViewStub, which is an invisible, zero-sized View you can use to lazily inflate layout resources at runtime.

Support for stateful behavior

Android 12 adds support for stateful behavior using the following existing components:

The widget is still stateless. Your app must store the state and register for state change events. Image: Example of shopping list widget showing stateful behavior Figure 3. Example of stateful behavior.

[!NOTE] Note: Always explicitly set the current checked state using RemoteViews.setCompoundButtonChecked, or you might encounter unexpected results when your widget is dragged or resized.

The following code example shows how to implement these components.

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has 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)
)

<br />

Provide two layouts: one targeting devices running Android 12 or higher in res/layout-v31, and the other targeting previous Android 11 or lower in the default res/layout folder.

Implement rounded corners

Calculating the outer background and inner proportional radii is standard and shared. In Compose-first development, this can be set dynamically in Kotlin alongside custom theme resources.

To implement corner radii or set up dynamic styles for older Android devices, see the Compose-first Implement rounded corners section in the Glance documentation.

Keep RemoteViews images within system memory limits

Widgets run inside the home screen launcher process, and system_server enforces a per-widget bitmap memory ceiling (6 * width * height bytes; see the Android 17 widget memory limit behavior change). When building classic AppWidgetProvider widgets with RemoteViews, do the following:

  • Downsample bitmaps using WindowMetrics: On API level 30 and higher, compute your maximum bitmap budget from WindowManager.currentWindowMetrics.bounds rather than resources.displayMetrics, and decode opaque images with Bitmap.Config.RGB_565 and power-of-two inSampleSize.

  • Use full updateAppWidget calls when replacing images: Avoid calling partiallyUpdateAppWidget to update ImageView bitmaps or Icon URIs, because partial updates append actions to the cached RemoteViews hierarchy in system_server without releasing prior image allocations.

  • Enqueue background work with WorkManager: Don't call Context.startForegroundService from AppWidgetProvider.onReceive or onUpdate, which throws ForegroundServiceStartNotAllowedException when triggered in the background. Enqueue a WorkManager CoroutineWorker instead, and pass context.applicationContext when inflating or updating RemoteViews from background workers.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

What our scanner noticed· Low risk

  • Scripts that your agent may run

This skill includes scripts that your agent may run. Read them before you use it.

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Google LLC
Publisher
Android (Google) (Official)
Distributed by
Android (Google)
Source
android/skills · system/glance-appwidget-updates-and-memory · commit bf68e2d
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.txt)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Google LLC or Android (Google).

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download glance-appwidget-updates-and-memory.zip