feat: properly register device and keep token as cookie
This commit is contained in:
parent
cba125738d
commit
5db2caeaa4
@ -7,18 +7,20 @@ import com.example.tvcontroller.data.Integration
|
|||||||
import io.ktor.client.engine.cio.*
|
import io.ktor.client.engine.cio.*
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.plugins.cookies.HttpCookies
|
||||||
import io.ktor.client.request.headers
|
import io.ktor.client.request.headers
|
||||||
import io.ktor.client.request.request
|
import io.ktor.client.request.request
|
||||||
import io.ktor.client.request.setBody
|
import io.ktor.client.request.setBody
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import io.ktor.http.HeadersBuilder
|
|
||||||
import io.ktor.http.HttpMethod
|
import io.ktor.http.HttpMethod
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
private const val SHARED_PREFERENCES_NAME = "devices";
|
private const val SHARED_PREFERENCES_NAME = "devices";
|
||||||
|
|
||||||
class DeviceService(private val context: Context) {
|
class DeviceService(private val context: Context) {
|
||||||
private var client = HttpClient(CIO)
|
private var client = HttpClient(CIO) {
|
||||||
|
install(HttpCookies)
|
||||||
|
}
|
||||||
private var serverAddress: String = ""
|
private var serverAddress: String = ""
|
||||||
private var token: String = ""
|
private var token: String = ""
|
||||||
private var deviceId: String = ""
|
private var deviceId: String = ""
|
||||||
@ -27,13 +29,15 @@ class DeviceService(private val context: Context) {
|
|||||||
loadPreferences()
|
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")
|
Log.i("DeviceService", "Creating integration for $name with code $code at $serverAddress")
|
||||||
val requestJson = JSONObject()
|
val requestJson = JSONObject()
|
||||||
requestJson.put("name", name)
|
requestJson.put("name", name)
|
||||||
requestJson.put("code", code)
|
requestJson.put("code", code)
|
||||||
|
token = ""
|
||||||
|
deviceId = ""
|
||||||
try {
|
try {
|
||||||
val response: HttpResponse = client.request("http://$serverAddress/api/integrations") {
|
val response: HttpResponse = client.request("http://$serverAddress/api/integrations/register") {
|
||||||
method = HttpMethod.Post
|
method = HttpMethod.Post
|
||||||
setBody(requestJson.toString())
|
setBody(requestJson.toString())
|
||||||
headers {
|
headers {
|
||||||
@ -54,7 +58,7 @@ class DeviceService(private val context: Context) {
|
|||||||
|
|
||||||
Log.i("DeviceService", "Response: ${response.status.value} $body")
|
Log.i("DeviceService", "Response: ${response.status.value} $body")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e("DeviceService", "Error creating integration", e)
|
Log.e("DeviceService", "Error registering integration", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@ -31,22 +32,24 @@ import androidx.navigation.compose.composable
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.example.tvcontroller.R
|
import com.example.tvcontroller.R
|
||||||
import com.example.tvcontroller.Settings
|
import com.example.tvcontroller.Settings
|
||||||
import com.example.tvcontroller.SettingsViewModel
|
import com.example.tvcontroller.ui.views.SettingsViewModel
|
||||||
import com.example.tvcontroller.SettingsViewModel.Companion.CONNECT_CONTROLLER_VIEW
|
import com.example.tvcontroller.ui.views.SettingsViewModel.Companion.CONNECT_CONTROLLER_VIEW
|
||||||
import com.example.tvcontroller.SettingsViewModel.Companion.MAIN_SETTINGS_VIEW
|
import com.example.tvcontroller.ui.views.SettingsViewModel.Companion.MAIN_SETTINGS_VIEW
|
||||||
import com.example.tvcontroller.services.BluetoothService
|
import com.example.tvcontroller.services.BluetoothService
|
||||||
import com.example.tvcontroller.services.ControllerService
|
import com.example.tvcontroller.services.ControllerService
|
||||||
import com.example.tvcontroller.services.DeviceService
|
import com.example.tvcontroller.services.DeviceService
|
||||||
|
import com.example.tvcontroller.webrtc.RtcPeerConnection
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsView(
|
fun SettingsView(
|
||||||
deviceService: DeviceService,
|
deviceService: DeviceService,
|
||||||
bluetoothService: BluetoothService
|
bluetoothService: BluetoothService,
|
||||||
|
rtcPeerConnection: RtcPeerConnection
|
||||||
) {
|
) {
|
||||||
val viewModel = viewModel<SettingsViewModel>(
|
val viewModel = viewModel<SettingsViewModel>(
|
||||||
factory = SettingsViewModel.provideFactory(
|
factory = SettingsViewModel.provideFactory(
|
||||||
deviceService, bluetoothService
|
deviceService, bluetoothService, rtcPeerConnection
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
@ -102,25 +105,29 @@ fun SettingsView(
|
|||||||
text = "Controller status: " + getBluetoothConnectionStateString(viewModel.bluetoothConnectionState) + ".",
|
text = "Controller status: " + getBluetoothConnectionStateString(viewModel.bluetoothConnectionState) + ".",
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED)
|
if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED) OutlinedButton(
|
||||||
OutlinedButton(
|
onClick = viewModel::disconnectBluetoothDevice, modifier = Modifier.fillMaxWidth()
|
||||||
onClick = viewModel::disconnectBluetoothDevice,
|
) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
Text(
|
||||||
) {
|
stringResource(id = R.string.disconnect_button_label)
|
||||||
Text(
|
)
|
||||||
stringResource(id = R.string.disconnect_button_label)
|
}
|
||||||
)
|
else OutlinedButton(
|
||||||
}
|
onClick = { navController.navigate(CONNECT_CONTROLLER_VIEW.toString()) },
|
||||||
else
|
enabled = viewModel.bluetoothConnectionState != BluetoothService.STATE_OFF,
|
||||||
OutlinedButton(
|
modifier = Modifier.fillMaxWidth()
|
||||||
onClick = { navController.navigate(CONNECT_CONTROLLER_VIEW.toString()) },
|
) {
|
||||||
enabled = viewModel.bluetoothConnectionState != BluetoothService.STATE_OFF,
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth()
|
stringResource(id = R.string.connect_button_label)
|
||||||
) {
|
)
|
||||||
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 ->
|
pairedDevices.value.forEach { device ->
|
||||||
if (device == null) return
|
if (device == null) return
|
||||||
ListItem(
|
ListItem(headlineContent = { Text(device.getName()) }, supportingContent = {
|
||||||
headlineContent = { Text(device.getName()) },
|
if (device.getName() != device.getAddress()) Text(
|
||||||
supportingContent = {
|
device.getAddress()
|
||||||
if (device.getName() != device.getAddress()) Text(
|
)
|
||||||
device.getAddress()
|
}, modifier = Modifier.clickable(onClick = {
|
||||||
)
|
viewModel.connectBluetoothDevice(
|
||||||
},
|
device
|
||||||
modifier = Modifier.clickable(onClick = {
|
)
|
||||||
viewModel.connectBluetoothDevice(
|
}), trailingContent = {
|
||||||
device
|
if (device == viewModel.currentBluetoothDevice) if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED) Icon(
|
||||||
)
|
painterResource(id = R.drawable.baseline_check_24),
|
||||||
}),
|
contentDescription = "state"
|
||||||
trailingContent = {
|
)
|
||||||
if (device == viewModel.currentBluetoothDevice)
|
else CircularProgressIndicator(
|
||||||
if (viewModel.bluetoothConnectionState == BluetoothService.STATE_CONNECTED)
|
modifier = Modifier.width(16.dp),
|
||||||
Icon(
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
painterResource(id = R.drawable.baseline_check_24),
|
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
contentDescription = "state"
|
)
|
||||||
)
|
})
|
||||||
else
|
|
||||||
CircularProgressIndicator(
|
|
||||||
modifier = Modifier.width(16.dp),
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
trackColor = MaterialTheme.colorScheme.surfaceVariant,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.tvcontroller.Settings
|
||||||
import com.example.tvcontroller.data.BluetoothDevice
|
import com.example.tvcontroller.data.BluetoothDevice
|
||||||
import com.example.tvcontroller.services.BluetoothService
|
import com.example.tvcontroller.services.BluetoothService
|
||||||
import com.example.tvcontroller.services.DeviceService
|
import com.example.tvcontroller.services.DeviceService
|
||||||
|
import com.example.tvcontroller.webrtc.RtcPeerConnection
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class SettingsViewModel(
|
class SettingsViewModel(
|
||||||
private val deviceService: DeviceService,
|
private val deviceService: DeviceService,
|
||||||
private val bluetoothService: BluetoothService
|
private val bluetoothService: BluetoothService,
|
||||||
|
private val rtcPeerConnection: RtcPeerConnection
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var serverAddress by mutableStateOf(deviceService.getServerAddress())
|
var serverAddress by mutableStateOf(deviceService.getServerAddress())
|
||||||
private set
|
private set
|
||||||
var deviceName by mutableStateOf(android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL)
|
var deviceName by mutableStateOf(Build.MANUFACTURER + " " + Build.MODEL)
|
||||||
private set
|
private set
|
||||||
var registrationCode by mutableStateOf("")
|
var registrationCode by mutableStateOf("")
|
||||||
private set
|
private set
|
||||||
@ -46,15 +51,22 @@ class SettingsViewModel(
|
|||||||
//Log.i("SettingsScreen", "Save settings: $serverUrl, $deviceName, $registrationCode")
|
//Log.i("SettingsScreen", "Save settings: $serverUrl, $deviceName, $registrationCode")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
deviceService.setServerAddress(serverAddress)
|
deviceService.setServerAddress(serverAddress)
|
||||||
deviceService.createIntegration(deviceName, registrationCode)
|
deviceService.registerIntegration(deviceName, registrationCode)
|
||||||
updateConnectionState()
|
updateConnectionState()
|
||||||
|
updateDeviceInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connectRtcPeerConnection() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
rtcPeerConnection.connect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateConnectionState() {
|
private fun updateConnectionState() {
|
||||||
|
Log.i("SettingsViewModel", "Device token: ${deviceService.getToken()}")
|
||||||
connectionState = if (deviceService.getToken().isEmpty()) {
|
connectionState = if (deviceService.getToken().isEmpty()) {
|
||||||
Settings.ConnectionState.Unregistered
|
Settings.ConnectionState.Unregistered
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
Settings.ConnectionState.Registered
|
Settings.ConnectionState.Registered
|
||||||
}
|
}
|
||||||
@ -100,11 +112,12 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
fun provideFactory(
|
fun provideFactory(
|
||||||
deviceService: DeviceService,
|
deviceService: DeviceService,
|
||||||
bluetoothService: BluetoothService
|
bluetoothService: BluetoothService,
|
||||||
|
rtcPeerConnection: RtcPeerConnection
|
||||||
) = object : ViewModelProvider.Factory {
|
) = object : ViewModelProvider.Factory {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||||
return SettingsViewModel(deviceService, bluetoothService) as T
|
return SettingsViewModel(deviceService, bluetoothService, rtcPeerConnection) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user