Carry a widget's revision into the multi-zone path on Android

The widget-refresh work covered the fullscreen path only, so editing a widget placed in a ZONE still
never reached the screen. Two independent gaps, both of which had to close:

- The zone render URL was built from the widget id alone, with no rev, so even a forced re-render
  fetched a URL the WebView had already seen.
- The decision to re-render zones at all keys on an assignment signature of
  content_id:zone_id:widget_id. A widget's identity does not change when it is edited, so the
  signature was byte-identical and the branch fell through to "Multi-zone unchanged, skipping".

A zone holding a single widget never rotates either, so nothing else would have reloaded it. The
customer edited a widget, the dashboard showed the new content, and that region of the screen kept
the old version until the layout geometry changed or the app was force-stopped.

The server has supplied widget_rev on every assignment since the fullscreen fix; both the fullscreen
Android path and the web player's zone path already used it. This is the path that was missed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
This commit is contained in:
Claude 2026-07-30 21:04:10 -05:00
parent acadb4c1f4
commit d580994bb3
2 changed files with 9 additions and 2 deletions

View file

@ -591,9 +591,12 @@ class MainActivity : AppCompatActivity() {
val currentLayoutId = zoneManager?.currentLayoutId
// Build a signature of current assignments to detect content changes
// widget_rev belongs in here for the same reason it is in the fullscreen playlist
// signature: editing a widget changes its CONTENT, never its id, so without it a
// zone assignment looked identical and the re-render was skipped as "unchanged".
val assignmentSig = (0 until assignments.length()).map { i ->
val a = assignments.getJSONObject(i)
"${a.optString("content_id")}:${a.optString("zone_id")}:${a.optString("widget_id")}"
"${a.optString("content_id")}:${a.optString("zone_id")}:${a.optString("widget_id")}:${a.optLong("widget_rev", 0L)}"
}.sorted().joinToString("|")
val changed = assignmentSig != zoneManager?.lastAssignmentSig

View file

@ -210,8 +210,12 @@ class ZoneManager(
widgetType != null -> {
val widgetId = a.optString("widget_id", "")
val webView = createWebView()
// rev, exactly as the fullscreen path does: a widget's id does not change when it
// is edited, so without it a zone kept rendering the old content indefinitely.
val wRev = a.optLong("widget_rev", 0L)
val wUrl = "$renderServerUrl/api/widgets/$widgetId/render" +
(if (renderDeviceId.isNotEmpty()) "?device=" + android.net.Uri.encode(renderDeviceId) else "")
(if (renderDeviceId.isNotEmpty()) "?device=" + android.net.Uri.encode(renderDeviceId) else "?d=") +
"&rev=" + wRev
webView.loadUrl(wUrl)
webView.layoutParams = params
container.addView(webView); zoneViews[zone.id] = webView