Skip to content

Add iCloud shared album sync, web settings UI, - #22

Open
ferni7 wants to merge 7 commits into
micw:masterfrom
ferni7:master
Open

ferni7 wants to merge 7 commits into
micw:masterfrom
ferni7:master

Conversation

@ferni7

@ferni7 ferni7 commented Aug 9, 2026

Copy link
Copy Markdown

Features:

  • iCloud shared album sync: two-step API (webstream + webasseturls), handles 330 redirects, picks best derivative by pixel dimensions (handles iPhone 15 Pro key '2049'), filters videos, checksum sidecar files for upgrade detection
  • Embedded web settings server on port 8080 with full settings form and sync controls; web UI save now also writes to SharedPreferences so BootReceiver picks up changes without requiring an app restart
  • Geocoding now shows city only (not City, State, Country); cache key bumped to v2 to invalidate old entries
  • Photo info overlay: added XSmall (22px) text size option (default); for bottom positions city is shown above date; for top positions date stays on top
  • Slide duration change now takes effect immediately without waiting for the current timer to expire
  • Boot autostart: BootReceiver uses AlarmManager (setExactAndAllowWhileIdle) to schedule MainActivity launch 3s after boot; logs upgraded to Log.i so they appear in release builds

…ements

Features:
- iCloud shared album sync: two-step API (webstream + webasseturls), handles
  330 redirects, picks best derivative by pixel dimensions (handles iPhone 15 Pro
  key '2049'), filters videos, checksum sidecar files for upgrade detection
- Embedded web settings server on port 8080 with full settings form and sync
  controls; web UI save now also writes to SharedPreferences so BootReceiver
  picks up changes without requiring an app restart
- Geocoding now shows city only (not City, State, Country); cache key bumped
  to v2 to invalidate old entries
- Photo info overlay: added XSmall (22px) text size option (default); for
  bottom positions city is shown above date; for top positions date stays on top
- Slide duration change now takes effect immediately without waiting for the
  current timer to expire
- Boot autostart: BootReceiver uses AlarmManager (setExactAndAllowWhileIdle)
  to schedule MainActivity launch 3s after boot; logs upgraded to Log.i so
  they appear in release builds
- Sync timeout is now configurable (15–120s, default 60s) in both the
  in-app settings and web UI; passed through to iCloud sync service so
  slow connections don't fail with the old 30s hard-coded limit
- Auto-sync now fires on every save when a source is configured, not
  only when the URL changes — so tapping Save always kicks off a sync
- Wi-Fi Settings shortcut added to Android section of settings screen
  (opens system Wi-Fi page via MethodChannel)
- Device IP/web UI URL banner shown at top of settings screen
- Web UI save now calls syncFromConfig so autostart SharedPreferences
  are updated immediately without requiring an app restart
- BootReceiver logs upgraded to Log.i so they appear in release builds
- Add FRAME_SETUP.md with quick ADB commands for setting up a new frame
Fixes a metrics-endpoint bug that could starve a low-end frame of CPU:

- WebServerService._refreshProcessStatus() walked /proc/[0-9]*/cmdline in a
  shell loop, forking two processes per PID (~520 per scan on the PX-110).
  Replaced with a single `su 0 ps -A -o NAME`
- The throttle timestamp was assigned after the await with no in-flight
  guard, so one slow scan let every later scrape start another 520-fork
  scan on top of the stuck one. Timestamp is now set up front, a 10s
  timeout bounds the call, and an in-flight flag allows only one scan
- On 2026-09-19 this pile-up, together with an ActivityManager respawn
  loop, wedged system_server for eight hours: an orphaned `tr` was found
  spinning at 100% of a core

Also adds FRAMEO.md coverage for the Pexar Frame PX-110 (MediaTek MT8167)
that replaced the retired Frameo 106K:

- Vendor install lock: the ROM blocks sideloading while
  ro.vendor.custom_recover is 1. The value is byte 0x3FE of the proinfo
  partition, published by nvram_daemon. Clearing it unblocks installs
  without touching the verity-protected system partition
- Stock service survey. Never pm disable-user a package marked
  android:persistent="true" on this ROM — that is what caused the respawn
  loop. Disable the components instead and leave the package enabled
- 32-bit only (armeabi-v7a); an arm64-only APK will not install
- Build note: Flutter picks Android Studio's OpenJDK 25 JBR, which Gradle
  8.14 cannot parse. Point --jdk-dir at Temurin 21
- Pinning the app as default HOME, and the outage post-mortem

Ignores /backups/, which holds raw partition dumps containing the device
serial and MAC address.
The Frameo 106K is retired, so its tooling and notes were mostly dead weight.

- Delete scripts/fix-frameo.sh. It hardcoded the 106K's serial and IP, stopped
  a Rockchip memtrack HAL that does not exist on the PX-110, and disabled a
  package list that no longer matches
- Add scripts/px110.sh: idempotent setup and health check. Connects over Wi-Fi
  (falling back to USB and re-enabling TCP mode), repairs the stock-service
  config, pins the app as default launcher, and reports health. --health
  reports without changing anything; --install deploys the APK after checking
  it has an armeabi-v7a slice
- Restructure FRAMEO.md around the PX-110 as the current device and compress
  the 106K to what still transfers: the eMMC failure mode and how to map a bad
  sector to a file, the ADB factory reset, and why the two Rockchip-only app
  workarounds stay in the tree. 537 -> 313 lines
- Drop the Wi-Fi/BT MAC from the notes. It is a stable hardware fingerprint
  with no operational use, and this repo has a public remote

The script checks for the adups respawn loop and orphaned scan helpers
specifically, since those are the two regressions that took the frame down.
Both existed for the retired Frameo 106K (RK3326) and are dead code on the
Pexar PX-110:

- stopCrashingMemtrackService() stopped a buggy RK3326 memtrack HAL. It was
  gated on Build.HARDWARE containing "rk" and on a vendor .so that does not
  exist here, so it never ran
- warmEmmcFileCache() preloaded the 106K's failing system files into the page
  cache. Off by default and pointless on healthy storage. Removes the setting
  from the config interface, JSON store, runtime-settings sync, the Flutter
  settings screen and the web UI, plus EmmcCacheWarmService

eMMC *monitoring* stays: opf_emmc_io_errors_total and _fetchEmmcIoErrors()
are device-independent, and are what ruled out hardware failure during the
2026-09-19 outage.

Also fixes two bugs in scripts/px110.sh found by running it:

- `cmd | grep -q` under `set -o pipefail` reports failure even on a match,
  because grep -q exits early and SIGPIPEs the writer. This made the APK
  armeabi-v7a guard reject a valid APK. Capture output, then match
- After --install the app relaunches and re-runs enableWifiAdb(), which does
  `stop adbd; start adbd` and kills the script's own connection, so every
  health check read as an empty failure. Wait and reconnect first
The settings server binds 0.0.0.0:8080 with Access-Control-Allow-Origin: * and
had no authentication, so any host on the LAN could GET /api/config or
/api/log. Both disclose the iCloud shared-album token, which is enough to open
the album — and the wildcard CORS header meant any page a LAN user visited
could read it too.

- New web_ui_password config setting; empty preserves the old open behaviour
- HTTP Basic auth on every route, checked with a constant-time comparison.
  Any username is accepted; only the password is verified
- 401 responses carry WWW-Authenticate, so browsers prompt normally
- Settable from the web UI and from the on-device settings screen. The
  on-device field is the escape hatch if the password is forgotten

/metrics is deliberately exempt so Prometheus keeps scraping without
credentials. It exposes only counters and gauges — no album URLs, tokens or log
lines. Protecting it too means adding basic_auth to the scrape config.

Verified on the frame: unauthenticated /api/config, /api/log, /api/status and /
return 401 while /metrics returns 200; a wrong password returns 401; the
correct one returns 200; and the album token is no longer retrievable without
credentials. With no password set, all routes behave as before.
This is a public fork, so the frame's serial and IP should not be in it.

- scripts/px110.sh now reads FRAME_SERIAL and FRAME_IP from scripts/frame.env,
  which is gitignored, and exits with guidance if they are unset. Environment
  variables still win over the file
- Add scripts/frame.env.example as the template to copy
- Replace the literal serial and IP in FRAMEO.md with $FRAME_SERIAL /
  $FRAME_IP, and drop the retired frame's serial from FRAME_SETUP.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants