feat: properly register device and keep token as cookie

This commit is contained in:
Fritz Heiden 2025-03-31 13:26:42 +02:00
parent cba125738d
commit 5db2caeaa4
4 changed files with 79 additions and 102 deletions

View File

@ -7,18 +7,20 @@ import com.example.tvcontroller.data.Integration
import io.ktor.client.engine.cio.*
import io.ktor.client.*
import io.ktor.client.call.body
import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.client.request.headers
import io.ktor.client.request.request
import io.ktor.client.request.setBody
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HeadersBuilder
import io.ktor.http.HttpMethod
import org.json.JSONObject
private const val SHARED_PREFERENCES_NAME = "devices";
class DeviceService(private val context: Context) {
private var client = HttpClient(CIO)
private var client = HttpClient(CIO) {
install(HttpCookies)
}
private var serverAddress: String = ""
private var token: String = ""
private var deviceId: String = ""
@ -27,13 +29,15 @@ class DeviceService(private val context: Context) {
loadPreferences()
}
suspend fun createIntegration(name: String, code: String) {
suspend fun registerIntegration(name: String, code: String) {
Log.i("DeviceService", "Creating integration for $name with code $code at $serverAddress")
val requestJson = JSONObject()
requestJson.put("name", name)
requestJson.put("code", code)
token = ""
deviceId = ""
try {
val response: HttpResponse = client.request("http://$serverAddress/api/integrations") {
val response: HttpResponse = client.request("http://$serverAddress/api/integrations/register") {
method = HttpMethod.Post
setBody(requestJson.toString())
headers {
@ -54,7 +58,7 @@ class DeviceService(private val context: Context) {
Log.i("DeviceService", "Response: ${response.status.value} $body")
} catch (e: Exception) {
Log.e("DeviceService", "Error creating integration", e)
Log.e("DeviceService", "Error registering integration", e)
}
}

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@ -31,22 +32,24 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.tvcontroller.R
import com.example.tvcontroller.Settings
import com.example.tvcontroller.SettingsViewModel
import com.example.tvcontroller.SettingsViewModel.Companion.CONNECT_CONTROLLER_VIEW
import com.example.tvcontroller.SettingsViewModel.Companion.MAIN_SETTINGS_VIEW
import com.example.tvcontroller.ui.views.SettingsViewModel
import com.example.tvcontroller.ui.views.SettingsViewModel.Companion.CONNECT_CONTROLLER_VIEW
import com.example.tvcontroller.ui.views.SettingsViewModel.Companion.MAIN_SETTINGS_VIEW
import com.example.tvcontroller.services.BluetoothService
import com.example.tvcontroller.services.ControllerService
import com.example.tvcontroller.services.DeviceService
import com.example.tvcontroller.webrtc.RtcPeerConnection
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsView(
deviceService: DeviceService,
bluetoothService: BluetoothService
bluetoothService: BluetoothService,
rtcPeerConnection: RtcPeerConnection
) {
val viewModel = viewModel<SettingsViewModel>(
factory = SettingsViewModel.provideFactory(
deviceService, bluetoothService
deviceService, bluetoothService, rtcPeerConnection
)
)
val navController = rememberNavController()
@ -102,25 +105,29 @@ fun SettingsView(
text = "Controller status: " + getBluetoothConnectionStateString(viewModel.bluetoothConnectionState) + ".",
style = MaterialTheme.typography.bodyMedium
)
if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED)
OutlinedButton(
onClick = viewModel::disconnectBluetoothDevice,
modifier = Modifier.fillMaxWidth()
) {
Text(
stringResource(id = R.string.disconnect_button_label)
)
}
else
OutlinedButton(
onClick = { navController.navigate(CONNECT_CONTROLLER_VIEW.toString()) },
enabled = viewModel.bluetoothConnectionState != BluetoothService.STATE_OFF,
modifier = Modifier.fillMaxWidth()
) {
Text(
stringResource(id = R.string.connect_button_label)
)
}
if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED) OutlinedButton(
onClick = viewModel::disconnectBluetoothDevice, modifier = Modifier.fillMaxWidth()
) {
Text(
stringResource(id = R.string.disconnect_button_label)
)
}
else OutlinedButton(
onClick = { navController.navigate(CONNECT_CONTROLLER_VIEW.toString()) },
enabled = viewModel.bluetoothConnectionState != BluetoothService.STATE_OFF,
modifier = Modifier.fillMaxWidth()
) {
Text(
stringResource(id = R.string.connect_button_label)
)
}
Button(
onClick = { viewModel.connectRtcPeerConnection() },
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Connect RTC Peer")
}
}
}
@ -151,33 +158,25 @@ fun SettingsView(
)
pairedDevices.value.forEach { device ->
if (device == null) return
ListItem(
headlineContent = { Text(device.getName()) },
supportingContent = {
if (device.getName() != device.getAddress()) Text(
device.getAddress()
)
},
modifier = Modifier.clickable(onClick = {
viewModel.connectBluetoothDevice(
device
)
}),
trailingContent = {
if (device == viewModel.currentBluetoothDevice)
if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED)
Icon(
painterResource(id = R.drawable.baseline_check_24),
contentDescription = "state"
)
else
CircularProgressIndicator(
modifier = Modifier.width(16.dp),
color = MaterialTheme.colorScheme.secondary,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
}
)
ListItem(headlineContent = { Text(device.getName()) }, supportingContent = {
if (device.getName() != device.getAddress()) Text(
device.getAddress()
)
}, modifier = Modifier.clickable(onClick = {
viewModel.connectBluetoothDevice(
device
)
}), trailingContent = {
if (device == viewModel.currentBluetoothDevice) if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED) Icon(
painterResource(id = R.drawable.baseline_check_24),
contentDescription = "state"
)
else CircularProgressIndicator(
modifier = Modifier.width(16.dp),
color = MaterialTheme.colorScheme.secondary,
trackColor = MaterialTheme.colorScheme.surfaceVariant,
)
})
}
}
}

View File

@ -1,24 +1,29 @@
package com.example.tvcontroller
package com.example.tvcontroller.ui.views
import android.os.Build
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.example.tvcontroller.Settings
import com.example.tvcontroller.data.BluetoothDevice
import com.example.tvcontroller.services.BluetoothService
import com.example.tvcontroller.services.DeviceService
import com.example.tvcontroller.webrtc.RtcPeerConnection
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class SettingsViewModel(
private val deviceService: DeviceService,
private val bluetoothService: BluetoothService
private val bluetoothService: BluetoothService,
private val rtcPeerConnection: RtcPeerConnection
) : ViewModel() {
var serverAddress by mutableStateOf(deviceService.getServerAddress())
private set
var deviceName by mutableStateOf(android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL)
var deviceName by mutableStateOf(Build.MANUFACTURER + " " + Build.MODEL)
private set
var registrationCode by mutableStateOf("")
private set
@ -46,15 +51,22 @@ class SettingsViewModel(
//Log.i("SettingsScreen", "Save settings: $serverUrl, $deviceName, $registrationCode")
viewModelScope.launch {
deviceService.setServerAddress(serverAddress)
deviceService.createIntegration(deviceName, registrationCode)
deviceService.registerIntegration(deviceName, registrationCode)
updateConnectionState()
updateDeviceInfo()
}
}
fun connectRtcPeerConnection() {
viewModelScope.launch(Dispatchers.IO) {
rtcPeerConnection.connect()
}
}
private fun updateConnectionState() {
Log.i("SettingsViewModel", "Device token: ${deviceService.getToken()}")
connectionState = if (deviceService.getToken().isEmpty()) {
Settings.ConnectionState.Unregistered
return
} else {
Settings.ConnectionState.Registered
}
@ -100,11 +112,12 @@ class SettingsViewModel(
fun provideFactory(
deviceService: DeviceService,
bluetoothService: BluetoothService
bluetoothService: BluetoothService,
rtcPeerConnection: RtcPeerConnection
) = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return SettingsViewModel(deviceService, bluetoothService) as T
return SettingsViewModel(deviceService, bluetoothService, rtcPeerConnection) as T
}
}
}

View File

@ -1,39 +0,0 @@
functionname,protocol,device,subdevice,function
INPUT SOURCE,NECx2,7,7,1
POWER,NECx2,7,7,2
1,NECx2,7,7,4
2,NECx2,7,7,5
3,NECx2,7,7,6
VOLUME +,NECx2,7,7,7
4,NECx2,7,7,8
5,NECx2,7,7,9
6,NECx2,7,7,10
VOLUME -,NECx2,7,7,11
7,NECx2,7,7,12
8,NECx2,7,7,13
9,NECx2,7,7,14
MUTE,NECx2,7,7,15
CHANNEL -,NECx2,7,7,16
0,NECx2,7,7,17
CHANNEL +,NECx2,7,7,18
LAST,NECx2,7,7,19
MENU,NECx2,7,7,26
INFO,NECx2,7,7,31
AD/SUBT,NECx2,7,7,37
EXIT,NECx2,7,7,45
E-MANUAL,NECx2,7,7,63
TOOLS,NECx2,7,7,75
GUIDE,NECx2,7,7,79
RETURN,NECx2,7,7,88
CURSOR UP,NECx2,7,7,96
CURSOR DOWN,NECx2,7,7,97
CURSOR RIGHT,NECx2,7,7,98
CURSOR LEFT,NECx2,7,7,101
ENTER,NECx2,7,7,104
CH LIST,NECx2,7,7,107
SMART HUB,NECx2,7,7,121
3D,NECx2,7,7,159
HDMI2,NECx2,7,7,190
HDMI3,NECx2,7,7,194
HDMI4,NECx2,7,7,197
HDMI1,NECx2,7,7,233
1 functionname protocol device subdevice function
2 INPUT SOURCE NECx2 7 7 1
3 POWER NECx2 7 7 2
4 1 NECx2 7 7 4
5 2 NECx2 7 7 5
6 3 NECx2 7 7 6
7 VOLUME + NECx2 7 7 7
8 4 NECx2 7 7 8
9 5 NECx2 7 7 9
10 6 NECx2 7 7 10
11 VOLUME - NECx2 7 7 11
12 7 NECx2 7 7 12
13 8 NECx2 7 7 13
14 9 NECx2 7 7 14
15 MUTE NECx2 7 7 15
16 CHANNEL - NECx2 7 7 16
17 0 NECx2 7 7 17
18 CHANNEL + NECx2 7 7 18
19 LAST NECx2 7 7 19
20 MENU NECx2 7 7 26
21 INFO NECx2 7 7 31
22 AD/SUBT NECx2 7 7 37
23 EXIT NECx2 7 7 45
24 E-MANUAL NECx2 7 7 63
25 TOOLS NECx2 7 7 75
26 GUIDE NECx2 7 7 79
27 RETURN NECx2 7 7 88
28 CURSOR UP NECx2 7 7 96
29 CURSOR DOWN NECx2 7 7 97
30 CURSOR RIGHT NECx2 7 7 98
31 CURSOR LEFT NECx2 7 7 101
32 ENTER NECx2 7 7 104
33 CH LIST NECx2 7 7 107
34 SMART HUB NECx2 7 7 121
35 3D NECx2 7 7 159
36 HDMI2 NECx2 7 7 190
37 HDMI3 NECx2 7 7 194
38 HDMI4 NECx2 7 7 197
39 HDMI1 NECx2 7 7 233