From d580994bb378dcc94e3d4cc7149d4d03eca2cc2c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 21:04:10 -0500 Subject: [PATCH] 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) Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL --- .../src/main/java/com/remotedisplay/player/MainActivity.kt | 5 ++++- .../java/com/remotedisplay/player/player/ZoneManager.kt | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/java/com/remotedisplay/player/MainActivity.kt b/android/app/src/main/java/com/remotedisplay/player/MainActivity.kt index 88f5b88..e4bacdf 100644 --- a/android/app/src/main/java/com/remotedisplay/player/MainActivity.kt +++ b/android/app/src/main/java/com/remotedisplay/player/MainActivity.kt @@ -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 diff --git a/android/app/src/main/java/com/remotedisplay/player/player/ZoneManager.kt b/android/app/src/main/java/com/remotedisplay/player/player/ZoneManager.kt index b2a8a96..a1c5e05 100644 --- a/android/app/src/main/java/com/remotedisplay/player/player/ZoneManager.kt +++ b/android/app/src/main/java/com/remotedisplay/player/player/ZoneManager.kt @@ -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