Skip to content

Releases: kmbrimble/wallpanel-android

0.12.0 Build 0-kmb.9

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 04:50

Handles MediaTek DuraSpeed blocking the WebView renderer.

DuraSpeed suppresses the sandboxed process the WebView needs for a renderer, leaving the app alive and focused on a permanently blank page. On this panel it reproduced every time after an adb shell am force-stop; a force-stop from Settings never triggered it (5/5 clean), so this mostly affects developers.

What changed

  • The app clears setting.duraspeed.enabled on startup when it holds WRITE_SECURE_SETTINGS (granted over adb; most installs will not have it, and the app never crashes without it). Measured on device: this cures an in-flight suppression, not just prevents future ones.
  • Settings > About reports whether the device ships DuraSpeed.
  • A blocked browser process is now explained, with the adb fix offered to copy, instead of a blank screen.

Post-install step

adb shell pm grant xyz.wallpanel.app.kmb android.permission.WRITE_SECURE_SETTINGS

The grant survives install -r and reboots; only an uninstall drops it.

Signed arm64-v8a split. See README, section "MediaTek DuraSpeed".

0.12.0 Build 0-kmb.8 — dagger.android -> Hilt migration; DuraSpeed renderer-wedge fix

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 03:21

Highlights

  • dagger.android -> Hilt migration, code-complete and verified on-device
    (all 13 injection entry points confirmed via dexdump, single-component
    collapse confirmed a semantic no-op ahead of time).
  • Root cause found for the SandboxedProcessService1 / renderer-bind wedge:
    MediaTek DuraSpeed, compiled into this tablet's system_server, was silently
    suppressing the WebView renderer's service spawn after a force-stop.
    Mitigated with settings put global setting.duraspeed.enabled 0 (does not
    survive reboot — a startup self-fix via WRITE_SECURE_SETTINGS is the next
    feature). Full ruled-out ledger (service-number correlation, dev-vs-prod,
    launch method, isolated-UID exhaustion, cache-clear theory) recorded in
    CLAUDE.md and CHANGELOG.md so none of it gets re-investigated.
  • Post-minSdk cleanup tail (kmb.7): dead SDK_INT>=O conditionals removed,
    legacy AndroidX libs and enableJetifier flagged for removal.

Promoted to the production tablet and confirmed rendering via
scripts/promote.sh.

v0.12.0-kmb.16

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 09:19

Wake/wifi/keyguard lock audit plus deprecation tidy.

Wifi lock is now functional. WIFI_MODE_FULL is documented by AOSP as "non-functional and will have no impact", and the tablet agreed — held as type=1 while the framework counted 0 full low latency acquired. Replaced with WIFI_MODE_FULL_LOW_LATENCY behind an SDK_INT >= 29 guard, the mode that actually disables wifi power save. Confirmed after promotion: type=4, 2 full low latency acquired, mPowerSaveDisableRequests 2.

Wake lock renamed, kept. partialWakeLock was never partial — it is FULL_WAKE_LOCK or ACQUIRE_CAUSES_WAKEUP. Now screenWakeLock. It is never held in steady state (FLAG_KEEP_SCREEN_ON does that job) but is the only thing that can turn a dark screen on, so it stays.

Keyguard disableKeyguard() kept. Redundant on a device with no lock screen, but the window flags it duplicates cover one activity only and setShowWhenLocked() needs API 27 against minSdk 26.

allowFileAccess = true removed — and it was not inert. targetSdk 34 means the platform default is false, so it was actively re-enabling filesystem access. The file:///android_asset error page is exempt and unaffected.

databaseEnabled removed (deprecated WebSQL, no-op) and a satisfied TODO comment deleted.

ABI splits dropped. No native libraries since kmb.11 made the four splits degenerate — five byte-identical ~9.2MB APKs. One universal APK now, same 9248461 bytes. Release artifacts are WallPanelApp-universal-<versionName>.apk; promote.sh's rollback glob was widened to WallPanelApp-*.apk so earlier -arm64- releases remain valid rollback targets.

Verified: smoke-device.sh PASS, panel-render-probe.sh RENDERING, promote.sh installed/relaunched/post-install-probed the universal APK RENDERING, and walk-up motion wake confirmed on the tablet.

v0.12.0-kmb.15 — remove allowFileAccessFromFileURLs and four dead WebSettings

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 08:58

Subtraction only. Five setters removed from BrowserActivityNative.configureWebSettings; nothing added, no behaviour change.

The security one — allowFileAccessFromFileURLs = true

Deprecated since API 30, still functional on the panel's API 31. It lets JavaScript running from a file:// origin read other file:// URLs; the default has been false since API 16.

It was inert here — but not for the reason a quick grep suggests. The app does load local content: InternalWebClient.onReceivedError loads file:///android_asset/error_page.html on any network error. That page contains zero <script> — a stylesheet <link>, an <img> and static text, all markup subresources that load regardless of this setting, which gates only JS-initiated cross-file:// reads. No JavaScript ever executes from a file:// origin anywhere in the app.

allowUniversalAccessFromFileURLs, the genuinely dangerous sibling, is not set anywhere in this codebase.

Added in 3866f04 (Aug 2018) alongside READ/WRITE_EXTERNAL_STORAGE for "access local files" — those permissions are long gone from the manifest, so the motivation died before the setting did.

Four dead setters

pluginState (NPAPI, gone from WebView), setRenderPriority (ignored by the Chromium renderer), saveFormData (no-op from API 26 on; minSdk is 26), databaseEnabled (WebSQL — dead for this app: Home Assistant uses IndexedDB/localStorage, not openDatabase()). Deprecation warnings in that file: 5 → 0.

javaScriptEnabled and domStorageEnabled are load-bearing and untouched, as is the val webSettings = webView.settings local-val pattern from the kmb.5 renderer-crash fix.

Verification

Beyond the usual bar, since this touches the function the kmb.5 fix lives in: build + unit tests + lintVital green, smoke-device.sh PASS, panel-render-probe.sh RENDERING, code-reviewer found no defects, and — attended — smoke-renderer-crash.sh PASS (app pid held, focus held, fresh renderer bound, dashboard rendering after the injected crash). Dashboard confirmed interactive by hand.

v0.12.0-kmb.14 — KSP stage 2: AGP built-in Kotlin, AGP 10 deadline closed

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 08:07

Replaces the classic Kotlin Gradle Plugin with AGP 9.4.0's built-in Kotlin and removes both android.newDsl=false and android.builtInKotlin=false. Those flags are deleted in AGP 10.0 (mid-2026), so this closes that deadline — nothing in the build now depends on anything AGP 10 removes. -Pandroid.debug.obsoleteApi=true reports zero legacy-variant-API warnings, down from three (all three came from kotlin-android itself).

The recorded blocker was wrong in both halves. Stage 2 was documented as blocked because AGP's built-in Kotlin supports neither KSP nor kotlin-parcelize, "and we use both". We never used both — a repo-wide scan found zero @Parcelize and zero Parcelable; the plugin was applied but had never generated anything. Dropping it also moots google/ksp#3053, the hazard most likely to sink this.

The real blocker was version-shaped. Checked against the plugin artifacts rather than the issue tracker: every 2.2.21-* KSP throws an unconditional RuntimeException ("KSP is not compatible with Android Gradle Plugin's built-in Kotlin"), and 2.2.21-2.0.5 was the last release on that line — so google/ksp#2615 was not resolved at the version we were pinned to. KSP 2.3.x versions independently of Kotlin and gates the same refusal on AGP < 9.0.0-alpha14. Bumped to 2.3.11.

Also removed: the two hand-passed Hilt ksp { arg(...) } values (the new DSL wires them again), the kotlin-gradle-plugin classpath entry, and ext.kotlin_version.

The Kotlin compiler is now AGP's bundled one — a future AGP bump is therefore also a Kotlin bump, moving under a KSP pin that no longer moves with it, with nothing failing at configuration time if they drift. Re-check KSP on every AGP bump.

kotlin { jvmToolchain(17) } went with KGP and was replaced, not dropped, by java { toolchain { languageVersion = 17 } }compileOptions/compilerOptions pin the bytecode level but say nothing about which JDK compiles. Caveat: 17-target bytecode (major version 61) is verified on a JDK 17 host only; the JDK 25 host path is untested.

Verified

Generated Hilt/KSP sources byte-identical to master across all 75 files in both build/generated/ksp/prodDebug/java and build/generated/hilt/component_sources/prodDebug — the compiler swap did not alter DI codegen. kspProdDebugKotlin genuinely runs rather than being skipped. Unit tests, lintVitalProdRelease and assembleProdRelease all pass. Release dex class set identical to master's (671 wallpanel/hilt/dagger descriptors).

Full per-screen DI walk across all 12 Hilt entry points (3 activities, the service, all 8 settings fragments) with no UninitializedPropertyAccessException. smoke-device.sh PASS, panel-render-probe.sh RENDERING, post-install probe RENDERING.

Two apparent regressions found during the walk were proved pre-existing by building master and running the identical check: the Motion/Face/QR rows ignoring taps (they are enabled="false" until Camera Enabled is on), and the release build emitting five identically-sized APKs. Benign packaging delta: kotlin-tooling-metadata.json is gone, a KGP-only artifact.

Known

There have been no native libraries in this APK since kmb.11, so the five ABI splits are degenerate — all ~9.2MB and identical. Splits are deliberately kept for now; disabling them and signing the universal is a standalone follow-up, not something to couple to a compiler change.

Install the attached arm64-v8a APK — signed with our own key.

v0.12.0-kmb.13 — AGP 9.4.0, Hilt 2.60.1

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 06:37

Routine version bumps, nothing else in the diff (three lines).

AGP 9.4.0 changes nothing for us. Both opt-out flags still warn as deprecated, still say "removed in version 10.0", default still true — wording identical to 9.3.2. 9.4 adds android.newDsl.optOut=:module for per-module Variant API exclusion; not suggested in our build output and no help to a single-module project. Recorded, not acted on.

The two hand-passed Hilt KSP args are now redundant under 2.60.1 — built with and without the ksp { arg(...) } block and diffed the generated sources: 77 files each, identical file set, identical md5 for every file. Deliberately NOT removed here: this release is scoped to bumps, and that change deserves its own verification.

Verified: 6 unit tests, smoke-device.sh PASS, panel-render-probe.sh RENDERING, post-install probe RENDERING, plus a DI spot-check across all three activities and the settings root fragment.

v0.12.0-kmb.12 — DefaultLifecycleObserver migration

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 06:25

All three @OnLifecycleEvent sites migrated to DefaultLifecycleObserver. The deprecated annotation resolved reflectively (no lifecycle-compiler on the processor path), which was a hidden blocker on ever enabling R8.

Behaviour verified before AND after with an identical on-device sequence — ON_START and DialogUtils ON_DESTROY fire in both; TTS ON_DESTROY fires in neither. Identical, so the migration is behaviour-preserving.

The pre-change check found more than the migration did. Two of the three callbacks never run in normal operation, for pre-existing structural reasons: BrowserActivityNative declares configChanges for everything and is never destroyed, and WallPanelService has no stopSelf() and never stops gracefully. Screensaver dialogs are cleared by the 9 direct clearDialogs() calls, not by the lifecycle observer — so no dialog leak occurs by that route.

Verified: 6 unit tests, smoke-device.sh PASS, panel-render-probe.sh RENDERING, post-install probe RENDERING.

v0.12.0-kmb.11 — five unused dependency stacks removed, APK down 67%

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 06:04

Removed ML Kit, Retrofit x3, OkHttp x2 (+Gson), rxandroid, lifecycle-reactivestreams-ktx, and the retrostreams/retrofuture backports. Every removal re-grepped across all source sets and then confirmed absent from the built dex.

arm64-v8a release split: 27,877,646 -> 9,243,267 bytes unsigned (-66.8%). 12,694,000 of that is ML Kit's two uncompressed native libraries. The long-standing Unable to strip build warning is gone.

RxJava was NOT removable — hivemq-mqtt-client depends on it. The direct declaration was redundant and pinned it down to 2.2.19; the transitive 2.2.21 now wins.

play-services-vision kept: it is the live detector stack, still 1525 dex references, and the panel reports "camera":true after promotion.

junit pinned to 4.13.2.

Verified: 6 unit tests, smoke-device.sh PASS, panel-render-probe.sh RENDERING, post-install probe RENDERING.

v0.12.0-kmb.10 — kapt removed, Hilt on KSP

Choose a tag to compare

@kmbrimble kmbrimble released this 02 Sep 05:42

KSP stage 1: hilt-compiler was the last kapt processor and now runs under KSP 2.2.21-2.0.5. kapt is gone from this project entirely.

Closes half the AGP 10.0 deadline — android.builtInKotlin=false existed only for kapt. android.newDsl=false remains: kotlin-android itself calls the legacy variant API, so that flag drops only when classic KGP is replaced by AGP's built-in Kotlin (stage 2, not in this release). AGP's com.android.legacy-kapt is an escape hatch if that spike stalls.

Zero other version changes, deliberately.

Verified: 6 unit tests, smoke-device.sh PASS, panel-render-probe.sh RENDERING, post-install probe RENDERING, and a full per-screen DI walk (3 activities, 7 settings fragments) with no uninjected-field failures.

arm64-v8a split, signed with our own key.

0.12.0 Build 0-kmb.7 — post-minSdk cleanup tail

Choose a tag to compare

@kmbrimble kmbrimble released this 01 Sep 22:33

Five items unlocked by the minSdk 19->26 raise, plus feature/dependency-sweep folded in:

  • Merged feature/dependency-sweep (Gradle 9.7.1, AGP 9.3.2, Kotlin 2.2.21, Dagger 2.59.2),
    then re-swept AndroidX to current stable: appcompat 1.8.0, material 1.14.0,
    navigation 2.10.0, lifecycle 2.11.0. compileSdk raised 34->35 (navigation's transitive
    core-ktx requirement); targetSdk unchanged at 34.
  • Removed 5 dead SDK_INT >= O (API 26) branches, now always-true.
  • Dropped legacy-support-v13/v4/legacy-preference-v14; replaced androidx.legacy.widget.Space.
  • Removed obsolete android.enableJetifier=true.
  • Bumped constraintlayout 2.1.4 -> 2.2.2.
  • Fixed InternalWebClient.dialogUtils: constructor injection instead of never-populated
    @Inject field (live UninitializedPropertyAccessException on any SSL error).

Verified: unit tests (6, CameraFpsPin), assembleProdRelease + lintVital (0 Fatal),
smoke-device.sh, panel-render-probe.sh, manual on-device check of settings/About/MQTT
screens, promote.sh (install, relaunch, post-install render probe — PASS).

See CHANGELOG.md for full detail.