Commit Graph

80 Commits

Author SHA1 Message Date
dadaloop82 a3147d704e chore: bump to v1.7.14 — kiosk versionCode 15, CHANGELOG 2026-05-16 13:31:54 +00:00
github-actions[bot] 424fc7bbe3 chore: auto-merge develop → main
Triggered by: 61a2372 feat(kiosk): add native settings shortcut in webapp settings page
2026-05-16 13:09:08 +00:00
dadaloop82 61a2372caa feat(kiosk): add native settings shortcut in webapp settings page 2026-05-16 13:07:29 +00:00
dadaloop82 6ab1da4bd5 ci(kiosk): trigger APK build — versionName 1.7.13 fix 2026-05-16 12:51:43 +00:00
dadaloop82 8d874944b5 fix(kiosk): versionName 1.7.2→1.7.13, versionCode 13→14 (stops false update loop) 2026-05-16 12:42:46 +00:00
dadaloop82 da4bd635db feat: professional repo cleanup + community infrastructure
- README: remove Recent Updates section, clean roadmap (pending only),
  replace Screenshots with demo link, add 6 new badges (stars, last
  commit, contributors, discussions, CI), invite GIF contributions
- CHANGELOG: translate all Italian entries to English, add v1.7.13
  (DB fresh-install crash fix)
- database.php: add missing 'undone' column to transactions schema;
  wrap ALTER TABLE calls in try/catch to prevent race-condition errors
  on concurrent first requests
- Wiki: Android-Kiosk v1.5.0 → v1.6.0, Step 5 rewritten (BLE scan,
  no external APK), removed gateway troubleshooting section
- Wiki: Scale-Gateway promoted to deprecated with redirect banner
- Wiki: Home What's New updated to v1.7.12 / v1.7.13
- Wiki: Features.md, kiosk README Italian UI strings translated
- .github: add bug_report.yml, feature_request.yml issue templates,
  config.yml (blank issues off, links to FAQ/Discussions/Security),
  PULL_REQUEST_TEMPLATE.md with checklist
- .github: FUNDING.yml (Ko-fi), dependabot.yml (monthly action updates)
- .github/workflows/security.yml: Trivy docker + fs scan, SARIF upload
- .github/workflows/build-scale-gateway.yml: disabled (deprecated)
- SECURITY.md: responsible disclosure policy, supported versions, scope
- CODE_OF_CONDUCT.md: Contributor Covenant 2.1
- Settings UI: About section with version display, Report Bug button,
  Changelog and GitHub links; reportBugManual() + _loadAboutSection()
- Translations: added 'about' key group (9 keys × 3 languages)
2026-05-16 06:02:18 +00:00
dadaloop82 4624811707 fix(kiosk): periodic update check every 30min + persist pending update across restarts 2026-05-14 14:47:47 +00:00
dadaloop82 8bb6c01b7d fix: move modal countdown stops on touch; vacuum btn no longer triggers native settings 2026-05-14 12:22:45 +00:00
dadaloop82 1b7b271b43 fix(kiosk): detect ANR/OOM/native crashes on restart via ApplicationExitInfo + dirty sentinel 2026-05-14 11:47:05 +00:00
dadaloop82 2d70e7a688 chore: release v1.7.12 — banner aperti, fix ricette pz, fix usa-tutto 2026-05-13 11:40:05 +00:00
dadaloop82 96d3514c38 kiosk: report ALL crashes via ErrorReporter
- onRenderProcessGone: WebView renderer crash/OOM kill now reported
  and Activity is recreated automatically (no more silent crash)
- onReceivedHttpError: HTTP 5xx from server now reported
- onLowMemory: system low-memory event reported
- onTrimMemory: moderate+ memory trim events reported

Every error path in the kiosk now sends an auto-report issue.
2026-05-08 11:27:28 +00:00
dadaloop82 ffb0341eb6 feat: screensaver configurable timeout + fix gitignore (exclude kiosk build artifacts) 2026-05-06 15:00:04 +00:00
dadaloop82 8535f4d4b9 fix: update check ignores non-semver tags + GitHub Actions builds versioned releases 2026-05-06 14:41:36 +00:00
dadaloop82 cbb3f6b288 fix: use project keystore for consistent APK signing (fixes OTA update signature conflict) 2026-05-06 14:25:59 +00:00
dadaloop82 1c890c66ea feat: kiosk manual update check + install from settings
KioskActivity.kt:
- checkForUpdates(forceCheck, jsCallback): accepts force flag + optional
  JS callback to deliver result as JSON to the WebView
- New @JavascriptInterface checkForUpdates(): bypasses 6h throttle,
  calls back window._kioskUpdateResult({has_update, current, latest, apk_url})
- New @JavascriptInterface installUpdate(apkUrl): triggers APK download+install
- Improved error handling: HTTP status check + network error JSON response

app.js:
- window._kioskUpdateResult(): callback receives update check JSON,
  shows green/amber status box with version info, shows install button
- _kioskCheckForUpdates(): triggers native check, shows spinner while waiting
- _kioskInstallUpdate(): passes apk_url to native installUpdate()
- loadSettings(): shows #kiosk-update-panel when in kiosk WebView

index.html:
- #kiosk-update-panel: version label, 'Cerca aggiornamenti' button,
  status box, 'Installa aggiornamento' button (hidden until update found)
- CSS cache bump ?v=20260506e

build.gradle.kts: version 1.6.0 → 1.7.0 (versionCode 10 → 11)
2026-05-06 14:17:31 +00:00
dadaloop82 115c966322 fix: scale dot contrast + kiosk scale config panel + download banner in kiosk
style.css:
- Scale connected dot: bright #4ade80 fill + white border + double shadow
  so it pops on the dark green header (was white on green = invisible)

index.html:
- Scale settings tab: add kiosk panel with 'Riconfigura bilancia BLE'
  button (hidden in browser, shown in kiosk mode)
- Wrap gateway download section and WebSocket URL section with IDs
  so JS can hide them in kiosk mode
- CSS cache bust ?v=20260506b

app.js:
- syncSettingsFromDB: in kiosk mode hide scale gateway download section,
  WebSocket URL section and test button; show kiosk BLE panel instead;
  auto-set URL to ws://localhost:8765
- Add _kioskReconfigureScale() helper that calls _kioskBridge.reconfigureScale()

KioskActivity.kt:
- Add reconfigureScale() @JavascriptInterface: stops GatewayService,
  clears saved scale device prefs, launches SetupActivity at step 4
- Import GatewayService
2026-05-06 13:44:50 +00:00
dadaloop82 f04e227cc0 fix: kiosk title center + location pref 2 uses + update confirm before download
app.js:
- _injectKioskOverlay: move kiosk-mode class assignment BEFORE the
  _kiosk_overlay existence guard — fixes race where Kotlin onPageFinished
  injects buttons during the await api() pause, then JS skips the class
- _PREF_LOC_NEEDED: 3 → 2 (remember use-location after 2 picks, not 3)

KioskActivity.kt:
- showNativeUpdateBanner: remove auto-start of triggerApkDownload;
  banner now shows with 'Scarica' button enabled, download only starts
  when user taps it (confirms before install)
2026-05-06 11:24:29 +00:00
dadaloop82 6d098a80a6 fix: scale auto-reconnect + reconfigure button in kiosk settings
GatewayService:
- onScanStopped() now calls scheduleReconnect() when not connected, so
  if the scale is off at startup the service keeps retrying every 8s
  instead of giving up after the first 20s scan window

SettingsActivity:
- Add 'Riconfigura bilancia' button (yellow outline) when a scale is
  already configured — clears saved device, stops the gateway service,
  and opens SetupActivity directly at step 4 (scale scan step)

SetupActivity:
- Accept 'start_step' intent extra to jump directly to a specific step
  (used by SettingsActivity reconfigure flow)
2026-05-06 11:18:38 +00:00
dadaloop82 754f13111f feat: kiosk setup improvements + webapp scale indicator fixes
kiosk setup wizard:
- Scale step: ask user to power on the scale before scanning (new
  'Accendi la bilancia' card with 'Bilancia accesa → Cerca' button)
- BLE scan: filter results to only show compatible scales (scaleScore>0)
  hiding clearly non-scale BLE devices from the list
- After device selection: run a live connection test — connect to the
  scale, display the live weight, ask 'Corrisponde al peso sulla bilancia?'
  with  Sì /  Riprova / Skip buttons before confirming the device

webapp:
- Scale indicator not live on first load: scaleInit() was firing before
  syncSettingsFromDB() resolved; fixed by chaining .then(scaleInit)
- Scale icon green-on-green: connected state dot changed from #22c55e
  (green, invisible on dark-green header) to white with green border+glow,
  visible on any background color
2026-05-05 18:11:51 +00:00
dadaloop82 7ea5505a0d fix: scale indicator, logo crop, gateway LAN IP, setup spacing
webapp:
- Scale indicator: replace plain green dot with ⚖️ emoji + colored
  status badge (green/amber/grey/red); icon fades out when disconnected;
  tap shows a toast with device name + battery level
- Logo images: crop excess transparent padding from logo.png and
  logo_icon.png so content fills the frame at small display sizes
- style.css: reworked .scale-status-indicator CSS for emoji+badge

kiosk:
- SetupActivity: use device's real LAN IP for scale_gateway_url
  (was hardcoded 127.0.0.1 — only worked if server and kiosk run on
  the same machine); added getDeviceLanIp() helper (prefers wlan/eth)
- activity_setup.xml: reduce welcome step padding/margins so step 1
  fits on screen without scrolling; text sizes slightly reduced
- activity_setup.xml: fix feature bullet 'Bilancia Bluetooth via
  Gateway app' → 'Bilancia BLE integrata (nessuna app esterna)'
- strings.xml (en + it): rewrite all wizard_gateway_* strings to
  reflect integrated BLE service instead of external gateway APK
- ic_logo.png: regenerated at all densities from cropped source
2026-05-05 17:47:54 +00:00
dadaloop82 a5094920bf kiosk: remove all external gateway app references + update docs
- SettingsActivity: replace GATEWAY_PACKAGE / PackageManager check with
  GatewayService status; show BLE device name + live :8765 probe;
  buttons now restart GatewayService or redirect to setup wizard
- activity_settings.xml: rename section label to 'BILANCIA SMART',
  update button text to '⚙️ Configura bilancia'
- evershelf-scale-gateway/README.md: add DEPRECATED notice (gateway is
  now integrated into kiosk v1.6.0+, this app is no longer maintained)
- evershelf-kiosk/README.md: full rewrite — reflects v1.6.0, integrated
  BLE gateway, 6-step wizard, permissions table, protocol reference
- README.md: update kiosk features (remove gateway APK install/launch,
  add integrated BLE service), update scale section, update architecture,
  add kiosk v1.6.0 entry in Recent Updates
2026-05-05 17:31:14 +00:00
dadaloop82 9cb29de1f0 kiosk: integrate BLE scale gateway + fix logo/branding
- Kiosk v1.6.0 (versionCode 10)
  - Integrate BLE scale gateway directly into kiosk app (no external app needed)
    - New scale/ package: BleScaleManager, GatewayWebSocketServer, ScaleProtocol, GatewayService
    - GatewayService: foreground service, runs BLE scan + WebSocket :8765 server
    - Auto-reconnect on BLE disconnect; protocol compatible with old gateway app
  - Setup step 4: replace gateway install flow with BLE device scan + selection (mandatory)
  - Permissions: added BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION (pre-S),
    FOREGROUND_SERVICE, FOREGROUND_SERVICE_CONNECTED_DEVICE
  - KioskActivity: replace launchGatewayInBackground() with startGatewayService()
  - checkForUpdates: remove gateway APK check (gateway is now internal)
  - Remove GATEWAY_PACKAGE / GATEWAY_DOWNLOAD_URL constants

- Logo / branding
  - logo.png + logo_icon.png: transparent background (no more black)
  - ic_logo.png regenerated in all densities
  - Removed house emoji (🏠) from web UI: favicon, bottom nav, setup wizard header
  - Removed 🏠 prefix from all translations (it/en/de) and manifest
  - Setup wizard: logo shown in language + welcome steps
  - Setup wizard: footer with credits ('Creato da Stimpfl Daniel • Open Source')
  - CSS: .nav-logo-icon for bottom nav logo sizing

- Scale Gateway v2.1.1 (versionCode 8)
  - Fix false update notification: replace == comparison with proper semverNewer()
    (was reporting 'update available' whenever tag != current, e.g. v2.1.0 != 2.1.0)
2026-05-05 17:24:24 +00:00
dadaloop82 8ee6fe8770 kiosk: gateway auto-launch, update auto-download, gateway setup UX
- KioskActivity: move launchGatewayInBackground() BEFORE enableKioskLock() so
  Android's lock-task restriction does not block starting the gateway Activity
- KioskActivity: webView.clearCache(true) before loadUrl — no caching
- KioskActivity: checkForUpdates() uses proper semver > comparison (not !=)
  to avoid false-positive 'update available' when already up-to-date
- KioskActivity: showNativeUpdateBanner() removed 30s auto-hide, now auto-
  triggers download immediately when update detected
- SetupActivity: onResume() re-checks gateway status when returning from gateway
  config (user opens gateway, configures it, presses back → wizard refreshes)
- SetupActivity: checkGatewayStatus() probes TCP 127.0.0.1:8765 to show whether
  gateway is actually running, with clear 'not running' warning to configure first
- SettingsActivity: same TCP probe for live gateway status in settings screen
- build.gradle.kts: versionCode 9, versionName 1.5.3
2026-05-05 16:48:32 +00:00
dadaloop82 30aa2db0b9 branding: add EverShelf logo to kiosk splash + web header/preloader
- assets/img/logo/logo.png: trimmed full logo (icon + text, transparent bg)
- assets/img/logo/logo_icon.png: icon-only crop (no text, for header)
- drawable-*/ic_logo.png: multi-density PNGs for Android splash (mdpi→xxxhdpi)
- activity_kiosk.xml: replace ic_launcher_foreground with ic_logo at 260dp,
  remove redundant 'EverShelf' text row (already in logo image)
- index.html: add logo_icon.png in header title, logo.png in preloader
- style.css: add .app-preloader-logo and .header-logo-icon rules
2026-05-05 16:37:57 +00:00
dadaloop82 b8d91c0089 kiosk: add missing Toast import in SetupActivity 2026-05-05 16:09:09 +00:00
dadaloop82 2ebf79d394 kiosk: fix strings.xml apostrophe syntax (' -> \') 2026-05-05 16:05:54 +00:00
dadaloop82 6500d22242 kiosk: fix install dialog + screensaver always-on
Fallback install (Intent.ACTION_VIEW):
- Remove FLAG_ACTIVITY_NEW_TASK: it caused startActivityForResult to return
  RESULT_CANCELED immediately, making the system installer dialog disappear in ~1s
- After fallback returns with app not installed: show '🔄 Riprova installazione'
  button that calls tryFallbackInstall() directly (skips PackageInstaller which
  is known to give STATUS=1 on this device)

Screensaver:
- KioskActivity.applyScreensaverFlag(): always add FLAG_KEEP_SCREEN_ON, never
  clear it — screen must ALWAYS stay on in kiosk mode
- The 'salvaschermo' toggle controls the in-app JS clock overlay (webapp setting),
  NOT the Android screen timeout
- finishSetup(): always push screensaver_enabled to webapp API (not just when scale
  is configured)
- SettingsActivity save: remove FLAG_KEEP_SCREEN_ON conditional; push
  screensaver_enabled to server API on save
- Update setup wizard description + strings to clarify in-app overlay vs screen off

Bump version to 1.5.2 (versionCode 8)
2026-05-05 16:01:47 +00:00
dadaloop82 84d2ff0264 kiosk: improve wizard UX + gateway configurability
- Wizard step 4 (scale): skip question if scale already configured (KEY_HAS_SCALE=true), show gateway status directly
- Add '⚙️ Apri Gateway per configurarlo' button in setup wizard after gateway is installed
- Add same button in SettingsActivity SMART SCALE section (visible only when installed)
- Fix btnRunWizard in SettingsActivity: immediately launches SetupActivity instead of just showing a toast
- Bump version to 1.5.1 (versionCode 7)
2026-05-05 06:09:48 +00:00
dadaloop82 b2a334340b fix(kiosk): warn user to press 'Done' not 'Open' after gateway install
- Before launching system installer (ACTION_VIEW fallback), show a
  non-cancellable dialog warning: press Fine, NOT Apri
- After installer returns, force SetupActivity back to foreground in case
  user pressed Open anyway (FLAG_ACTIVITY_REORDER_TO_FRONT)
2026-05-05 06:01:53 +00:00
dadaloop82 0f0ce684f1 fix(kiosk): fallback Intent.ACTION_VIEW when PackageInstaller STATUS=1 + fix self-update URL
- SetupActivity: catch STATUS_FAILURE=1 separately and immediately retry with
  Intent.ACTION_VIEW (system installer dialog) instead of showing a dead error.
  STATUS=1 is a generic PackageInstaller failure that can happen on many
  Android 14 devices even with a valid APK, but the system installer handles it.
- SetupActivity: remove misleading 'incompatibile' hint for status=1 (was wrong;
  STATUS_FAILURE_INCOMPATIBLE = 7, not 1).
- SetupActivity: deduplicate buildDeviceLabel() to shared private method
- KioskActivity: fix KIOSK_DOWNLOAD_URL to point to kiosk-latest release
  (was pointing to 'latest' which only has the gateway APK, so self-update
  was silently broken).
- Bump version 1.4.0 -> 1.5.0 (versionCode 5 -> 6)
2026-05-05 05:50:22 +00:00
dadaloop82 abeb87c536 fix(kiosk): fix APK install failure — session lifecycle, error details, issue reporting
- KioskActivity: remove .use{} on PackageInstaller session to prevent premature
  session close causing STATUS_FAILURE=1; align with SetupActivity pattern
- SetupActivity: show full diagnostic info (status code + human-readable hint,
  device, Android version) in the UI card instead of just 'status=1'
- SetupActivity: use Build.PRODUCT/BOARD fallback when MANUFACTURER='unknown'
- ErrorReporter: add forceReport param to bypass in-session dedup for retries
- ErrorReporter: include Android SDK version in deviceInfo; fallback for
  'unknown' MANUFACTURER/MODEL using PRODUCT/HARDWARE/BOARD
2026-05-05 05:32:31 +00:00
dadaloop82 e4869c4308 fix(kiosk): gateway install hardening + diagnostic dialog on failure
- installApk(): add runtime canRequestPackageInstalls() check with user dialog
  (permission may have been revoked or not active even if declared in manifest)
- STATUS_FAILURE else branch: show AlertDialog with full diagnostics on screen
  (status code, message, APK size, Android version, device model) so the
  problem is visible even when the server error report fails to deliver
- Dialog has Riprova button (retries installWithPackageInstaller immediately)
  and Salta button (skips to gateway status check)
- INSTALL_PERM_REQUEST onActivityResult: resume from installApk() if a local
  APK file is already present, not just from triggerApkDownload()
- ErrorReporter context enriched with device model string
2026-05-04 18:24:56 +00:00
dadaloop82 6d13b895ea fix(kiosk): gateway install STATUS_FAILURE root cause
Two bugs caused the gateway APK install to always fail with status=1:

1. setAppPackageName() removed from SessionParams
   This optional call forces the installer to verify the package name
   against the APK's manifest. On some OEM/Android versions this
   comparison fails even when the name is correct, returning the generic
   STATUS_FAILURE (1) with no EXTRA_STATUS_MESSAGE. Removing it lets
   the installer proceed without the extra check.

2. BroadcastReceiver was unregistered on STATUS_PENDING_USER_ACTION
   On Android 11+ the final install result (STATUS_SUCCESS/STATUS_FAILURE)
   arrives as a SECOND broadcast AFTER the user confirms the dialog.
   The receiver was being unregistered immediately on the first broadcast
   (PENDING_USER_ACTION), so the final result was never received.
   Fix: only unregister on terminal statuses (SUCCESS, FAILURE, ABORTED).

Additional improvements:
- STATUS_FAILURE_ABORTED (-1) handled explicitly: resets UI without
  showing an error (user just pressed back on the confirmation dialog)
- session.abandon() called on exception instead of letting .use{} close
- ErrorReporter now includes apk_kb and android API level in context
- onActivityResult(INSTALL_CONFIRM_REQUEST) no longer sets success/failure
  UI (the BroadcastReceiver is responsible for the final result)
2026-05-04 18:07:35 +00:00
dadaloop82 4f6592b749 fix: install_failure type mismatch — issue never reached GitHub
SetupActivity was sending type 'install-failure' (hyphen) but the PHP
version-guard bypass list only checked for 'install_failure' (underscore).
Result: if the kiosk was not on the latest released version the error was
silently discarded and no GitHub issue was created.

Fix:
- SetupActivity: change type to 'install_failure' (underscore, consistent
  with KioskActivity which already used the correct name)
- api/index.php: add 'install-failure' (hyphen) to the bypass list as
  defensive fallback so old APK builds already in the field are covered too
2026-05-04 17:45:46 +00:00
dadaloop82 5af62e61cd fix(kiosk): APK install error reporting + gateway pre-configuration
ErrorReporter:
- Init ErrorReporter at Setup onCreate using any previously saved URL
  (before the fix, init() was only called at step 3, so install errors
   happening in step 4 were silently dropped)
- Add ErrorReporter.reportMessage() call in the STATUS_FAILURE else branch
  of installWithPackageInstaller (was showing error UI but sending nothing)

Gateway pre-configuration:
- finishSetup() now detects has_scale=true + gateway installed
- If so, POSTs scale_enabled=true + scale_gateway_url=ws://127.0.0.1:8765
  to the EverShelf server's save_settings API endpoint
- This means the webapp works with the scale out-of-the-box after setup
  without the user having to go into web Settings and configure it manually
2026-05-04 17:36:11 +00:00
dadaloop82 d1040e5747 fix(kiosk): add ports 443 and 8443 to auto-discovery scan list 2026-05-04 17:17:41 +00:00
dadaloop82 cfcba32c45 fix(kiosk): permissions button transforms to 'Continue' after grant + fix subnet detection
Permissions step:
- Add btnGrantPerms as class field (was only inline findViewById)
- Extract onPermissionsGranted() helper: transforms button to green ' Permessi
  concessi — Continua →' and sets click listener to showStep(3)
  instead of calling onPermissionsGranted() which advances to step 3 (Server)
- Remove the 600ms auto-advance; user controls when to proceed
- Add setup_perms_granted_next string in EN/IT/DE

Network discovery — wrong subnet fix:
- Skip virtual/VPN/cellular interfaces: tun, ppp, rmnet, pdp, ccmni, dummy, sit,
  gre, v4-, v6-, p2p, ham, nordlynx prefixes
- Also skip intf.isVirtual interfaces
- Sort: wlan*/eth* interfaces first (highest priority), others after
- Show detected subnet(s) in UI immediately before scan starts
2026-05-04 17:11:17 +00:00
dadaloop82 09fd122718 fix(kiosk): rewrite autoDiscover — real-time IP feedback + CompletionService + TCP pre-check
Problems fixed:
- f.get() sequential collection blocked on timed-out futures in submission order
  → replaced with ExecutorCompletionService: results collected as soon as ready
- WifiManager.getConnectionInfo() deprecated on Android 10+, could return IP=0
  → replaced with NetworkInterface.getNetworkInterfaces() for subnet detection
- No real-time feedback: status stuck on 'Scanning…' throughout
  → UI updated every ~120ms showing current IP:port (n/total)
- TCP socket pre-check (600ms) before HTTP probe: filters unreachable hosts instantly
  → reduces scan time from minutes to seconds on typical /24 networks
- executor.shutdownNow() cancels remaining tasks the moment server is found
- 60-thread pool instead of 40 for faster parallel scanning
- Remove unused WifiManager import
2026-05-04 17:01:01 +00:00
dadaloop82 4df06b01f4 feat(kiosk): language selection as first setup step + screensaver step
- SetupActivity: new Step 0 — language picker (IT/EN/DE) with large buttons,
  hardcoded trilingual title so it's always readable; saves 'kiosk_language' pref,
  calls recreate() via onSaveInstanceState to reload the Activity in chosen locale
- SetupActivity: new Step 5 — screensaver toggle (before Done), saves 'screensaver_enabled'
- All existing steps shifted: Welcome→1, Permissions→2, Server→3, Scale→4, Done→6
- Progress dots updated to 5 dots (steps 1-5)
- attachBaseContext override in SetupActivity, KioskActivity, SettingsActivity to
  apply the saved locale to all Activities via SetupActivity.applyLocale()
- buildSummary now shows language, screensaver setting, and scale status
- New string resources: setup_screensaver_*, summary_lang, summary_scale_skip,
  summary_screensaver_on/off in IT, EN, DE
2026-05-04 16:47:56 +00:00
dadaloop82 3649be848a feat(kiosk): add screensaver toggle in settings (default off) 2026-05-04 15:42:51 +00:00
dadaloop82 fa9c52e997 feat(kiosk): complete setup wizard overhaul
- New SetupActivity (5 steps, NOT kiosk-locked, always has Exit button)
  - Step 0: Welcome — logo, tagline, privacy/offline notice, feature list
  - Step 1: Permissions — explain camera/mic/install before requesting
  - Step 2: Server — URL input + LAN auto-discovery + fixed API validation
  - Step 3: Scale — YES/NO question first; gateway info + install only after YES
  - Step 4: Done — summary + Launch button
- KioskActivity simplified: removed all wizard code (~700 lines)
  - kiosk lock only enabled AFTER setup completes
  - starts SetupActivity on first run; launches WebView on return
- activity_kiosk.xml simplified: removed wizard ScrollView
- AndroidManifest: added SetupActivity declaration
- Fixed testConnection(): validates /api/index.php?action=get_settings JSON
- Fixed scale freeze: scale YES/NO asked before any connection check
- Fixed gateway install status=1: same PackageInstaller flow + signature conflict dialog
- Auto-discovery: parallel scan of LAN (30 threads, ports 80/8080, common subnets)
- i18n: new setup strings in en/it/de
2026-05-04 15:40:45 +00:00
dadaloop82 7c61ae61bb fix(kiosk): fix ByteArray type inference error in APK magic-byte check
The try expression had a spurious 'true' result in one branch which
made Kotlin infer the type as Any? instead of ByteArray?.
Simplified to a single try block with explicit type annotation
ByteArray? to eliminate the ambiguity.
2026-05-03 20:14:19 +00:00
dadaloop82 22e506bd66 fix(kiosk): 4 bug fix — uninstall loop, PHP check, APK validation, ErrorReporter init
Bug 1 — Uninstall loop (kiosk lock task blocks system uninstall UI):
  startActivityForResult(ACTION_DELETE) was called while lock task was
  active. The system uninstall activity is not in the lock task whitelist
  so it either silently fails or creates an unresolvable loop.
  Fix: call disableKioskLock() immediately before every ACTION_DELETE
  intent (3 call sites). Call enableKioskLock() at the start of
  onActivityResult(UNINSTALL_REQUEST) before retrying install.
  Added 600 ms delay after uninstall so PackageManager finishes cleanup.

Bug 2 — Step 2 only checks HTTP connectivity, not PHP API:
  testConnection() was checking the root URL only. A generic web server
  could pass while the EverShelf PHP API was absent.
  Fix: after HTTP 200-399 on the root URL, do a second GET to
  /api/?action=check_update and check the response body contains
  'latest_tag'|'webapp_version'|'ok'. Shows:
     Server EverShelf trovato e API attiva!
    ⚠  Server raggiungibile ma API PHP non trovata (codice N)

Bug 3 — STATUS_FAILURE=1 even after uninstall (invalid APK file):
  GitHub DownloadManager follows redirects; if the release asset does
  not exist yet, GitHub returns a 404 HTML page but DownloadManager
  still reports STATUS_SUCCESSFUL. PackageInstaller then tries to parse
  HTML as an APK and returns STATUS_FAILURE=1.
  Fix: validate APK magic bytes (0x504B = 'PK') before calling
  installWithPackageInstaller. If invalid: show error, delete corrupt
  file, send ErrorReporter event, re-enable retry button.
  Also renamed install error string to install_error_install (separate
  from install_error_download) for clarity.

Bug 4 — ErrorReporter.serverBaseUrl empty during wizard install:
  ErrorReporter.init() is called in onCreate() with the saved URL.
  On first setup the URL is typed in step 2 and saved to prefs, but
  ErrorReporter still has serverBaseUrl='' for the rest of that session.
  Any install error in step 3 silently failed to POST.
  Fix: call ErrorReporter.init(this, url) in btnStep2Next immediately
  after prefs.edit().putString(KEY_URL, url) so step 3 has a live URL.
2026-05-03 20:10:40 +00:00
dadaloop82 38eb66cfbf feat(kiosk): server reachability check in step 3 + uninstall-on-generic-failure
Server check (wizard step 3):
- New horizontal card above the scale question always shows server status
  as soon as step 3 is entered:  checking →  reachable / ⚠️ not reachable
- Pings GET $serverUrl/api/?action=check_update (5 s timeout)
- If reachable: 'Error reporting active — failures sent to GitHub Issues'
- If not reachable: 'Check the URL in step 2' warning
- checkServerReachability() called every time goToStep(3) runs
- Strings added in EN / IT / DE

Signature-conflict fallback (else branch in installWithPackageInstaller):
- When PackageInstaller returns a generic STATUS_FAILURE and the target
  package is already installed, a signature conflict is the most likely
  cause (CONFLICT/INCOMPATIBLE are caught separately earlier)
- New AlertDialog: 'Disinstalla e riprova' → startActivityForResult
  ACTION_DELETE → UNINSTALL_REQUEST → auto-retries install on return
- Only shown when all else has already failed
2026-05-03 19:35:20 +00:00
dadaloop82 15e1dfbd69 fix(kiosk): STATUS_FAILURE=1 (wrong package) + issue version-guard bypass
Bug 1 — Root cause of PackageInstaller STATUS_FAILURE=1:
  The dest file is always named 'evershelf-update.apk'. installApk()
  was trying to detect 'gateway' in the filename — always false.
  So setAppPackageName() was always passed 'it.dadaloop.evershelf.kiosk'
  even when installing the gateway APK (package scalegate).
  PackageInstaller rejects the mismatch with STATUS_FAILURE=1.

  Fix: save apkUrl into pendingApkDownloadUrl at the TOP of
  triggerApkDownload() (not only in the permission branch), then derive
  targetPkg from the URL (which does contain 'gateway'/'scale') instead
  of the filename.

Bug 2 — Install errors not reaching GitHub Issues:
  PHP reportError() has a version guard: if the client version is not
  the latest release, it silently skips GitHub issue creation.
  A device that is FAILING TO INSTALL an update is by definition on an
  old version, so every install error was silently dropped.

  Fix: bypass the version guard for types install_download_failed,
  install_failure, install_packager_exception.
2026-05-03 19:29:04 +00:00
dadaloop82 645162f063 fix(kiosk,gateway): use RECEIVER_EXPORTED for DownloadManager broadcast
Root cause of 'stuck on downloading' bug (Android 13+):

DownloadManager.ACTION_DOWNLOAD_COMPLETE is sent by the system process,
which is external to our app. Registering the receiver with
RECEIVER_NOT_EXPORTED silently drops the broadcast — the BroadcastReceiver
never fires, the install never starts, and the UI stays frozen at
whatever progress percentage the poller last saw.

Fix: use RECEIVER_EXPORTED for the DownloadManager completion receiver in
both kiosk and scale-gateway apps.

The PackageInstaller result receiver (internal PendingIntent broadcast,
same package) correctly keeps RECEIVER_NOT_EXPORTED — that one is
intentionally app-private.
2026-05-03 19:19:44 +00:00
dadaloop82 0dac10d05e fix(kiosk): fix compile error — ErrorReporter.report() → reportMessage()
ErrorReporter.report() takes a Throwable as first argument.
The three new calls added in fe633c9 incorrectly passed 'this'
(Context) instead, causing compileDebugKotlin to fail.

Replace with ErrorReporter.reportMessage(type, message) which
is the correct overload for non-exception error events.
2026-05-03 19:12:42 +00:00
dadaloop82 fe633c97cb fix(kiosk): real-time download progress bar + ErrorReporter on failures
Problem: tapping 'Aggiorna Scale Gateway' gave no visible feedback after
the button was pressed — user could not tell if the download was
happening, stuck, or had silently failed.

Changes:
- layout: add horizontal ProgressBar (determinate) + percentage TextView
  inside the wizard step-3 status card
- layout: add thin ProgressBar (4 dp) at the bottom of the update banner
  (banner changed to vertical orientation to accommodate it)
- startDownloadProgressPoll(downloadId): polls DownloadManager every
  500 ms, reads COLUMN_BYTES_DOWNLOADED_SO_FAR and COLUMN_TOTAL_SIZE_BYTES,
  updates status card + banner with 'Download: 45% (18.2 MB / 40.5 MB)'
- setInstallUI(): new 'progress' parameter (-2 = hide, -1 = indeterminate,
  0-100 = determinate) and 'progressText' for the label under the bar
  — both bars updated in sync
- Status transitions now visible:
     Download: 45% [====----] 18.2 MB / 40.5 MB
     Installazione in corso… [~~~~] (indeterminate)
     + 'Conferma nel dialog…'
     Installato! → bar hidden, gateway status re-checked after 3 s
     + error detail → bar hidden, button re-enabled as '↩ Riprova'
- All error paths (download fail, PackageInstaller exception, installer
  failure status) now call ErrorReporter.report() → GitHub Issue created
  automatically so failures are tracked without user intervention
- Dismiss button also cancels the progress poll + hides the bar
2026-05-03 19:07:19 +00:00
dadaloop82 7d8132a743 fix(kiosk): persistent install progress in UI — no more silent Toasts
Problem: tapping 'Aggiorna ora' showed a fleeting 'Download avviato'
Toast and then nothing — no feedback on download progress, installer
state, success or failure.

Solution — setInstallUI() central helper:
- Updates the wizard step-3 status card (icon + title + detail line)
  OR the update banner (tvUpdateMessage) depending on which is visible
- Always updates and enables/disables the button that triggered the flow

States shown (status card + button text):
   Scaricamento in corso…  (download started)
   Installazione in corso… (download done, PackageInstaller running)
   Installazione in corso… + 'Conferma nel dialog…' (user action needed)
   Installato con successo! (onActivityResult RESULT_OK or STATUS_SUCCESS)
     → after 3 s auto-refreshes gateway status + closes banner
   Download fallito / Installazione non riuscita
     → button re-enabled as '↩ Riprova'

Strings added (EN default + IT + DE):
  install_downloading, install_downloading_detail
  install_installing, install_confirm_detail
  install_success, install_success_detail
  install_error_download, install_error_download_detail
  install_perm_detail, install_btn_retry
2026-05-03 18:54:27 +00:00
dadaloop82 4897da571d feat(kiosk): ask if scale is present; check+update gateway in wizard
Wizard step 3 — 'Do you have a Bluetooth smart scale?':
- New question card with two buttons shown first:
     Yes → reveal gateway status card + bottom nav buttons
    ➡️ No  → save KEY_HAS_SCALE=false, skip to web view
- KEY_HAS_SCALE pref controls whether the gateway is auto-launched
  both after wizard completion and on every subsequent app start
- checkGatewayStatus(): uses string resources (multilingual)
- checkGatewayUpdate(): fetches GitHub release, compares version;
  if gateway needs an update shows '📥 Update Scale Gateway' button
  that triggers triggerApkDownload() (full PackageInstaller flow)
- onResume step-3 re-check only fires when status card is visible
  (i.e. user already answered 'Yes') — handles return from install

Multi-language: strings.xml added for EN (default), IT, DE
strings: wizard_step3_question/yes/no, wizard_gateway_installed/
  not_installed/checking/up_to_date/update_available/update_detail,
  btn_back/launch/launch_no_scale/download_gateway/update_gateway
2026-05-03 18:31:22 +00:00