From 754f13111f52b0b7d1d1054e8ee7a9dc18cd5c0b Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Tue, 5 May 2026 18:11:51 +0000 Subject: [PATCH] feat: kiosk setup improvements + webapp scale indicator fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- assets/css/style.css | 15 +- assets/js/app.js | 5 +- .../dadaloop/evershelf/kiosk/SetupActivity.kt | 158 ++++++++++++++++-- .../src/main/res/layout/activity_setup.xml | 158 ++++++++++++++++++ 4 files changed, 315 insertions(+), 21 deletions(-) diff --git a/assets/css/style.css b/assets/css/style.css index 751782d..2e260c0 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -284,18 +284,19 @@ body { width: 9px; height: 9px; border-radius: 50%; - border: 1.5px solid #0f172a; - background: #475569; - transition: background 0.4s, box-shadow 0.4s; + border: 1.5px solid rgba(0,0,0,0.35); + background: #94a3b8; + transition: background 0.4s, box-shadow 0.4s, border-color 0.4s; pointer-events: none; } -.scale-status-connected .scale-status-dot { background: #22c55e; box-shadow: 0 0 5px #22c55eaa; } +/* Connected: white dot with green glow — always visible regardless of header color */ +.scale-status-connected .scale-status-dot { background: #ffffff; border-color: #22c55e; box-shadow: 0 0 6px #22c55e; } .scale-status-connected .scale-icon-emoji { filter: none; opacity: 1; } -.scale-status-searching .scale-status-dot { background: #f59e0b; animation: scaleStatusPulse 1.4s infinite; } +.scale-status-searching .scale-status-dot { background: #f59e0b; border-color: rgba(0,0,0,0.35); animation: scaleStatusPulse 1.4s infinite; } .scale-status-searching .scale-icon-emoji { filter: none; opacity: 0.85; } -.scale-status-disconnected .scale-status-dot { background: #475569; } +.scale-status-disconnected .scale-status-dot { background: #64748b; border-color: rgba(0,0,0,0.35); } .scale-status-disconnected .scale-icon-emoji { filter: grayscale(0.5); opacity: 0.55; } -.scale-status-error .scale-status-dot { background: #ef4444; box-shadow: 0 0 5px #ef4444aa; } +.scale-status-error .scale-status-dot { background: #ef4444; border-color: rgba(0,0,0,0.35); box-shadow: 0 0 5px #ef4444aa; } .scale-status-error .scale-icon-emoji { filter: none; opacity: 1; } @keyframes scaleStatusPulse { 0%, 100% { box-shadow: 0 0 3px #f59e0b88; } diff --git a/assets/js/app.js b/assets/js/app.js index 7bb69bf..489a986 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -12177,13 +12177,14 @@ async function _initApp() { localStorage.removeItem('_bgBringSyncTs'); localStorage.setItem('_bgBringSyncReset_v1', '1'); } - syncSettingsFromDB(); + syncSettingsFromDB().then(() => { + scaleInit(); // connect to smart scale gateway if configured (needs settings) + }); showPage('dashboard'); initInactivityWatcher(); initSpesaMode(); initScreensaverShortcuts(); startBgShoppingRefresh(); - scaleInit(); // connect to smart scale gateway if configured _injectKioskOverlay(); // kiosk X / refresh buttons (only when running inside Android WebView) // Hide preloader once the dashboard is rendered diff --git a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt index 1299374..603379b 100644 --- a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt +++ b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt @@ -87,18 +87,27 @@ class SetupActivity : AppCompatActivity() { // Scale step (BLE) private lateinit var scaleQuestionCard: LinearLayout + private lateinit var scalePowerOnCard: LinearLayout private lateinit var bleSetupCard: LinearLayout + private lateinit var scaleTestCard: LinearLayout private lateinit var tvScanStatus: TextView private lateinit var btnScanBle: MaterialButton private lateinit var tvSelectedScale: TextView private lateinit var rvScaleDevices: RecyclerView private lateinit var step3NextButtons: LinearLayout + private lateinit var tvTestStatus: TextView + private lateinit var tvTestWeight: TextView + private lateinit var testWeightBox: android.widget.LinearLayout private var bleManager: BleScaleManager? = null private val discoveredDevices = mutableListOf() private var selectedDevice: BleDeviceInfo? = null private var deviceAdapter: DeviceAdapter? = null + // Test-mode state + private var isInTestMode = false + private var testHasWeight = false + // Screensaver step private lateinit var setupSwitchScreensaver: SwitchMaterial @@ -176,14 +185,15 @@ class SetupActivity : AppCompatActivity() { override fun onDestroy() { bleManager?.stopScan() bleManager?.disconnect() + isInTestMode = false discoverCancelled.set(true) super.onDestroy() } override fun onResume() { super.onResume() - // If we're on step 4 with a saved device, reflect it in the UI - if (currentStep == 4) { + // If we're on step 4 with a saved device and not in test mode, reflect it in the UI + if (currentStep == 4 && !isInTestMode) { val savedName = bleManager?.getSavedDeviceName() if (savedName != null) { tvSelectedScale.text = "✅ $savedName" @@ -214,12 +224,17 @@ class SetupActivity : AppCompatActivity() { // Scale step scaleQuestionCard = findViewById(R.id.scaleQuestionCard) + scalePowerOnCard = findViewById(R.id.scalePowerOnCard) bleSetupCard = findViewById(R.id.bleSetupCard) + scaleTestCard = findViewById(R.id.scaleTestCard) tvScanStatus = findViewById(R.id.tvScanStatus) btnScanBle = findViewById(R.id.btnScanBle) tvSelectedScale = findViewById(R.id.tvSelectedScale) rvScaleDevices = findViewById(R.id.rvScaleDevices) step3NextButtons = findViewById(R.id.step3NextButtons) + tvTestStatus = findViewById(R.id.tvTestStatus) + tvTestWeight = findViewById(R.id.tvTestWeight) + testWeightBox = findViewById(R.id.testWeightBox) // Screensaver step setupSwitchScreensaver = findViewById(R.id.setupSwitchScreensaver) @@ -279,9 +294,16 @@ class SetupActivity : AppCompatActivity() { findViewById(R.id.btnScaleYes).setOnClickListener { prefs.edit().putBoolean(KEY_HAS_SCALE, true).apply() scaleQuestionCard.visibility = View.GONE + // Show power-on instruction first + scalePowerOnCard.visibility = View.VISIBLE + } + + // "Bilancia accesa — Cerca" button + findViewById(R.id.btnPowerOnReady).setOnClickListener { + scalePowerOnCard.visibility = View.GONE bleSetupCard.visibility = View.VISIBLE step3NextButtons.visibility = View.VISIBLE - // Disable Next until device selected + // Disable Next until device confirmed val savedName = bleManager?.getSavedDeviceName() if (savedName != null) { tvSelectedScale.text = "✅ $savedName" @@ -300,13 +322,63 @@ class SetupActivity : AppCompatActivity() { btnScanBle.setOnClickListener { startBleScan() } findViewById(R.id.btnScaleBack).setOnClickListener { bleManager?.stopScan() + bleManager?.disconnect() + isInTestMode = false showStep(3) } findViewById(R.id.btnScaleNext).setOnClickListener { bleManager?.stopScan() + bleManager?.disconnect() showStep(5) } + // Test card buttons + findViewById(R.id.btnTestConfirm).setOnClickListener { + // User confirms weight matches → scale is good, proceed + isInTestMode = false + bleManager?.stopScan() + bleManager?.disconnect() + scaleTestCard.visibility = View.GONE + bleSetupCard.visibility = View.GONE + step3NextButtons.visibility = View.VISIBLE + val name = selectedDevice?.name ?: bleManager?.getSavedDeviceName() ?: "Bilancia" + tvSelectedScale.text = "✅ $name" + tvSelectedScale.visibility = View.VISIBLE + findViewById(R.id.btnScaleNext).isEnabled = true + // Put scan card back (hidden) and show only nav buttons + bleSetupCard.visibility = View.GONE + } + findViewById(R.id.btnTestRetry).setOnClickListener { + // User says weight doesn't match → disconnect, go back to scan + isInTestMode = false + testHasWeight = false + bleManager?.disconnect() + selectedDevice = null + bleManager?.clearSavedDevice() + scaleTestCard.visibility = View.GONE + testWeightBox.visibility = View.GONE + bleSetupCard.visibility = View.VISIBLE + tvSelectedScale.text = "" + tvSelectedScale.visibility = View.GONE + tvScanStatus.text = "Bilancia non confermata. Riprova la scansione." + tvScanStatus.setTextColor(0xFFfbbf24.toInt()) + btnScanBle.isEnabled = true + btnScanBle.text = "🔍 Cerca bilancia" + findViewById(R.id.btnScaleNext).isEnabled = false + } + findViewById(R.id.btnTestSkip).setOnClickListener { + // User skips test — trust the selection + isInTestMode = false + bleManager?.disconnect() + scaleTestCard.visibility = View.GONE + bleSetupCard.visibility = View.GONE + step3NextButtons.visibility = View.VISIBLE + val name = selectedDevice?.name ?: bleManager?.getSavedDeviceName() ?: "Bilancia" + tvSelectedScale.text = "✅ $name" + tvSelectedScale.visibility = View.VISIBLE + findViewById(R.id.btnScaleNext).isEnabled = true + } + // ── Screensaver ─────────────────────────────────────────────────── findViewById(R.id.btnScreensaverBack).setOnClickListener { showStep(4) } findViewById(R.id.btnScreensaverNext).setOnClickListener { @@ -356,10 +428,15 @@ class SetupActivity : AppCompatActivity() { // Reset scale step when entering it if (step == 4) { + isInTestMode = false + testHasWeight = false + scaleTestCard.visibility = View.GONE + testWeightBox.visibility = View.GONE val hasScaleYes = prefs.contains(KEY_HAS_SCALE) && prefs.getBoolean(KEY_HAS_SCALE, false) if (hasScaleYes) { - // Already said YES — go straight to BLE scan card + // Already said YES — skip power-on card, go straight to BLE scan scaleQuestionCard.visibility = View.GONE + scalePowerOnCard.visibility = View.GONE bleSetupCard.visibility = View.VISIBLE step3NextButtons.visibility = View.VISIBLE val savedName = bleManager?.getSavedDeviceName() @@ -374,6 +451,7 @@ class SetupActivity : AppCompatActivity() { } } else { scaleQuestionCard.visibility = View.VISIBLE + scalePowerOnCard.visibility = View.GONE bleSetupCard.visibility = View.GONE step3NextButtons.visibility = View.GONE } @@ -751,15 +829,33 @@ class SetupActivity : AppCompatActivity() { bleManager?.saveDevice(info.device.address, info.name) tvSelectedScale.text = "✅ ${info.name}" tvSelectedScale.visibility = View.VISIBLE - tvScanStatus.text = "Bilancia selezionata. Premi Avanti per continuare." - tvScanStatus.setTextColor(0xFF34d399.toInt()) btnScanBle.isEnabled = true btnScanBle.text = "🔄 Scansiona di nuovo" - findViewById(R.id.btnScaleNext).isEnabled = true + // Start connection test + startScaleTest(info) + } + + private fun startScaleTest(info: BleDeviceInfo) { + isInTestMode = true + testHasWeight = false + // Show test card, hide scan card + bleSetupCard.visibility = View.GONE + scaleTestCard.visibility = View.VISIBLE + testWeightBox.visibility = View.GONE + step3NextButtons.visibility = View.GONE + tvTestStatus.text = "🔗 Connessione a ${info.name}…" + tvTestStatus.setTextColor(0xFF94a3b8.toInt()) + tvTestWeight.text = "— g" + // Disable confirm/retry until we have data + findViewById(R.id.btnTestConfirm).isEnabled = false + findViewById(R.id.btnTestRetry).isEnabled = false + bleManager?.connect(info.device) } private fun makeBleListener() = object : BleScaleListener { override fun onDeviceFound(info: BleDeviceInfo) { + // Only show devices that look like scales (positive score) + if (info.scaleScore <= 0) return val existing = discoveredDevices.indexOfFirst { it.device.address == info.device.address } if (existing >= 0) { discoveredDevices[existing] = info @@ -769,12 +865,50 @@ class SetupActivity : AppCompatActivity() { deviceAdapter?.notifyItemInserted(discoveredDevices.size - 1) } } - override fun onConnecting(device: BluetoothDevice) {} - override fun onConnected(deviceName: String) {} - override fun onDisconnected() {} - override fun onWeightReceived(reading: WeightReading) {} + override fun onConnecting(device: BluetoothDevice) { + if (!isInTestMode) return + tvTestStatus.text = "🔗 Connessione in corso…" + tvTestStatus.setTextColor(0xFF94a3b8.toInt()) + } + override fun onConnected(deviceName: String) { + if (!isInTestMode) return + tvTestStatus.text = "⚖️ Connesso! Posiziona un oggetto sulla bilancia…" + tvTestStatus.setTextColor(0xFF34d399.toInt()) + testWeightBox.visibility = View.VISIBLE + findViewById(R.id.btnTestRetry).isEnabled = true + } + override fun onDisconnected() { + if (!isInTestMode) return + tvTestStatus.text = "⚠️ Connessione persa. Riprova." + tvTestStatus.setTextColor(0xFFfbbf24.toInt()) + testWeightBox.visibility = View.GONE + testHasWeight = false + findViewById(R.id.btnTestConfirm).isEnabled = false + } + override fun onWeightReceived(reading: WeightReading) { + if (!isInTestMode) return + testHasWeight = true + val display = if (reading.unit == "kg") + "%.3f kg".format(reading.value) + else + "%g ${reading.unit}".format(reading.value) + tvTestWeight.text = display + testWeightBox.visibility = View.VISIBLE + tvTestStatus.text = "Peso ricevuto — coincide con quello sulla bilancia?" + tvTestStatus.setTextColor(0xFF94a3b8.toInt()) + findViewById(R.id.btnTestConfirm).isEnabled = true + findViewById(R.id.btnTestRetry).isEnabled = true + } override fun onBatteryReceived(level: Int) {} override fun onError(message: String) { + if (isInTestMode) { + tvTestStatus.text = "⚠️ $message" + tvTestStatus.setTextColor(0xFFf87171.toInt()) + testWeightBox.visibility = View.GONE + testHasWeight = false + findViewById(R.id.btnTestRetry).isEnabled = true + return + } tvScanStatus.text = "⚠️ $message" tvScanStatus.setTextColor(0xFFf87171.toInt()) btnScanBle.isEnabled = true @@ -782,7 +916,7 @@ class SetupActivity : AppCompatActivity() { override fun onScanStopped() { btnScanBle.isEnabled = true if (discoveredDevices.isEmpty()) { - tvScanStatus.text = "Nessuna bilancia trovata. Assicurati che sia accesa e vicina." + tvScanStatus.text = "Nessuna bilancia trovata. Assicurati che sia accesa e vicina, poi riprova." tvScanStatus.setTextColor(0xFFfbbf24.toInt()) } else { tvScanStatus.text = "Seleziona la tua bilancia dall'elenco." diff --git a/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml b/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml index b6b1fa7..2aa5b73 100644 --- a/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml +++ b/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml @@ -807,6 +807,56 @@ android:textColor="#64748b" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +