Compare commits

..

2 Commits

Author SHA1 Message Date
2f90fd7b09 feat: use camerax with webrtc lib 2025-04-01 19:15:03 +02:00
7471168a21 feat: connect to server on app launch 2025-04-01 19:11:03 +02:00
2 changed files with 38 additions and 24 deletions

View File

@ -5,7 +5,6 @@ import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.camera.core.ImageAnalysis
import androidx.camera.view.CameraController import androidx.camera.view.CameraController
import androidx.camera.view.LifecycleCameraController import androidx.camera.view.LifecycleCameraController
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -16,29 +15,26 @@ import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.tvcontroller.ui.theme.TVControllerTheme
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.tvcontroller.services.BluetoothService import com.example.tvcontroller.services.BluetoothService
import com.example.tvcontroller.services.CameraService import com.example.tvcontroller.services.CameraService
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.ui.theme.TVControllerTheme
import com.example.tvcontroller.ui.views.CameraView import com.example.tvcontroller.ui.views.CameraView
import com.example.tvcontroller.ui.views.RemoteView import com.example.tvcontroller.ui.views.RemoteView
import com.example.tvcontroller.ui.views.SettingsView import com.example.tvcontroller.ui.views.SettingsView
import com.example.tvcontroller.webrtc.CameraXCapturer
import com.example.tvcontroller.webrtc.RtcPeerConnection import com.example.tvcontroller.webrtc.RtcPeerConnection
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -71,6 +67,10 @@ class MainActivity : ComponentActivity() {
cameraService = CameraService(applicationContext) cameraService = CameraService(applicationContext)
controllerService = ControllerService(applicationContext, bluetoothService) controllerService = ControllerService(applicationContext, bluetoothService)
checkPermissions() checkPermissions()
lifecycleScope.launch(Dispatchers.IO) {
deviceService.initialize()
}
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
TVControllerTheme { TVControllerTheme {
@ -164,8 +164,7 @@ fun TvControllerApp(
composable(route = Screen.Settings.name) { composable(route = Screen.Settings.name) {
SettingsView( SettingsView(
deviceService = deviceService, deviceService = deviceService,
bluetoothService = bluetoothService, bluetoothService = bluetoothService
rtcPeerConnection = rtcPeerConnection
) )
} }
} }

View File

@ -10,10 +10,13 @@ import io.ktor.client.call.body
import io.ktor.client.plugins.cookies.HttpCookies import io.ktor.client.plugins.cookies.HttpCookies
import io.ktor.client.plugins.websocket.WebSockets import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.webSocket import io.ktor.client.plugins.websocket.webSocket
import io.ktor.client.request.HttpRequestBuilder
import io.ktor.client.request.cookie
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.Cookie
import io.ktor.http.HttpMethod import io.ktor.http.HttpMethod
import io.ktor.websocket.Frame import io.ktor.websocket.Frame
import io.ktor.websocket.readText import io.ktor.websocket.readText
@ -25,15 +28,18 @@ private const val TAG = "DeviceService"
class DeviceService(private val context: Context) { class DeviceService(private val context: Context) {
private var client = HttpClient(CIO) { private var client = HttpClient(CIO) {
install(HttpCookies)
install(WebSockets) install(WebSockets)
} }
private var serverAddress: String = "" private var serverAddress: String = ""
private var token: String = "" private var token: String = ""
private var deviceId: String = "" private var deviceId: String = ""
init { suspend fun initialize() {
loadPreferences() loadPreferences()
if (token.isEmpty()) return
getIntegration()?.let {
connect()
}
} }
suspend fun registerIntegration(name: String, code: String) { suspend fun registerIntegration(name: String, code: String) {
@ -44,12 +50,14 @@ class DeviceService(private val context: Context) {
token = "" token = ""
deviceId = "" deviceId = ""
try { try {
val response: HttpResponse = client.request("http://$serverAddress/api/integrations/register") { val response: HttpResponse =
client.request("http://$serverAddress/api/integrations/register") {
method = HttpMethod.Post method = HttpMethod.Post
setBody(requestJson.toString()) setBody(requestJson.toString())
headers { headers {
append("Content-Type", "application/json") append("Content-Type", "application/json")
} }
cookie(name = "token", value = token)
} }
val body: String = response.body() val body: String = response.body()
@ -78,6 +86,7 @@ class DeviceService(private val context: Context) {
headers { headers {
append("Authorization", "Bearer $token") append("Authorization", "Bearer $token")
} }
cookie(name = "token", value = token)
} }
val body: String = response.body() val body: String = response.body()
@ -120,11 +129,17 @@ class DeviceService(private val context: Context) {
fun connect() { fun connect() {
Log.i(TAG, "Connecting to websocket at $serverAddress") Log.i(TAG, "Connecting to websocket at $serverAddress")
runBlocking { runBlocking {
// split server address into host and port
val (host, port) = serverAddress.split(":") val (host, port) = serverAddress.split(":")
// if no port is specified, assume 80
val portInt = if (port.isEmpty()) 80 else port.toInt() val portInt = if (port.isEmpty()) 80 else port.toInt()
client.webSocket(method = HttpMethod.Get, host = host, port = portInt, path = "/ws") { client.webSocket(
method = HttpMethod.Get,
host = host,
port = portInt,
path = "/ws",
request = {
cookie(name = "token", value = token)
}
) {
Log.i(TAG, "Listening for incoming websocket messages") Log.i(TAG, "Listening for incoming websocket messages")
while (true) { while (true) {
val frame = incoming.receive() val frame = incoming.receive()