diff --git a/app/src/main/kotlin/org/eclipse/kuksa/companion/extension/UriExtension.kt b/app/src/main/kotlin/org/eclipse/kuksa/companion/extension/UriExtension.kt
new file mode 100644
index 0000000..9053574
--- /dev/null
+++ b/app/src/main/kotlin/org/eclipse/kuksa/companion/extension/UriExtension.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+
+package org.eclipse.kuksa.companion.extension
+
+import android.content.Context
+import android.net.Uri
+import android.provider.OpenableColumns
+
+fun Uri.fetchFileName(context: Context): String? {
+ var fileName: String? = null
+ val cursor = context.contentResolver.query(this, null, null, null, null)
+ cursor?.use {
+ val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
+
+ if (cursor.moveToFirst()) {
+ fileName = cursor.getString(nameIndex)
+ }
+ }
+ return fileName
+}
diff --git a/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/CategorySetting.kt b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/CategorySetting.kt
new file mode 100644
index 0000000..88783d1
--- /dev/null
+++ b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/CategorySetting.kt
@@ -0,0 +1,39 @@
+package org.eclipse.kuksa.companion.feature.settings.view
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+
+@Suppress("SameParameterValue") // re-usability
+@Composable
+fun CategorySetting(
+ label: String,
+ modifier: Modifier = Modifier,
+) {
+ Row(
+ modifier = modifier.padding(SettingsElementPadding),
+ ) {
+ Text(
+ text = label,
+ fontSize = SettingsCategoryFontSize,
+ fontWeight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+}
+
+@Composable
+@Preview
+private fun CategorySettingPreview() {
+ Surface {
+ CategorySetting(label = "Connection")
+ }
+}
diff --git a/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/EditableTextSetting.kt b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/EditableTextSetting.kt
new file mode 100644
index 0000000..ef7e0ea
--- /dev/null
+++ b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/EditableTextSetting.kt
@@ -0,0 +1,223 @@
+package org.eclipse.kuksa.companion.feature.settings.view
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
+
+@Composable
+fun EditableTextSetting(
+ label: String,
+ value: String,
+ modifier: Modifier = Modifier,
+ onValueChanged: (String) -> Unit,
+) {
+ var isDialogOpen: Boolean by remember { mutableStateOf(false) }
+
+ Row(
+ modifier = modifier
+ .fillMaxWidth()
+ .clickable {
+ isDialogOpen = true
+ }
+ .padding(SettingsElementPadding),
+ ) {
+ Column(
+ Modifier
+ .fillMaxWidth(),
+ ) {
+ Text(
+ text = label,
+ fontSize = SettingPrimaryFontSize,
+ fontWeight = FontWeight.Bold,
+ )
+ Text(
+ text = value,
+ fontSize = SettingSecondaryFontSize,
+ )
+ }
+ }
+
+ if (isDialogOpen) {
+ EditTextDialog(
+ label = label,
+ initialValue = value,
+ onDismissRequest = {
+ isDialogOpen = false
+ },
+ onClickOk = onValueChanged,
+ )
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
+@Composable
+fun EditTextDialog(
+ label: String,
+ initialValue: String,
+ modifier: Modifier = Modifier,
+ onDismissRequest: () -> Unit = {},
+ onClickCancel: () -> Unit = {},
+ onClickOk: (String) -> Unit = {},
+) {
+ val containerColor = MaterialTheme.colorScheme.secondaryContainer
+
+ val keyboardController = LocalSoftwareKeyboardController.current
+
+ val focusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) {
+ focusRequester.requestFocus()
+ }
+
+ val direction = LocalLayoutDirection.current
+ var textFieldValue by remember {
+ val selection = if (direction == LayoutDirection.Ltr) { // move Cursor to end
+ TextRange(initialValue.length)
+ } else {
+ TextRange.Zero
+ }
+
+ mutableStateOf(TextFieldValue(initialValue, selection))
+ }
+
+ Dialog(
+ onDismissRequest = onDismissRequest,
+ properties = DialogProperties(
+ dismissOnClickOutside = true,
+ dismissOnBackPress = true,
+ ),
+ ) {
+ Box(modifier) {
+ val interactionSource = remember { MutableInteractionSource() }
+ Column(
+ Modifier
+ .background(containerColor, RoundedCornerShape(15.dp))
+ .padding(25.dp),
+ ) {
+ Text(
+ text = label,
+ fontSize = SettingPrimaryFontSize,
+ fontWeight = FontWeight.Bold,
+ )
+
+ Spacer(modifier = Modifier.height(10.dp))
+
+ BasicTextField(
+ value = textFieldValue,
+ onValueChange = { newValue ->
+ textFieldValue = newValue
+ },
+ textStyle = TextStyle(
+ fontSize = SettingSecondaryFontSize,
+ ),
+ keyboardOptions = KeyboardOptions(
+ imeAction = ImeAction.Done,
+ ),
+ keyboardActions = KeyboardActions(
+ onDone = {
+ keyboardController?.hide()
+ onDismissRequest()
+ onClickOk(textFieldValue.text)
+ },
+ ),
+ modifier = Modifier
+ .fillMaxWidth()
+ .focusRequester(focusRequester),
+ ) { innerTextField ->
+ TextFieldDefaults.DecorationBox(
+ value = initialValue,
+ enabled = true,
+ innerTextField = innerTextField,
+ interactionSource = interactionSource,
+ singleLine = true,
+ visualTransformation = VisualTransformation.None,
+ contentPadding = PaddingValues(0.dp),
+ colors = TextFieldDefaults.colors(
+ focusedContainerColor = containerColor,
+ unfocusedContainerColor = containerColor,
+ ),
+ )
+ }
+ Spacer(modifier = Modifier.height(10.dp))
+ Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) {
+ TextButton(onClick = {
+ onDismissRequest()
+ onClickCancel()
+ }) {
+ Text("Cancel")
+ }
+ TextButton(onClick = {
+ onDismissRequest()
+ onClickOk(textFieldValue.text)
+ }) {
+ Text("OK")
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun EditableTextSettingPreview() {
+ Surface {
+ EditableTextSetting(
+ label = "Gerätename",
+ value = "Pixel",
+ ) {
+ // unused in preview
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun EditTextDialogPreview() {
+ EditTextDialog(
+ label = "Gerätename",
+ initialValue = "Pixel 3",
+ onDismissRequest = {},
+ )
+}
diff --git a/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/FileSelectorSetting.kt b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/FileSelectorSetting.kt
new file mode 100644
index 0000000..ff205ae
--- /dev/null
+++ b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/FileSelectorSetting.kt
@@ -0,0 +1,103 @@
+@file:JvmName("FileSelectorSettingKt")
+
+package org.eclipse.kuksa.companion.feature.settings.view
+
+import android.content.Intent
+import android.net.Uri
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
+import org.eclipse.kuksa.companion.R
+
+@Suppress("SameParameterValue") // re-usability
+@Composable
+fun FileSelectorSetting(
+ label: String,
+ value: String,
+ modifier: Modifier = Modifier,
+ onResult: (Uri) -> Unit,
+) {
+ val context = LocalContext.current
+
+ val launcher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
+ val uri = it ?: return@rememberLauncherForActivityResult
+
+ context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+
+ onResult(uri)
+ }
+
+ ConstraintLayout(
+ modifier
+ .fillMaxWidth()
+ .padding(SettingsElementPadding)
+ .clickable {
+ val fileTypes = arrayOf("*/*")
+ launcher.launch(fileTypes)
+ },
+ ) {
+ val (labelRef, valueRef, imageRef) = createRefs()
+
+ Text(
+ text = label,
+ fontSize = SettingPrimaryFontSize,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier
+ .padding(end = SettingsTextPaddingEnd)
+ .constrainAs(labelRef) {
+ width = Dimension.fillToConstraints
+ start.linkTo(parent.start)
+ end.linkTo(imageRef.start)
+ },
+ )
+ Text(
+ text = value,
+ fontSize = SettingSecondaryFontSize,
+ modifier = Modifier
+ .padding(end = SettingsTextPaddingEnd)
+ .constrainAs(valueRef) {
+ width = Dimension.fillToConstraints
+ start.linkTo(parent.start)
+ end.linkTo(imageRef.start)
+ top.linkTo(labelRef.bottom)
+ },
+ )
+ Image(
+ painter = painterResource(id = R.drawable.baseline_upload_file_24),
+ contentDescription = "Select Certificate",
+ modifier = Modifier
+ .size(40.dp)
+ .constrainAs(imageRef) {
+ end.linkTo(parent.end)
+ top.linkTo(parent.top)
+ bottom.linkTo(parent.bottom)
+ height = Dimension.fillToConstraints
+ },
+ )
+ }
+}
+
+@Composable
+@Preview
+private fun SwitchSettingPreview() {
+ Surface {
+ FileSelectorSetting(label = "File Selector Setting", value = "Some Value") {
+ // unused in preview
+ }
+ }
+}
diff --git a/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SettingsView.kt b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SettingsView.kt
index f4c699f..5660565 100644
--- a/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SettingsView.kt
+++ b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SettingsView.kt
@@ -19,63 +19,40 @@
package org.eclipse.kuksa.companion.feature.settings.view
-import android.content.Intent
import android.net.Uri
-import androidx.activity.compose.rememberLauncherForActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.consumeWindowInsets
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.layout.wrapContentHeight
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
-import androidx.compose.material3.Switch
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextField
-import androidx.compose.material3.contentColorFor
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.Alignment
-import androidx.compose.ui.Alignment.Companion.CenterVertically
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalSoftwareKeyboardController
-import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import org.eclipse.kuksa.companion.extension.fetchFileName
import org.eclipse.kuksa.companion.feature.connection.model.ConnectionInfo
import org.eclipse.kuksa.companion.feature.connection.repository.ConnectionInfoRepository
import org.eclipse.kuksa.companion.feature.settings.viewModel.SettingsViewModel
+import org.eclipse.kuksa.companion.ui.theme.KuksaCompanionTheme
+
+val SettingsElementPadding = 10.dp
+val SettingsTextPaddingEnd = 15.dp
+
+val SettingsCategoryFontSize = 20.sp
+val SettingPrimaryFontSize = 18.sp
+val SettingSecondaryFontSize = 16.sp
-@OptIn(ExperimentalLayoutApi::class)
@Composable
fun SettingsView(
settingsViewModel: SettingsViewModel,
modifier: Modifier = Modifier,
) {
+ val context = LocalContext.current
val connectionInfoState
by settingsViewModel.connectionInfoFlow.collectAsStateWithLifecycle(initialValue = ConnectionInfo())
@@ -83,26 +60,25 @@ fun SettingsView(
mutableStateOf(connectionInfoState)
}
- Scaffold(
- modifier = modifier.fillMaxSize(),
- ) { paddingValues ->
- Surface(
- modifier = Modifier
- .padding(paddingValues)
- .consumeWindowInsets(paddingValues),
- ) {
+ KuksaCompanionTheme {
+ Surface(modifier) {
Column(
modifier = Modifier
- .padding(10.dp, 0.dp)
- .consumeWindowInsets(paddingValues),
+ .padding(10.dp, 0.dp),
) {
- Category(label = "Connection")
- EditableTextSetting("Host", connectionInfo.host) { newValue ->
+ CategorySetting(label = "Connection")
+ EditableTextSetting(
+ "Host",
+ connectionInfo.host,
+ ) { newValue ->
connectionInfo = connectionInfoState.copy(host = newValue)
settingsViewModel.updateConnectionInfo(connectionInfo)
}
- EditableTextSetting("Port", connectionInfo.port.toString()) { newValue ->
+ EditableTextSetting(
+ "Port",
+ connectionInfo.port.toString(),
+ ) { newValue ->
try {
val port = newValue.toInt()
connectionInfo = connectionInfoState.copy(port = port)
@@ -112,223 +88,32 @@ fun SettingsView(
}
}
- SwitchSetting("Enable TLS", connectionInfo.isTlsEnabled) { newValue ->
+ SwitchSetting(
+ "Enable TLS",
+ connectionInfo.isTlsEnabled,
+ ) { newValue ->
connectionInfo = connectionInfoState.copy(isTlsEnabled = newValue)
settingsViewModel.updateConnectionInfo(connectionInfo)
}
- FileSelectorSetting(
- label = "Certificate",
- value = connectionInfo.certificate.uriPath,
- ) { uri: Uri? ->
- val certificate = connectionInfoState.certificate.copy(uriPath = uri.toString())
- connectionInfo = connectionInfoState.copy(certificate = certificate)
- settingsViewModel.updateConnectionInfo(connectionInfo)
- }
- }
- }
- }
-}
-
-@Suppress("SameParameterValue") // re-usability
-@Composable
-private fun Category(label: String) {
- Row(
- modifier = Modifier.padding(10.dp),
- ) {
- Text(
- text = label,
- fontSize = 15.sp,
- fontWeight = FontWeight.Bold,
- color = MaterialTheme.colorScheme.primary,
- modifier = Modifier.fillMaxWidth(),
- )
- }
-}
-
-@Composable
-private fun EditableTextSetting(
- label: String,
- value: String,
- onValueChanged: (String) -> Unit,
-) {
- var isDialogOpen by remember { mutableStateOf(false) }
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .height(75.dp)
- .offset(10.dp),
- ) {
- Column(
- Modifier
- .fillMaxWidth()
- .fillMaxHeight()
- .weight(1F)
- .clickable {
- isDialogOpen = true
- },
- ) {
- Text(text = label, fontSize = 20.sp)
- Spacer(modifier = Modifier.height(3.dp))
- Text(text = value)
- }
- }
-
- if (isDialogOpen) {
- Box(
- contentAlignment = Alignment.Center,
- modifier = Modifier
- .fillMaxSize()
- .background(
- color = contentColorFor(MaterialTheme.colorScheme.background).copy(alpha = 0.6f),
- )
- .clickable { isDialogOpen = true },
- ) {
- TextDialog(label, value, onClickOk = {
- isDialogOpen = false
- onValueChanged(it)
- }, onClickCancel = {
- isDialogOpen = false
- })
- }
- }
-}
-
-@OptIn(ExperimentalComposeUiApi::class)
-@Composable
-fun TextDialog(
- label: String,
- value: String,
- onClickOk: (String) -> Unit,
- modifier: Modifier = Modifier,
- onClickCancel: () -> Unit,
-) {
- val keyboardController = LocalSoftwareKeyboardController.current
-
- var newValue by remember { mutableStateOf(value) }
-
- Column(
- modifier = modifier
- .clip(RoundedCornerShape(4.dp))
- .background(MaterialTheme.colorScheme.background)
- .padding(8.dp),
- ) {
- Column(
- modifier = Modifier.padding(16.dp),
- ) {
- Text(text = label)
+ if (connectionInfo.isTlsEnabled) {
+ val uri = connectionInfo.certificate.uri
+ val fileName = uri.fetchFileName(context) ?: "Select certificate..."
- Spacer(modifier = Modifier.height(8.dp))
-
- TextField(
- value = newValue,
- onValueChange = { newValue = it },
- singleLine = true,
- keyboardActions = KeyboardActions(
- onDone = {
- keyboardController?.hide()
- onClickOk(newValue.trim())
- },
- ),
- )
- }
-
- Spacer(modifier = Modifier.height(8.dp))
-
- Row(
- modifier = Modifier.align(Alignment.End),
- ) {
- Button(onClick = {
- keyboardController?.hide()
- onClickCancel()
- }) {
- Text("Cancel")
- }
-
- Spacer(modifier = Modifier.width(8.dp))
-
- Button(onClick = {
- keyboardController?.hide()
- onClickOk(newValue.trim())
- }) {
- Text("OK")
+ FileSelectorSetting(
+ label = "Certificate",
+ value = fileName,
+ ) { selectedUri: Uri? ->
+ val certificate = connectionInfoState.certificate.copy(uriPath = selectedUri.toString())
+ connectionInfo = connectionInfoState.copy(certificate = certificate)
+ settingsViewModel.updateConnectionInfo(connectionInfo)
+ }
+ }
}
}
}
}
-@Suppress(
- // re-usability
- "SameParameterValue",
- // it does not make sense to create a constant for each elements weight
- "MagicNumber",
-)
-@Composable
-private fun SwitchSetting(
- label: String,
- enabled: Boolean,
- modifier: Modifier = Modifier,
- onValueChanged: (Boolean) -> Unit,
-) {
- Row(
- modifier = modifier.padding(10.dp),
- ) {
- Text(
- text = label,
- fontSize = 20.sp,
- modifier = Modifier
- .fillMaxWidth()
- .wrapContentHeight()
- .weight(3F)
- .align(CenterVertically),
- )
- Switch(checked = enabled, onCheckedChange = {
- onValueChanged(it)
- }, modifier = Modifier.weight(1F))
- }
-}
-
-@Suppress("SameParameterValue") // Reusability for the label parameter
-@Composable
-private fun FileSelectorSetting(
- label: String,
- value: String,
- onResult: (Uri) -> Unit,
-) {
- val context = LocalContext.current
-
- val launcher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) {
- val uri = it ?: return@rememberLauncherForActivityResult
-
- context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
-
- onResult(uri)
- }
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .height(75.dp)
- .offset(10.dp),
- ) {
- Column(
- Modifier
- .fillMaxWidth()
- .fillMaxHeight()
- .weight(1F)
- .clickable {
- val fileTypes = arrayOf("*/*")
- launcher.launch(fileTypes)
- },
- ) {
- Text(text = label, fontSize = 20.sp)
- Spacer(modifier = Modifier.height(3.dp))
- Text(text = value)
- }
- }
-}
-
@Preview
@Composable
private fun SettingsViewPreview() {
diff --git a/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SwitchSetting.kt b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SwitchSetting.kt
new file mode 100644
index 0000000..8cad0cf
--- /dev/null
+++ b/app/src/main/kotlin/org/eclipse/kuksa/companion/feature/settings/view/SwitchSetting.kt
@@ -0,0 +1,72 @@
+package org.eclipse.kuksa.companion.feature.settings.view
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.constraintlayout.compose.ConstraintLayout
+import androidx.constraintlayout.compose.Dimension
+
+@Suppress("SameParameterValue") // re-usability
+@Composable
+fun SwitchSetting(
+ label: String,
+ enabled: Boolean,
+ modifier: Modifier = Modifier,
+ onValueChanged: (Boolean) -> Unit,
+) {
+ ConstraintLayout(
+ modifier = modifier
+ .fillMaxWidth()
+ .clickable {
+ onValueChanged(!enabled)
+ }
+ .padding(SettingsElementPadding),
+ ) {
+ val (textView, switchView) = createRefs()
+
+ Text(
+ text = label,
+ fontSize = SettingPrimaryFontSize,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier
+ .padding(end = SettingsTextPaddingEnd)
+ .constrainAs(textView) {
+ start.linkTo(parent.start)
+ end.linkTo(switchView.start)
+
+ top.linkTo(parent.top)
+ bottom.linkTo(parent.bottom)
+ width = Dimension.fillToConstraints
+ },
+ )
+ Switch(
+ checked = enabled,
+ onCheckedChange = {
+ onValueChanged(it)
+ },
+ modifier = Modifier.constrainAs(switchView) {
+ end.linkTo(parent.end)
+
+ top.linkTo(parent.top)
+ bottom.linkTo(parent.bottom)
+ },
+ )
+ }
+}
+
+@Composable
+@Preview
+private fun SwitchSettingPreview() {
+ Surface {
+ SwitchSetting(label = "Switch Setting", enabled = true) {
+ // unused in preview
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/baseline_upload_file_24.xml b/app/src/main/res/drawable/baseline_upload_file_24.xml
new file mode 100644
index 0000000..90e8cd8
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_upload_file_24.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 64e2d9b..8fc81b5 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -19,5 +19,12 @@
-
+
+
+
+