Commit Graph

372 Commits

Author SHA1 Message Date
github-actions[bot] 7cb39d94b6 chore: auto-merge develop → main
Triggered by: 0dac10d fix(kiosk): fix compile error — ErrorReporter.report() → reportMessage()
2026-05-03 19:14:11 +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
github-actions[bot] 8865131557 chore: auto-merge develop → main
Triggered by: fe633c9 fix(kiosk): real-time download progress bar + ErrorReporter on failures
2026-05-03 19:08:59 +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
github-actions[bot] fe7aca43ab chore: auto-merge develop → main
Triggered by: 7d8132a fix(kiosk): persistent install progress in UI — no more silent Toasts
2026-05-03 18:56:14 +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
github-actions[bot] 4b27f249e3 chore: auto-merge develop → main
Triggered by: 03f201c ci: auto-merge develop → main after all checks pass
2026-05-03 18:49:58 +00:00
dadaloop82 03f201c651 ci: auto-merge develop → main after all checks pass
New job 'auto-merge-to-main' in ci.yml:
- needs: lint-php, lint-js, docker-build, validate-translations
- only runs when github.ref == refs/heads/develop
- uses git merge --no-ff so history is preserved
- push to develop → CI passes → main updated automatically
2026-05-03 18:48:20 +00:00
dadaloop82 0dbe5cf00f Merge develop → main: EverShelf v1.6.0
Brings in all changes from develop:
- Offline OCR (Tesseract) + category classifier
- Centralized error reporting → auto GitHub Issues
- Version-aware update banners (kiosk, scale gateway, webapp)
- APK self-update via PackageInstaller (conflict auto-retry + uninstall)
- Webapp: 'Aggiorna ora' hard-reload banner
- Dashboard skeleton loading (.stat-loading shimmer)
- _checkWebappUpdate ReferenceError fix (Issue #7 closed)
- Kiosk wizard: ask if user has a smart scale (EN/IT/DE strings)
  scale gateway check + update in wizard step 3
- Version bumps: webapp v1.6.0, kiosk v1.4.0, scale gateway v2.1.0
- CHANGELOG + README updated
2026-05-03 18:33:12 +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
dadaloop82 a701e5a239 fix: 'Aggiorna ora' banner + APK conflict auto-retry after uninstall
Webapp update banner:
- 'Vedi novità' link replaced with 'Aggiorna ora' button
- Clicking 'Aggiorna ora' does a hard page reload (?bust=timestamp)
  which forces the browser to fetch the latest files from the server
- GitHub release URL kept as a small secondary 'novità' link

APK install conflict (kiosk + scale gateway):
- STATUS_PENDING_USER_ACTION: changed startActivity → startActivityForResult
  (kiosk) / installConfirmLauncher.launch (gateway) so we get notified
  if the system installer fails due to signature conflict
- On non-OK result from system installer: show AlertDialog offering to
  uninstall, using UNINSTALL_REQUEST / uninstallLauncher
- STATUS_FAILURE_CONFLICT/INCOMPATIBLE: same uninstall flow
- After uninstall completes, install automatically retries with the
  saved APK file (pendingInstallFile) — no manual re-download needed
- Gateway: also saves destFile to pendingInstallFile at download time
2026-05-03 18:22:29 +00:00
dadaloop82 eb2e8fa1d0 fix: _checkWebappUpdate ReferenceError — convert IIFE to function declaration
The function was wrapped as a named function expression (function foo(){})
which scopes the name 'foo' only to the function body, not the outer scope.
_initApp called setTimeout(_checkWebappUpdate, 6000) causing:
  ReferenceError: _checkWebappUpdate is not defined

Fix: remove the (...)​ wrapper so it becomes a regular function declaration
visible to the entire module scope.

Fixes #7
2026-05-03 18:14:15 +00:00
dadaloop82 e3df15bf9e fix: Kotlin Unresolved reference fp_ in ErrorReporter (use ${fp}) 2026-05-03 18:04:36 +00:00
dadaloop82 9e4a8323c3 chore: bump versions + update CHANGELOG/README for v1.6.0
Webapp:    v1.5.0 → v1.6.0
Kiosk:     v1.3.0 → v1.4.0 (versionCode 4→5)
Scale GW:  v2.0.0 → v2.1.0 (versionCode 6→7)

CI: build-scale-gateway.yml now also triggers on develop branch
    (was main-only, causing APK builds to not run on feature branches)

CHANGELOG: added [1.6.0] entry covering PackageInstaller OTA fixes,
  dashboard skeleton, update banners, cooking mode z-index, XOR token
README: updated 'Recent Updates' section with 1.6.0 highlights
2026-05-03 18:00:46 +00:00
dadaloop82 73fbb73974 fix: APK install conflict (PackageInstaller) + dashboard stat skeleton
APK install conflict:
- Replace ACTION_VIEW-based install with PackageInstaller.Session API (API 21+)
- PackageInstaller gives us the actual install status via BroadcastReceiver:
  STATUS_PENDING_USER_ACTION → launch system confirmation dialog automatically
  STATUS_SUCCESS → success toast
  STATUS_FAILURE_CONFLICT/INCOMPATIBLE → show AlertDialog offering to
    uninstall the old version (ACTION_DELETE) so user can re-download and install
- FileProvider no longer needed for install (still kept for other uses)
- Kiosk: derive target package from filename (gateway vs kiosk self-update)

Dashboard 0-flash:
- Replace hardcoded 0 in HTML stat-value spans with ... placeholder
- Add .stat-loading CSS class: shimmer skeleton animation (gradient sweep)
- showPage(dashboard): set ... + stat-loading before API call
- loadDashboard: remove stat-loading class and set real count after data arrives
2026-05-03 17:51:18 +00:00
dadaloop82 58e69625bd fix: preloader + update notification robustness
- Add full-screen CSS preloader to webapp (fades out when _initApp completes)
- Defer _checkWebappUpdate() to 6s after app init so it does not compete
  with startup API calls (fixes perceived slowness on first load)
- Switch update-check throttle from sessionStorage to localStorage (6h TTL);
  use release published_at instead of version string for comparison, so the
  banner correctly appears when a new release is published regardless of whether
  the tag is a semver or the rolling "latest" tag
- PHP _isLatestVersion(): return true (do not suppress error reports) when
  tag_name is non-semver (e.g. "latest") — was incorrectly blocking ALL reports
- Kiosk checkForUpdates(): show banner only when the release asset actually
  contains an APK for the component; handle non-semver tag by treating it
  as always-update (prevents silent no-op with rolling "latest" tag)
- Scale gateway checkForUpdates(): same non-semver fix; apkUrl now defaults
  to empty and bails out if no matching APK found in assets (prevents 404 install)
2026-05-03 17:46:42 +00:00
dadaloop82 f9718fee6d fix: APK self-update download+install in kiosk and scale gateway
Root causes fixed:
- REQUEST_INSTALL_PACKAGES permission missing from both manifests
- FileProvider not declared in either manifest (FileProvider.getUriForFile() crashed)
- res/xml/file_paths.xml missing (required by FileProvider)
- setDestinationInExternalPublicDir() used public Downloads dir (needs storage
  permission + FileProvider can't serve it); replaced with getExternalFilesDir()
  which is app-private, needs no permission, and IS accessible by FileProvider
- canRequestPackageInstalls() check returned early with startActivity (fire-and-
  forget); user could never retry. Now uses startActivityForResult/installPermLauncher
  so the download auto-retries when user returns from the Settings screen
- Added download status check (COLUMN_STATUS == STATUS_SUCCESSFUL) before installing
2026-05-03 17:37:45 +00:00
dadaloop82 9ef2a53aeb fix: hide update banner + app-header during cooking mode; raise overlay z-index
- .cooking-overlay z-index 500 → 99998 (above everything)
- body.cooking-mode-active: hide #_evershelf_update_banner and .app-header
- .cooking-mode-active #modal-overlay z-index 600 → 99999
2026-05-03 17:33:24 +00:00
dadaloop82 076cf13ed8 feat: version-aware error reporting, XOR token, update banners
in both PHP (api/index.php) and Scale Gateway (ErrorReporter.kt)
- Add _isLatestVersion() / _latestReleaseTag() / _appVersion() helpers in PHP;
  skip GitHub issue creation if caller is not on the latest released version
- Add checkUpdate() PHP endpoint (GET api/?action=check_update, no auth required)
- Webapp (app.js): fetch check_update on load, show dismissible amber top-banner
  when a newer GitHub release is available; auto-dismiss after 20 s
- Kiosk (KioskActivity.kt + activity_kiosk.xml): replace old JS bottom-banner with
  native Android top-banner; real APK download via DownloadManager + PackageInstaller
- Scale Gateway (MainActivity.kt + activity_main.xml): same native top-banner
  with checkForUpdates() / showNativeUpdateBanner() / triggerApkDownload() / installApk()
2026-05-03 17:24:26 +00:00
dadaloop82 ea40c8e02b feat: centralized error reporting → GitHub Issues
- PHP (api/index.php): hardcode GH_ISSUE_TOKEN/GH_REPO constants at top of
  file (before exception handler runs); fix $fp_ variable interpolation bug;
  global set_exception_handler + register_shutdown_function; reportError()
  endpoint (POST ?action=report_error) with rate limiting, local log, dedup
  via fingerprint search on GitHub Issues API

- Kiosk (ErrorReporter.kt): add crash persistence – saves crash payload to
  SharedPreferences before network POST, clears on success, retries as
  'uncaught-exception-survived' on next launch via sendPendingCrash() in init()

- Scale Gateway: new ErrorReporter.kt – calls GitHub Issues API directly
  (no relay needed, token hardcoded, scoped Issues R+W only); crash
  persistence via SharedPreferences; MainActivity.kt hooked at onCreate,
  startGatewayServer catch, onError (BLE errors)

Tested end-to-end: issues #3-#6 created and closed during QA.
2026-05-03 17:11:11 +00:00
dadaloop82 f2e151d89b feat: centralized error reporting → auto GitHub Issues
PHP (api/index.php):
- reportError() endpoint (POST ?action=report_error): accepts source/type/message/stack/context/ua/version
- _createOrCommentGithubIssue(): creates new issue OR adds comment on existing one (dedup by sha1 fingerprint via GitHub search API)
- _appendErrorLog(): local data/error_reports.log fallback (500 KB rotation)
- _phpErrorReport(): called by set_exception_handler + register_shutdown_function → catches all PHP fatals and uncaught exceptions
- _githubRequest(): minimal curl-based GitHub REST v3 helper
- Rate limit bucket: error_report (20 req/min)
- Labels auto-created: auto-report, php-crash, js-error, kiosk-error, scale-error

JS (assets/js/app.js):
- reportError(payload): single POST to report_error, session-level dedup via _reportedFingerprints Set
- window.onerror: reports uncaught-error with message+stack+location context
- window.unhandledrejection: reports unhandled-promise with reason+stack
- api(): reports api-server-error on HTTP 5xx responses

Android Kiosk:
- ErrorReporter.kt: singleton with init(context, serverUrl), report(Throwable), reportMessage(type, message)
  - Thread.setDefaultUncaughtExceptionHandler → catches ALL unhandled JVM crashes
  - Async executor (single thread), per-session fingerprint dedup, synchronous fallback for crash handler
  - doPost(): HttpURLConnection POST to /api/?action=report_error with device/version info
- KioskActivity: ErrorReporter.init() in onCreate + finishWizard()
  - onReceivedError: reports webview-load-error with URL + error code
  - onConsoleMessage: reports webview-js-error for ERROR level console messages

Config: GITHUB_ISSUE_TOKEN + GITHUB_REPO added to .env.example
2026-05-03 15:36:03 +00:00
dadaloop82 a6c2fb93cf feat: offline OCR (Tesseract) + embedding category classifier (@xenova/transformers)
Tesseract OCR (PHP, server-side):
- Dockerfile: adds tesseract-ocr + tesseract-ocr-ita + libgd-dev (gd extension)
- api/index.php: new tesseractReadExpiry() — decodes base64 image, pre-processes with GD (2× upscale, greyscale, auto-contrast, sharpen), runs tesseract CLI with ita+eng PSM-6, extracts date with multi-pattern regex (DD/MM/YYYY, MM/YYYY, ISO, named-month), returns YYYY-MM-DD + confidence
- geminiReadExpiry() now: (1) tries Tesseract first; (2) falls back to Gemini Vision if OCR returns null or no date found; (3) passes source ('ocr'|'gemini') in response

@xenova/transformers embedding classifier (browser-side):
- index.html: ES-module bootstrap that lazy-loads 'Xenova/all-MiniLM-L6-v2' quantized (~23 MB, cached in browser) via window._getCategoryPipeline(); pre-warms on first scan page visit
- assets/js/app.js: classifyCategoryByEmbedding(name) — embeds product name + 16 category anchor descriptions, cosine similarity, threshold 0.30; results cached in _embeddingCache Map
- autoDetectCategory(): after keyword map misses, fires classifyCategoryByEmbedding async and updates select when resolved (respects manuallySet flag)
- createQuickProduct(): if regex returned 'altro', silently patches category with embedding result via a background api call
2026-05-03 13:17:14 +00:00
dadaloop82 c814d99d1f feat: smart use-all context, scale baseline reset, freezer-ok alert suppression, conf qty fix, low-stock finish button
- submitUseAll() now detects opened packages: if current location has an opened pack, finishes only that; if exactly one opened pack exists elsewhere, uses it automatically; multiple opened packs → disambiguation modal
- quickUse() resets scale baseline on page open so stale weight doesn't immediately trigger auto-fill
- Expired alerts (dashboard + banner) now filter out freezer items within their safety window (level='ok')
- Review banner: conf unit quantity displayed as sub-unit total (e.g. 800g) instead of raw pack count; high-qty threshold evaluated on sub-unit volume to prevent '400 confezioni' nonsense
- Low-stock review banner gains 'È finito tutto' button → new bannerFinishAll() handler
- New _submitUseAllAt() helper and _showUseAllDisambiguation() modal
- New translation keys: toast_opened_finished, disambiguation_hint, disambiguation_all, banner_review_action_finish (it/en/de)
2026-05-03 13:12:35 +00:00
dadaloop82 4e583127dd Banner: suppress low-qty alert when sibling product entries exist elsewhere
A partially-used fridge entry (e.g. 191 ml of milk) triggered a
'suspiciously low quantity' banner even when sealed packages of the
same product were present in another location (e.g. pantry).

Fix: before pushing a low-qty review alert, group all inventory rows
by product key (barcode, or name+brand fallback). If any sibling entry
for the same product has qty > 0 in a different row, skip the alert.
High-qty and suspicious package-size alerts are unaffected.
2026-04-30 05:28:43 +00:00
dadaloop82 8359b14931 Banner: adapt expired icon/color/title to safety level (non-alarmist)
- ok level (long-life/freezer safe): green banner,  icon, 'Scaduto (ancora ok)'
- warning level: amber banner, 👀 icon, 'Scaduto (controlla)'
- danger level: unchanged red 🚫 banner
- Added banner-expired-ok / banner-expired-warning CSS variants
- Added expiry.expired_suffix_ok / expired_suffix_warning i18n keys (IT/EN/DE)
- Updated README and CHANGELOG
2026-04-30 05:21:50 +00:00
dadaloop82 42c0896e7b Merge branch 'develop' 2026-04-29 17:15:06 +00:00
dadaloop82 9249a2f936 README: document anti-waste report, opened products panel, freezer shelf-life rules 2026-04-29 17:15:04 +00:00
dadaloop82 e4d71f6409 Merge branch 'develop' 2026-04-29 17:11:35 +00:00
dadaloop82 2ec9b5d6c0 Freezer shelf-life: replace flat 90d rule with granular per-product estimates (USDA/EFSA); AI+cache still takes priority 2026-04-29 17:11:27 +00:00
dadaloop82 2980a150e4 Merge branch 'develop' 2026-04-29 17:05:47 +00:00
dadaloop82 8d02e76501 Remove interval% from annual waste info; fix conf whole-qty using package expiry not opened shelf-life 2026-04-29 17:05:38 +00:00
dadaloop82 c4f963dbd8 Merge branch 'develop' 2026-04-29 17:02:20 +00:00
dadaloop82 e71ef3aba3 Dashboard: move waste-chart above expiring; fix opened-items conf split, expiry cache, AI validation, MAX_SHOWN 20; remove DupliClick from README 2026-04-29 17:02:10 +00:00
dadaloop82 abf42059ad Merge branch 'develop' 2026-04-29 16:52:46 +00:00
dadaloop82 3c9fe7dfea Remove all Dupliclick/Spesa integration; merge annual waste info into status line 2026-04-29 16:52:36 +00:00
dadaloop82 0cf64ccca1 Merge branch 'develop' 2026-04-29 16:27:15 +00:00
dadaloop82 9c1346019c Waste section: neutral terminology, drop trend-cards & meals badge, annual/range in bar, 5-min facts, AW facts in screensaver 2026-04-29 16:27:05 +00:00
dadaloop82 8938ae517f Merge branch 'develop' 2026-04-29 16:20:05 +00:00
dadaloop82 67f58172e5 Waste: bigger fonts, auto-fit badge row, 5-min rotation 2026-04-29 16:19:54 +00:00
dadaloop82 6372db6cb6 Merge branch 'develop' 2026-04-29 16:13:45 +00:00
dadaloop82 85274948b4 Waste section: single stacked comparison bar instead of two rows 2026-04-29 16:13:43 +00:00
dadaloop82 c98f5d47bb Merge branch 'develop' 2026-04-29 15:27:02 +00:00
dadaloop82 da46fec174 Fix anomaly banner when expected_qty is negative (untracked initial stock) 2026-04-29 15:26:59 +00:00
dadaloop82 bcf0a8927d Merge branch 'develop' 2026-04-29 06:42:24 +00:00
dadaloop82 22266cb620 Fix sealed/opened expiry; AI shelf-life cache; redesign waste UI 2026-04-29 06:42:21 +00:00
dadaloop82 60e6f3c09c Merge branch 'develop' 2026-04-29 06:28:48 +00:00
dadaloop82 e002955173 Anti-waste: daily food-facts API, 3-badge rotating row with fade 2026-04-29 06:28:46 +00:00
dadaloop82 cd76f5bcdd Merge branch 'develop' 2026-04-29 06:19:38 +00:00
dadaloop82 7c4dd99289 Anti-waste: themed border, rich info badges, fix latte di montagna shelf-life, exclude opened from expiring_soon 2026-04-29 06:19:35 +00:00
dadaloop82 f1129b97f2 Merge branch 'develop' 2026-04-29 06:11:55 +00:00