diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5651002..aee9c35 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 38b7a03..5f93a75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + () 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 + ) } } } diff --git a/app/src/main/java/com/example/tvcontroller/SettingsViewModel.kt b/app/src/main/java/com/example/tvcontroller/SettingsViewModel.kt new file mode 100644 index 0000000..ff29e14 --- /dev/null +++ b/app/src/main/java/com/example/tvcontroller/SettingsViewModel.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/tvcontroller/BluetoothService.kt b/app/src/main/java/com/example/tvcontroller/services/BluetoothService.kt similarity index 94% rename from app/src/main/java/com/example/tvcontroller/BluetoothService.kt rename to app/src/main/java/com/example/tvcontroller/services/BluetoothService.kt index 12de817..6fe8601 100644 --- a/app/src/main/java/com/example/tvcontroller/BluetoothService.kt +++ b/app/src/main/java/com/example/tvcontroller/services/BluetoothService.kt @@ -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) { diff --git a/app/src/main/java/com/example/tvcontroller/services/DeviceService.kt b/app/src/main/java/com/example/tvcontroller/services/DeviceService.kt new file mode 100644 index 0000000..5ad2bb7 --- /dev/null +++ b/app/src/main/java/com/example/tvcontroller/services/DeviceService.kt @@ -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 + } +} diff --git a/app/src/main/java/com/example/tvcontroller/ui/AppUiState.kt b/app/src/main/java/com/example/tvcontroller/ui/AppUiState.kt index 722d8f5..449ae6d 100644 --- a/app/src/main/java/com/example/tvcontroller/ui/AppUiState.kt +++ b/app/src/main/java/com/example/tvcontroller/ui/AppUiState.kt @@ -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, ) \ No newline at end of file diff --git a/app/src/main/java/com/example/tvcontroller/ui/AppViewModel.kt b/app/src/main/java/com/example/tvcontroller/ui/AppViewModel.kt index 03551ba..35d0abd 100644 --- a/app/src/main/java/com/example/tvcontroller/ui/AppViewModel.kt +++ b/app/src/main/java/com/example/tvcontroller/ui/AppViewModel.kt @@ -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) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b085a8..942f1aa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,4 +10,5 @@ disconnected Save Connect + Server address \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 20e2a01..132244e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 \ No newline at end of file +android.nonTransitiveRClass=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3fe6ea9..c7146a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" }