diff --git a/android/app/src/main/java/com/remotedisplay/player/data/ServerConfig.kt b/android/app/src/main/java/com/remotedisplay/player/data/ServerConfig.kt index f93c1e6..28a3d49 100644 --- a/android/app/src/main/java/com/remotedisplay/player/data/ServerConfig.kt +++ b/android/app/src/main/java/com/remotedisplay/player/data/ServerConfig.kt @@ -33,6 +33,10 @@ class ServerConfig(context: Context) { get() = prefs.getString("device_id", "") ?: "" set(value) = prefs.edit().putString("device_id", value).apply() + var deviceToken: String + get() = prefs.getString("device_token", "") ?: "" + set(value) = prefs.edit().putString("device_token", value).apply() + var deviceName: String get() = prefs.getString("device_name", "Unnamed Display") ?: "Unnamed Display" set(value) = prefs.edit().putString("device_name", value).apply() @@ -47,6 +51,14 @@ class ServerConfig(context: Context) { prefs.edit().putBoolean("is_paired", paired).apply() } + fun clearDeviceCredentials() { + prefs.edit() + .remove("device_id") + .remove("device_token") + .remove("is_paired") + .apply() + } + fun clear() { prefs.edit().clear().apply() } diff --git a/android/app/src/main/java/com/remotedisplay/player/service/WebSocketService.kt b/android/app/src/main/java/com/remotedisplay/player/service/WebSocketService.kt index 0908255..b6a1ce9 100644 --- a/android/app/src/main/java/com/remotedisplay/player/service/WebSocketService.kt +++ b/android/app/src/main/java/com/remotedisplay/player/service/WebSocketService.kt @@ -102,15 +102,25 @@ class WebSocketService : Service() { val data = args[0] as JSONObject val newDeviceId = data.getString("device_id") config.deviceId = newDeviceId + // Persist device_token (issued on first register, or refreshed on reconnect) + if (data.has("device_token")) { + config.deviceToken = data.getString("device_token") + } Log.i("WebSocketService", "Registered as: $newDeviceId") handler.post { onRegistered?.invoke(newDeviceId) } startHeartbeat() } on("device:unpaired") { - Log.w("WebSocketService", "Device not found on server - clearing config") - config.setPaired(false) - config.deviceId = "" + Log.w("WebSocketService", "Device not found on server - clearing credentials") + config.clearDeviceCredentials() + handler.post { onUnpaired?.invoke() } + } + + on("device:auth-error") { args -> + val msg = (args.firstOrNull() as? JSONObject)?.optString("error", "Authentication failed") ?: "Authentication failed" + Log.w("WebSocketService", "Device auth rejected: $msg — clearing credentials for re-pair") + config.clearDeviceCredentials() handler.post { onUnpaired?.invoke() } } @@ -234,6 +244,11 @@ class WebSocketService : Service() { val data = JSONObject().apply { if (config.isProvisioned && config.isPaired) { put("device_id", config.deviceId) + // Send device_token for authentication (may be empty for legacy devices) + val token = config.deviceToken + if (token.isNotEmpty()) { + put("device_token", token) + } } else { // Generate a pairing code if we don't have one val pairingCode = (100000..999999).random().toString() @@ -279,6 +294,10 @@ class WebSocketService : Service() { // Re-register triggers the server to send current playlist val data = org.json.JSONObject().apply { put("device_id", config.deviceId) + val token = config.deviceToken + if (token.isNotEmpty()) { + put("device_token", token) + } put("device_info", deviceInfo.getDeviceInfo()) } socket?.emit("device:register", data) diff --git a/server/player/index.html b/server/player/index.html index 9bd2838..c8564f6 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -253,6 +253,7 @@ socket.on('device:registered', (data) => { config.deviceId = data.device_id; + if (data.device_token) config.deviceToken = data.device_token; saveConfig(config); console.log('Registered:', data.device_id); @@ -278,6 +279,30 @@ showStatus('Waiting for content...'); }); + socket.on('device:unpaired', () => { + console.warn('Device not found on server — clearing credentials'); + delete config.deviceId; + delete config.deviceToken; + config.paired = false; + saveConfig(config); + document.getElementById('setupScreen').style.display = 'flex'; + document.getElementById('urlForm').style.display = 'block'; + document.getElementById('pairingSection').style.display = 'none'; + document.getElementById('setupStatus').textContent = 'Device was removed from server. Please reconnect.'; + }); + + socket.on('device:auth-error', (data) => { + console.warn('Device auth rejected:', data?.error || 'unknown'); + delete config.deviceId; + delete config.deviceToken; + config.paired = false; + saveConfig(config); + document.getElementById('setupScreen').style.display = 'flex'; + document.getElementById('urlForm').style.display = 'block'; + document.getElementById('pairingSection').style.display = 'none'; + document.getElementById('setupStatus').textContent = 'Authentication failed. Please re-pair this device.'; + }); + socket.on('device:playlist-update', (data) => { console.log('Playlist update:', data.assignments?.length, 'items'); handlePlaylistUpdate(data); @@ -361,6 +386,7 @@ const data = {}; if (config.deviceId && config.paired) { data.device_id = config.deviceId; + if (config.deviceToken) data.device_token = config.deviceToken; } else { const code = String(Math.floor(100000 + Math.random() * 900000)); config.pairingCode = code;