Commit Graph

424 Commits

Author SHA1 Message Date
github-actions[bot] 74abb73912 chore: auto-merge develop → main
Triggered by: 874ae90 fix(settings): fix screensaver toggle (add toggle-slider, correct layout)
2026-05-04 16:54:40 +00:00
dadaloop82 874ae90afa fix(settings): fix screensaver toggle (add toggle-slider, correct layout)
- Replace checkbox-label with toggle-row pattern matching other toggles
- Add missing <span class="toggle-slider"></span> inside toggle-switch
- Add data-i18n attributes to card title and hint
- Add screensaver.card_title / card_hint translations in all 3 locales

feat(demo): full demo mode implementation
- _applyDemoModeUI(): set _geminiAvailable=true + call _updateGeminiButtonState()
- api(): no-op all bring_add/bring_remove/bring_set_spec calls in demo mode
- api(): return in-memory shoppingItems for bring_list in demo mode
- loadShoppingList(): show placeholder list in demo mode, skip all Bring! calls

fix(shopping): graceful Bring! missing credentials handling
- Show friendly message with link to settings instead of raw PHP error
- Add shopping.bring_not_configured i18n key in IT/EN/DE

Bump app.js cache buster to v=20260504b
2026-05-04 16:52:22 +00:00
github-actions[bot] e53d6f78b5 chore: auto-merge develop → main
Triggered by: 4df06b0 feat(kiosk): language selection as first setup step + screensaver step
2026-05-04 16:49:35 +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
github-actions[bot] c14f1927af chore: auto-merge develop → main
Triggered by: 8d56d4a fix(settings): use correct toggle-switch markup for screensaver checkbox
2026-05-04 16:42:36 +00:00
dadaloop82 8d56d4a221 fix(settings): use correct toggle-switch markup for screensaver checkbox 2026-05-04 16:40:47 +00:00
github-actions[bot] 52d4a0e23d chore: auto-merge develop → main
Triggered by: ab647f3 fix(cache): bump app.js version to force browser cache refresh
2026-05-04 16:40:32 +00:00
dadaloop82 ab647f38d6 fix(cache): bump app.js version to force browser cache refresh 2026-05-04 16:38:54 +00:00
github-actions[bot] a6e8136ba0 chore: auto-merge develop → main
Triggered by: d9602df fix(settings): ReferenceError s not defined in _populateLanguageSelector
2026-05-04 16:37:42 +00:00
github-actions[bot] 9b83acdef6 chore: auto-merge develop → main
Triggered by: 1fb00d4 fix(shopping): prevent cleanup from removing user-manually-added items
2026-05-04 16:36:26 +00:00
dadaloop82 d9602df3c3 fix(settings): ReferenceError s not defined in _populateLanguageSelector
Screensaver toggle init was incorrectly placed inside _populateLanguageSelector()
where 's' (getSettings()) is not in scope. Moved to loadSettingsUI() alongside
the other preference checkboxes where 's = getSettings()' is already defined.
2026-05-04 16:35:54 +00:00
dadaloop82 1fb00d48a9 fix(shopping): prevent cleanup from removing user-manually-added items
- cleanupObsoleteBringItems now protects items the user explicitly added
  from the suggestions panel via a '_userPinnedBring' localStorage set
  (30-day TTL, cleared on force-sync)
- cleanup now protects ALL smart-predicted items (any urgency), not only
  critical/high — if the algorithm still flags it, it should stay in list
- autoAddCriticalItems: bypass purchased-blocklist for depleted items
  (current_qty=0) so products that ran out are always re-added to Bring
- forceSyncBring also clears _userPinnedBring for a full reset
2026-05-04 16:34:34 +00:00
github-actions[bot] 922e633ec9 chore: auto-merge develop → main
Triggered by: 108f3ef feat(settings): add screensaver toggle in Language tab (default off)
2026-05-04 16:19:21 +00:00
dadaloop82 108f3ef283 feat(settings): add screensaver toggle in Language tab (default off)
Toggle appears in the Language settings tab, below the language selector.
Default: disabled. When disabled, initInactivityWatcher() exits early so
the screensaver never activates. i18n added for it/en/de.
2026-05-04 16:17:36 +00:00
github-actions[bot] d4d1aca774 chore: auto-merge develop → main
Triggered by: bc0beea fix(header): full redesign — left-aligned title, uniform buttons, update badge
2026-05-04 16:14:28 +00:00
dadaloop82 bc0beea090 fix(header): full redesign — left-aligned title, uniform buttons, update badge
- Title always left-aligned (was centered via 3-col flex trick)
- In kiosk mode: exit/refresh buttons appear left of title via header-left
- All action buttons unified as .header-btn (42×42px, consistent style)
- Scan button: 48×48px + pulse animation to stand out from others
- Gemini button: no longer misuses header-scan-btn class; own indigo tint
- Scale status: same 42×42px .header-btn shape with colored .scale-dot
  inside instead of a tiny 22px standalone circle
- Update notification: uses #header-update-badge beside the title instead
  of replacing title innerHTML (title never disappears anymore)
- Fixed _scaleUpdateStatus() to preserve header-btn class on className reset
2026-05-04 16:12:43 +00:00
github-actions[bot] 56584e07df chore: auto-merge develop → main
Triggered by: 04cba79 chore: update GitHub issue reporter token
2026-05-04 16:08:18 +00:00
dadaloop82 04cba79519 chore: update GitHub issue reporter token 2026-05-04 16:06:12 +00:00
github-actions[bot] 5cb1799d1d chore: auto-merge develop → main
Triggered by: e68d11a fix(pwa): handle orientation.lock() promise rejection silently
2026-05-04 15:48:11 +00:00
dadaloop82 e68d11a7fc fix(pwa): handle orientation.lock() promise rejection silently
screen.orientation.lock() returns a Promise; the old try/catch only
caught synchronous errors, leaving the rejection unhandled and triggering
the auto-reporter (issue #8). Added .catch(()=>{}) to suppress it.
Also fixed CI: add sleep+retry around gh release create to avoid 502
race condition after delete.

Closes #8
2026-05-04 15:46:17 +00:00
github-actions[bot] 74a60f5bbf chore: auto-merge develop → main
Triggered by: 3649be8 feat(kiosk): add screensaver toggle in settings (default off)
2026-05-04 15:44:27 +00:00
dadaloop82 3649be848a feat(kiosk): add screensaver toggle in settings (default off) 2026-05-04 15:42:51 +00:00
github-actions[bot] 644fa2b94f chore: auto-merge develop → main
Triggered by: fa9c52e feat(kiosk): complete setup wizard overhaul
2026-05-04 15:42:31 +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
github-actions[bot] b06beb23be chore: auto-merge develop → main
Triggered by: f625e55 fix: header 3-col layout, shopping_name migration, demo mode UI, kiosk buttons left
2026-05-04 12:54:08 +00:00
dadaloop82 f625e55526 fix: header 3-col layout, shopping_name migration, demo mode UI, kiosk buttons left
Header layout:
- Redesign header-content as 3-column flex (left / center / right)
- Add #header-left div: dedicated slot for kiosk buttons (empty by default)
- header-title: flex-shrink auto, no more position:absolute centering hack
- header-actions: flex:1 1 0 + justify-content:flex-end (right)
- header-left: flex:1 1 0 (left) — equal width balances the title visually

Kiosk exit/refresh buttons:
- _injectKioskOverlay() now appends to #header-left instead of
  insertBefore(firstChild) — buttons appear on LEFT, not mixed with center

DB migration:
- Add shopping_name TEXT DEFAULT '' to CREATE TABLE products schema
- Add ALTER TABLE migration in migrateDB() for existing databases
- Avoids repeated ALTER TABLE in seed code on every request

Demo mode UI:
- _applyDemoModeUI(): hides ⚙️ settings nav button in demo mode
- Suppresses first-run setup wizard when _demoMode === true
- Shows a small DEMO badge in header-left
- Called from both syncSettingsFromDB() and _initApp()
2026-05-04 12:52:29 +00:00
github-actions[bot] 04efbe29b3 chore: auto-merge develop → main
Triggered by: 77c2bd5 docs: update README with AI features, security hardening, new .env keys
2026-05-04 06:24:59 +00:00
dadaloop82 77c2bd59a7 docs: update README with AI features, security hardening, new .env keys 2026-05-04 06:23:18 +00:00
github-actions[bot] 13e88bc5b8 chore: auto-merge develop → main
Triggered by: bf27469 security: fix 3 critical vulnerabilities
2026-05-04 06:22:04 +00:00
dadaloop82 bf27469228 security: fix 3 critical vulnerabilities
1. Remove raw API key from get_settings response
   - getServerSettings() no longer returns gemini_key in plain text
   - Only gemini_key_set (boolean) and settings_token_set (boolean)
   - JS updated to only check gemini_key_set (removes stale gemini_key fallback)

2. Protect save_settings with SETTINGS_TOKEN
   - If SETTINGS_TOKEN is set in .env, all save_settings calls must
     include matching X-Settings-Token header (uses hash_equals)
   - Empty token = no protection (backwards-compatible default)
   - Settings UI (Security tab) has a token input field
   - Wrong/missing token returns HTTP 403 with error 'unauthorized'
   - JS shows '🔒 Token non valido o mancante' on 403

3. DEMO_MODE native blocking in PHP
   - DEMO_MODE=false added to .env (default off)
   - When DEMO_MODE=true, all write actions return HTTP 403 before routing
   - Blocked: save_settings, product_save/delete/merge, inventory_add/use/update/remove,
     dismiss_anomaly, bring_add/remove/sync
   - demo_mode flag exposed via get_settings so JS can adapt UI
2026-05-04 06:20:23 +00:00
github-actions[bot] 60c1f406cc chore: auto-merge develop → main
Triggered by: 529c09f feat(ai): 3 new AI features — product storage hint, shopping tips, anomaly explain
2026-05-04 06:03:40 +00:00
dadaloop82 529c09fda3 feat(ai): 3 new AI features — product storage hint, shopping tips, anomaly explain
Feature 1: AI product storage/shelf-life hint
- New API: gemini_product_hint → {location, expiry_days, reason}
- After opening the add form, Gemini suggests optimal storage and expiry
- Shown inline next to expiry estimate as a subtle AI badge with tooltip
- Also updates location buttons if AI suggests a different location
- Cached permanently in food_facts_cache.json (per name+lang)

Feature 2: AI-enriched shopping suggestions
- New API: gemini_shopping_enrich → adds tip field to each suggestion
- After bring_suggest renders, Gemini adds practical buying/storing tips
- Tips shown inline under each suggestion item in indigo italic text
- Cached per item list + lang in food_facts_cache.json

Feature 3: AI anomaly explanation
- New API: gemini_anomaly_explain → plain-language explanation
- '🤖 Spiega' button added to anomaly banners (when Gemini available)
- Explains in 2-3 conversational sentences why the discrepancy likely happened
- Replaces technical banner detail text with friendly explanation
- No caching (anomaly context is always specific)
2026-05-04 06:01:44 +00:00
github-actions[bot] 1b7fe58769 chore: auto-merge develop → main
Triggered by: a85390b feat(ai): guard all Gemini features when API key is not configured
2026-05-04 05:52:14 +00:00
dadaloop82 a85390b498 feat(ai): guard all Gemini features when API key is not configured
Added _geminiAvailable global flag (false by default):
- Set in _initApp() from serverSettings.gemini_key_set after app loads
- Updated in syncSettingsFromDB() so it stays current if key is added later

Added _requireGemini() helper:
- Returns true if Gemini key is configured → proceed normally
- Returns false + shows a warning toast if key is missing → abort

Added _updateGeminiButtonState():
- Adds .header-btn-no-ai CSS class to Gemini button when key is missing:
  greyed out, slight grayscale filter, amber dot badge in corner
- Updates button tooltip to explain what to do
- Removes class/restores normal appearance when key is present

All 6 AI entry points now call _requireGemini() as first line:
  captureForAI()          — AI product identification from scan page
  captureForAIFormFill()  — AI product fill in manual add form
  scanExpiryWithAI()      — AI expiry date reader
  openRecipeDialog()      — recipe generation dialog
  generateRecipe()        — recipe generation (direct call path)
  quickRecipeSuggestion() — quick expiring-products recipe (→ chat)
  showPage('chat')        — Gemini chat page

Previously: user would click the button, camera would open, API call
would fail, and only THEN see an error message deep in the flow.
Now: blocked immediately at the entry point with a clear toast.
2026-05-04 05:50:30 +00:00
github-actions[bot] 9e078c9930 chore: auto-merge develop → main
Triggered by: d635635 fix(ui): update notification inline in header title area, no full-page banner
2026-05-04 05:43:20 +00:00
dadaloop82 d635635577 fix(ui): update notification inline in header title area, no full-page banner
Instead of a fixed banner that covers the top of the page, the update
notification now replaces only the header title area (the centered title):
- .header-title content is swapped in-place with an animated pill:
    ⬆️ v1.x.x  [Aggiorna]  ✕
- Pulsing animation (header-update-pulse) draws attention without being
  intrusive; camera and Gemini buttons stay exactly where they are
- [Aggiorna] button does window.location.reload()
- [✕] dismisses: for a release update stores publishedAt so it won't reappear;
  for a server deploy simply restores title (reappears next 5-min check)
- Auto-restores after 60 s without marking as seen
- Removed the old fixed position:fixed banner entirely
2026-05-04 05:41:38 +00:00
github-actions[bot] bb77395a3a chore: auto-merge develop → main
Triggered by: 968e26c fix(ui): header title always centered, actions to right, real-time update detection
2026-05-04 05:34:37 +00:00
dadaloop82 968e26cc6a fix(ui): header title always centered, actions to right, real-time update detection
CSS header fixes:
- .header-content: justify-content:flex-end so .header-actions (camera, Gemini)
  naturally stays at the right edge as a flex child
- .header-title: removed overflow:hidden and text-overflow:ellipsis that were
  clipping the version number; title stays absolutely centered
- Cleaned up unused max-width:none and margin:0 from previous broken attempt

Real-time webapp update detection:
- Added module-level _loadedVersion captured at page load (version in HTML header)
- _checkWebappUpdate() now has two checks:
  1. webapp_version from server vs _loadedVersion: if different, the server was
     updated since this page was loaded → show '🔄 Nuova versione disponibile' banner
  2. GitHub latest release vs _loadedVersion (existing behaviour)
  Different banner messages: deploy-changed shows simple reload prompt;
  release-newer shows version + changelog link (same as before)
- TTL reduced from 6h to 5 min so updates are detected quickly
- _checkWebappUpdate() now also fires on visibilitychange so the user sees
  the banner as soon as they return to the tab after a deploy
2026-05-04 05:32:57 +00:00
github-actions[bot] 84229a4345 chore: auto-merge develop → main
Triggered by: 6756b16 fix(ui): center header title on tablet + add skeleton loader to spesa stat card
2026-05-04 05:28:11 +00:00
dadaloop82 6756b16ecb fix(ui): center header title on tablet + add skeleton loader to spesa stat card
Header title centering:
- .header-content: remove max-width:600px, use position:relative + justify-content:center
- .header-title: position:absolute; left:50%; transform:translateX(-50%)
  so the title is always at the exact center of the header regardless of
  screen width or how many action buttons are on the right
- Added max-width:calc(100% - 200px) to prevent overlap with action buttons
  on narrow screens

Spesa skeleton preloader:
- index.html: add stat-loading class to stat-spesa (was missing, other 3 had it)
- app.js showPage('dashboard'): add 'spesa' to the skeleton init array
- app.js loadShoppingCount(): remove stat-loading class after data loads
  (like loadDashboard() does for the other 3 locations)
2026-05-04 05:26:38 +00:00
github-actions[bot] e12a1ebde1 chore: auto-merge develop → main
Triggered by: c6e3d13 fix(api): bringAddItems() missing $input/$items decode — always returned 0
2026-05-04 05:22:36 +00:00
dadaloop82 c6e3d13e8c fix(api): bringAddItems() missing $input/$items decode — always returned 0
bringAddItems() used $input and $items without ever decoding the request
body. $items was undefined (null) so the foreach never ran, every call
returned added=0 skipped=0 regardless of what was sent.

Added:
  $input = json_decode(file_get_contents('php://input'), true) ?? [];
  $items = $input['items'] ?? [];
Also added the missing $auth guard (consistent with all other Bring functions).
2026-05-04 05:21:07 +00:00
github-actions[bot] 03bd0bb321 chore: auto-merge develop → main
Triggered by: 7c61ae6 fix(kiosk): fix ByteArray type inference error in APK magic-byte check
2026-05-03 20:15:57 +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
github-actions[bot] 653746e913 chore: auto-merge develop → main
Triggered by: 22e506b fix(kiosk): 4 bug fix — uninstall loop, PHP check, APK validation, ErrorReporter init
2026-05-03 20:12:17 +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
github-actions[bot] 7d134f561a chore: auto-merge develop → main
Triggered by: 38eb66c feat(kiosk): server reachability check in step 3 + uninstall-on-generic-failure
2026-05-03 19:37:18 +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
github-actions[bot] a2b1a6f2cf chore: auto-merge develop → main
Triggered by: 15e1dfb fix(kiosk): STATUS_FAILURE=1 (wrong package) + issue version-guard bypass
2026-05-03 19:30:40 +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