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
This commit is contained in:
dadaloop82
2026-05-05 17:31:14 +00:00
parent 9cb29de1f0
commit a5094920bf
5 changed files with 182 additions and 91 deletions
+105 -29
View File
@@ -1,58 +1,84 @@
# EverShelf Kiosk
Android kiosk app for wall-mounted kitchen tablets. A pure full-screen WebView wrapper that displays the EverShelf web interface in immersive mode — no BLE, no gateway, just the web app locked to screen.
Android kiosk app for wall-mounted kitchen tablets. Full-screen WebView wrapper with integrated BLE scale gateway — no external apps required.
> **Version:** 1.2.0 (versionCode 3)
> **Version:** 1.6.0 (versionCode 10)
> **Package:** `it.dadaloop.evershelf.kiosk`
> **Min SDK:** Android 7.0 (API 24)
---
## Features
### Kiosk Mode
- **Full-screen WebView** — immersive mode hides status bar and navigation bar
- **True kiosk lock** — screen pinning (`startLockTask`) blocks home/recent/back buttons
- **3-step setup wizard** — Welcome → Server URL (with connection test) → Scale Gateway detection
- **Gateway auto-launch** — starts [EverShelf Scale Gateway](../evershelf-scale-gateway/) in background on boot
- **Exit button (✕)** — visible in header, requires confirmation dialog to exit kiosk
- **Hard refresh (↻)** — clears WebView cache to pick up web app updates instantly
- **Camera & microphone** — runtime permission handling for barcode scanning and voice
- **SSL support** — accepts self-signed certificates for local HTTPS servers
- **Splash screen** — branded 1.5-second splash on startup
- **Update notifications** — checks GitHub releases every 6 hours, shows auto-dismiss banner
- **Native TTS bridge** — cooking mode voice readout uses Android TextToSpeech directly
- **Settings activity** — change server URL, test connection, re-run setup wizard
- **Error recovery** — retry page when server is unreachable
### BLE Scale Gateway (integrated, no external app)
- **Built-in BLE gateway** — `GatewayService` foreground service handles BLE scanning and connection automatically when a scale is configured
- **WebSocket server** — exposes scale data on `ws://127.0.0.1:8765`, fully protocol-compatible with the legacy standalone gateway app (no webapp JS changes needed)
- **Auto-start** — service starts automatically on kiosk launch if a scale device is configured
- **Auto-reconnect** — reconnects automatically after 8 seconds if the BLE link drops
- **Multi-protocol** — supports Bluetooth SIG Weight Scale (`0x181D`/`0x2A9D`), Body Composition (`0x181B`/`0x2A9C`), QN/Yolanda scales, and 100+ models via generic fallback heuristic
### Setup Wizard (6 steps)
1. **Language** — choose Italian / English / German
2. **Welcome** — intro and privacy information
3. **Permissions** — camera, microphone, BLE permissions with in-wizard grant flow
4. **Server URL** — enter your EverShelf URL; auto-discovery scans the LAN (60 parallel threads, ports 80/443/8080/8443)
5. **Smart Scale** — optional: scan for BLE scales and select yours from the discovered device list (mandatory before proceeding if you choose "yes")
6. **Screensaver** — toggle display sleep after inactivity
---
## Architecture
```
KioskActivity (WebView — full-screen EverShelf)
├── Setup wizard (3 steps, shown on first launch only)
├── SetupActivity (6-step wizard, shown on first launch only)
├── SettingsActivity (URL, scale status, re-run wizard)
├── Immersive mode (SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
├── Screen pinning (startLockTask / stopLockTask)
├── JS bridge (_kioskBridge: exit, hardReload)
── Header injection (✕ exit + ↻ refresh buttons)
└── Gateway launcher (launches gateway APK in background)
↓ FLAG_ACTIVITY_NEW_TASK
EverShelf Scale Gateway (separate app, runs in background)
── GatewayService (foreground service — BLE + WebSocket)
├── BleScaleManager — BLE scanning, GATT, auto-reconnect
├── GatewayWebSocketServer — WebSocket server :8765
└── ScaleProtocol — multi-protocol BLE weight parser
```
The kiosk app does **not** contain any BLE or scale code. Scale functionality is handled entirely by the separate [EverShelf Scale Gateway](../evershelf-scale-gateway/) app.
The kiosk app is fully self-contained. No separate gateway app is required.
---
## Setup
1. Install both APKs on your Android tablet:
- **EverShelf Kiosk** (this app)
- **[EverShelf Scale Gateway](../evershelf-scale-gateway/)** (optional, for smart scale support)
2. Launch the kiosk app — the setup wizard starts automatically
3. Enter your EverShelf server URL (e.g. `https://192.168.1.100/dispensa`)
4. The wizard tests the connection and detects the gateway app
5. Done — the web app loads in full-screen kiosk mode
1. Install the **EverShelf Kiosk** APK on your Android tablet
2. Launch the app — the setup wizard starts automatically
3. Choose your language
4. Grant camera, microphone and Bluetooth permissions when prompted
5. Enter your EverShelf server URL (e.g. `https://192.168.1.100/dispensa`) or use auto-discovery
6. If you have a Bluetooth scale: tap **"Sì, ho una bilancia"**, wait for the BLE scan, then tap your scale in the list
7. Done — the web app loads in full-screen kiosk mode
### Scale Configuration
BLE scale setup happens inside the kiosk app itself — **no external app needed**:
- During the **setup wizard (step 5)**, the app scans for nearby BLE scales and shows them in a list. Devices most likely to be scales are marked with ⭐.
- Tap a device to select it. The selection is saved and the "Next" button becomes enabled.
- From the **Settings screen**, you can restart the BLE service or reconfigure the scale device.
### Exiting Kiosk Mode
Tap the **✕** button in the header (left of the title). A confirmation dialog appears — tap "Esci" to exit.
Tap the **✕** button in the header. A confirmation dialog appears — tap "Esci" to exit.
### Triple-tap (Developer)
Triple-tap the setup wizard title to access hidden settings.
---
## Permissions
@@ -60,12 +86,57 @@ Triple-tap the setup wizard title to access hidden settings.
|---|---|
| `INTERNET` | Load EverShelf web app |
| `ACCESS_NETWORK_STATE` | Check connectivity |
| `ACCESS_WIFI_STATE` | WiFi status |
| `ACCESS_WIFI_STATE` | LAN subnet detection for auto-discovery |
| `WAKE_LOCK` | Keep screen on |
| `CAMERA` | Barcode scanning, AI photo identification |
| `RECORD_AUDIO` | Voice input in chat assistant |
| `READ_MEDIA_IMAGES` / `READ_EXTERNAL_STORAGE` | Image access for AI scan |
| `REORDER_TASKS` | Bring kiosk to foreground after gateway launch |
| `REORDER_TASKS` | Bring kiosk to foreground |
| `BLUETOOTH` / `BLUETOOTH_ADMIN` | BLE (Android ≤ 11) |
| `BLUETOOTH_SCAN` / `BLUETOOTH_CONNECT` | BLE scan and connect (Android 12+) |
| `ACCESS_FINE_LOCATION` | Required for BLE scan on Android < 12 |
| `FOREGROUND_SERVICE` | Run BLE gateway as foreground service |
| `FOREGROUND_SERVICE_CONNECTED_DEVICE` | Service type for BLE (Android 14+) |
---
## Supported Scale Protocols
| Protocol | Service UUID | Notes |
|---|---|---|
| **Bluetooth SIG Weight Scale** | `0x181D` / char `0x2A9D` | Most compatible |
| **Bluetooth SIG Body Composition** | `0x181B` / char `0x2A9C` | Weight + body fat %, BMI |
| **QN/Yolanda** | Custom UUIDs | Xiaomi Mi Scale 2, Renpho, etc. |
| **Generic fallback** | Any notifiable characteristic | Auto-heuristic parsing for 100+ models |
### Verified compatible scales
- Xiaomi Mi Body Composition Scale 2
- Renpho Smart Body Fat Scale
- INEVIFIT Smart Body Fat Scale
- Any [openScale-compatible scale](https://github.com/oliexdev/openScale/wiki/Supported-scales)
---
## WebSocket Protocol
The built-in WebSocket server speaks the same protocol as the legacy standalone gateway app — the EverShelf webapp needs no changes.
**Server → client:**
```json
{"type":"status","state":"connected","device":"Mi Scale 2","battery":85}
{"type":"status","state":"disconnected"}
{"type":"weight","value":72.50,"unit":"kg","stable":true,"timestamp":1712345678000}
{"type":"pong"}
```
**Client → server:**
```json
{"type":"get_status"}
{"type":"get_weight"}
{"type":"ping"}
```
---
## Building
@@ -75,17 +146,22 @@ cd evershelf-kiosk
# APK at app/build/outputs/apk/debug/app-debug.apk
```
For release builds:
For release:
```bash
./gradlew assembleRelease
```
---
## Requirements
- Android 7.0+ (API 24)
- Bluetooth LE support (for scale integration)
- Network access to EverShelf server
- EverShelf Scale Gateway app (optional, for smart scale support)
---
## License
GPLv3 — see [LICENSE](../LICENSE)
MIT — see [LICENSE](../LICENSE)
@@ -3,8 +3,7 @@ package it.dadaloop.evershelf.kiosk
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.WindowManager
import android.widget.EditText
@@ -13,6 +12,7 @@ import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.button.MaterialButton
import com.google.android.material.switchmaterial.SwitchMaterial
import it.dadaloop.evershelf.kiosk.scale.GatewayService
import java.net.URL
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
@@ -29,7 +29,7 @@ class SettingsActivity : AppCompatActivity() {
private const val KEY_URL = "evershelf_url"
private const val KEY_SETUP_COMPLETE = "setup_complete"
private const val KEY_SCREENSAVER = "screensaver_enabled"
private const val GATEWAY_PACKAGE = "it.dadaloop.evershelf.scalegate"
private const val KEY_HAS_SCALE = "has_scale"
}
override fun attachBaseContext(newBase: Context) {
@@ -51,53 +51,58 @@ class SettingsActivity : AppCompatActivity() {
val switchScreensaver = findViewById<SwitchMaterial>(R.id.switchScreensaver)
switchScreensaver.isChecked = prefs.getBoolean(KEY_SCREENSAVER, false)
// Gateway status
val gatewayInstalled = try {
packageManager.getPackageInfo(GATEWAY_PACKAGE, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
val statusView = findViewById<TextView>(R.id.scaleGatewayStatus)
val deviceView = findViewById<TextView>(R.id.scaleDeviceInfo)
if (gatewayInstalled) {
statusView.text = "Installed"
statusView.setTextColor(0xFF34d399.toInt())
deviceView.text = "EverShelf Scale Gateway app is installed"
} else {
statusView.text = "Not installed"
statusView.setTextColor(0xFFfbbf24.toInt())
deviceView.text = "Install the Scale Gateway app to use a Bluetooth scale"
}
// ── Smart Scale (BLE gateway service) ──────────────────────────────
val hasScale = prefs.getBoolean(KEY_HAS_SCALE, false)
val deviceName = prefs.getString("scale_device_name", null)
val deviceAddr = prefs.getString("scale_device_address", null)
val statusView = findViewById<TextView>(R.id.scaleGatewayStatus)
val deviceView = findViewById<TextView>(R.id.scaleDeviceInfo)
val btnScaleAction = findViewById<MaterialButton>(R.id.btnConfigureGateway)
val btnConfigureGateway = findViewById<MaterialButton>(R.id.btnConfigureGateway)
if (gatewayInstalled) {
btnConfigureGateway.visibility = android.view.View.VISIBLE
btnConfigureGateway.setOnClickListener {
val intent = packageManager.getLaunchIntentForPackage(GATEWAY_PACKAGE)
if (intent != null) startActivity(intent)
}
// Probe WebSocket port in background to show live status
Thread {
val running = try {
java.net.Socket().use { s ->
s.connect(java.net.InetSocketAddress("127.0.0.1", 8765), 1200); true
}
} catch (_: Exception) { false }
runOnUiThread {
if (running) {
statusView.text = "Attivo ✅"
statusView.setTextColor(0xFF34d399.toInt())
deviceView.text = "Gateway in ascolto su ws://127.0.0.1:8765"
} else {
statusView.text = "Installato, non avviato ⚠️"
statusView.setTextColor(0xFFfbbf24.toInt())
deviceView.text = "Premi \"Apri Gateway\" per avviarlo e configurarlo"
}
when {
!hasScale || deviceAddr == null -> {
statusView.text = "Non configurata"
statusView.setTextColor(0xFF94a3b8.toInt())
deviceView.text = "Nessuna bilancia configurata — riesegui il wizard per aggiungerne una"
btnScaleAction.visibility = android.view.View.VISIBLE
btnScaleAction.text = "⚙️ Configura bilancia"
btnScaleAction.setOnClickListener {
prefs.edit().putBoolean(KEY_SETUP_COMPLETE, false).apply()
startActivity(Intent(this, SetupActivity::class.java))
finish()
}
}.start()
} else {
btnConfigureGateway.visibility = android.view.View.GONE
}
else -> {
statusView.text = "Configurata"
statusView.setTextColor(0xFF34d399.toInt())
deviceView.text = deviceName ?: deviceAddr
btnScaleAction.visibility = android.view.View.VISIBLE
btnScaleAction.text = "🔄 Riavvia servizio bilancia"
btnScaleAction.setOnClickListener {
GatewayService.stop(this)
GatewayService.start(this)
Toast.makeText(this, "Servizio bilancia riavviato", Toast.LENGTH_SHORT).show()
}
// Probe WebSocket port to show live status
Thread {
val running = try {
java.net.Socket().use { s ->
s.connect(java.net.InetSocketAddress("127.0.0.1", 8765), 1200); true
}
} catch (_: Exception) { false }
runOnUiThread {
if (running) {
statusView.text = "Attivo ✅"
statusView.setTextColor(0xFF34d399.toInt())
deviceView.text = "${deviceName ?: "Bilancia"} — ws://127.0.0.1:8765"
} else {
statusView.text = "Non avviato ⚠️"
statusView.setTextColor(0xFFfbbf24.toInt())
deviceView.text = "${deviceName ?: "Bilancia"} — servizio non in esecuzione"
}
}
}.start()
}
}
// Back
@@ -95,7 +95,7 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="SMART SCALE"
android:text="BILANCIA SMART"
android:textColor="#7c3aed"
android:textSize="12sp"
android:textStyle="bold"
@@ -121,7 +121,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Scale Gateway"
android:text="Servizio bilancia BLE"
android:textColor="#cbd5e1"
android:textSize="14sp" />
@@ -146,7 +146,7 @@
android:id="@+id/btnConfigureGateway"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="⚙️ Apri Gateway per configurarlo"
android:text="⚙️ Configura bilancia"
android:textSize="13sp"
android:textAllCaps="false"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"