feat: add logic to create integration using code

This commit is contained in:
Fritz Heiden 2025-03-19 14:41:10 +01:00
parent 035393578f
commit 7a871f9fa4
13 changed files with 112 additions and 49 deletions

View File

@ -50,6 +50,8 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.cio)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

View File

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"

View File

@ -1,9 +0,0 @@
package com.example.tvcontroller
import android.util.Log
object DeviceService {
fun createIntegration(name: String, code: String) {
Log.i("DeviceService", "Creating integration for $name with code $code")
}
}

View File

@ -14,10 +14,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -31,8 +29,8 @@ import com.example.tvcontroller.ui.theme.TVControllerTheme
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.tvcontroller.services.BluetoothService
import com.example.tvcontroller.ui.AppViewModel

View File

@ -1,6 +1,5 @@
package com.example.tvcontroller
import android.util.Log
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@ -13,10 +12,6 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -25,13 +20,7 @@ import com.example.tvcontroller.ui.AppViewModel
@Composable
fun SettingsScreen(appViewModel: AppViewModel = viewModel()) {
var serverUrl by remember { mutableStateOf("") }
var deviceName by remember { mutableStateOf(android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL) }
var registrationCode by remember { mutableStateOf("") }
fun connect() {
Log.i("SettingsScreen", "Save settings: $serverUrl, $deviceName, $registrationCode")
}
val viewModel = viewModel<SettingsViewModel>()
Column(
modifier = Modifier
@ -54,23 +43,23 @@ fun SettingsScreen(appViewModel: AppViewModel = viewModel()) {
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = serverUrl,
onValueChange = { serverUrl = it },
label = { Text(stringResource(id = R.string.server_url_label)) }
value = viewModel.serverAddress,
onValueChange = viewModel::onServerAddressChanged,
label = { Text(stringResource(id = R.string.server_address_label)) }
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = deviceName,
onValueChange = { deviceName = it },
value = viewModel.deviceName,
onValueChange = viewModel::onDeviceNameChanged,
label = { Text(stringResource(id = R.string.device_name_label)) }
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = registrationCode,
onValueChange = { registrationCode = it },
value = viewModel.registrationCode,
onValueChange = viewModel::onRegistrationCodeChanged,
label = { Text(stringResource(id = R.string.registration_code_label)) }
)
OutlinedButton(onClick = { connect() }, modifier = Modifier.fillMaxWidth()) {
OutlinedButton(onClick = { viewModel.connect() }, modifier = Modifier.fillMaxWidth()) {
Text(
stringResource(id = R.string.connect_button_label)
)
@ -81,9 +70,15 @@ fun SettingsScreen(appViewModel: AppViewModel = viewModel()) {
style = MaterialTheme.typography.headlineSmall
)
if (appViewModel.isBluetoothEnabled()) {
Text(text = "Controller settings: Bluetooth is enabled.", style = MaterialTheme.typography.bodyMedium)
Text(
text = "Controller settings: Bluetooth is enabled.",
style = MaterialTheme.typography.bodyMedium
)
} else {
Text(text = "Bluetooth is disabled. Please enable it in settings.", style = MaterialTheme.typography.bodyMedium)
Text(
text = "Bluetooth is disabled. Please enable it in settings.",
style = MaterialTheme.typography.bodyMedium
)
}
}
}

View File

@ -0,0 +1,38 @@
package com.example.tvcontroller
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.tvcontroller.services.DeviceService
import kotlinx.coroutines.launch
class SettingsViewModel : ViewModel() {
var serverAddress by mutableStateOf(DeviceService.getServerAddress())
private set
var deviceName by mutableStateOf(android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL)
private set
var registrationCode by mutableStateOf("")
private set
fun connect() {
//Log.i("SettingsScreen", "Save settings: $serverUrl, $deviceName, $registrationCode")
viewModelScope.launch {
DeviceService.setServerAddress(serverAddress)
DeviceService.createIntegration(deviceName, registrationCode)
}
}
fun onServerAddressChanged(url: String) {
serverAddress = url
}
fun onDeviceNameChanged(name: String) {
deviceName = name
}
fun onRegistrationCodeChanged(code: String) {
registrationCode = code
}
}

View File

@ -1,14 +1,11 @@
package com.example.tvcontroller
package com.example.tvcontroller.services
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.IntentFilter
import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.ComponentActivity
import androidx.core.content.ContextCompat.getSystemService
class BluetoothService(private val context: Context) {

View File

@ -0,0 +1,46 @@
package com.example.tvcontroller.services
import android.util.Log
import io.ktor.client.engine.cio.*
import io.ktor.client.*
import io.ktor.client.call.body
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.HttpMethod
import org.json.JSONObject
object DeviceService {
private var client = HttpClient(CIO)
private var serverAddress: String = ""
suspend fun createIntegration(name: String, code: String) {
Log.i("DeviceService", "Creating integration for $name with code $code at $serverAddress")
val json = JSONObject()
json.put("name", name)
json.put("code", code)
try {
val response: HttpResponse = client.request("http://$serverAddress/api/integrations") {
method = HttpMethod.Post
setBody(json.toString())
headers {
append("Content-Type", "application/json")
}
}
val body: String = response.body()
Log.i("DeviceService", "Response: ${response.status.value} $body")
} catch (e: Exception) {
Log.e("DeviceService", "Error creating integration", e)
}
}
fun setServerAddress(url: String) {
serverAddress = url
}
fun getServerAddress(): String {
return serverAddress
}
}

View File

@ -3,5 +3,5 @@ package com.example.tvcontroller.ui
import com.example.tvcontroller.Screen
data class AppUiState(
val currentScreen: Screen = Screen.Camera,
val currentScreen: Screen = Screen.Settings,
)

View File

@ -1,16 +1,7 @@
package com.example.tvcontroller.ui
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.tvcontroller.BluetoothService
import com.example.tvcontroller.Screen
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
class AppViewModel() : ViewModel() {
private var isBluetoothEnabled = mutableStateOf(false)

View File

@ -10,4 +10,5 @@
<string name="connection_state_disconnected">disconnected</string>
<string name="save_button_label">Save</string>
<string name="connect_button_label">Connect</string>
<string name="server_address_label">Server address</string>
</resources>

View File

@ -20,4 +20,4 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.nonTransitiveRClass=true

View File

@ -5,6 +5,7 @@ coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
ktor = "3.1.0"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
composeBom = "2024.04.01"
@ -26,6 +27,8 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }