Private
Public Access
1
0

SettingsScreen: Started working on settings screen

Still need to do auth
This commit is contained in:
2024-02-25 19:42:10 -08:00
parent f242a08967
commit ed4e3cd0bb
8 changed files with 319 additions and 21 deletions

View File

@@ -20,9 +20,13 @@ import net.buzzert.kordophonedroid.data.AppContainer
import net.buzzert.kordophonedroid.ui.theme.KordophoneTheme
import net.buzzert.kordophonedroid.ui.conversationlist.ConversationListScreen
import net.buzzert.kordophonedroid.ui.messagelist.MessageListScreen
import net.buzzert.kordophonedroid.ui.settings.SettingsScreen
sealed class Destination(val route: String) {
object ConversationList : Destination("conversations")
object Settings : Destination("settings")
object MessageList : Destination("messages/{id}") {
fun createRoute(data: String) = "messages/$data"
}
@@ -64,6 +68,8 @@ fun KordophoneApp(
composable(route = Destination.ConversationList.route) {
ConversationListScreen(onConversationSelected = {
navController.navigate(Destination.MessageList.createRoute(it))
}, onSettingsInvoked = {
navController.navigate(Destination.Settings.route)
})
}
@@ -73,6 +79,12 @@ fun KordophoneApp(
navController.popBackStack()
})
}
composable(Destination.Settings.route) {
SettingsScreen(backAction = {
navController.popBackStack()
})
}
}
errorVisible.value?.let {

View File

@@ -10,6 +10,7 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.icons.rounded.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -45,25 +46,30 @@ fun formatDateTime(dateTime: LocalDateTime): String {
@Composable
fun ConversationListScreen(
viewModel: ConversationListViewModel = hiltViewModel(),
onConversationSelected: (conversationID: String) -> Unit
onConversationSelected: (conversationID: String) -> Unit,
onSettingsInvoked: () -> Unit,
) {
val conversations by viewModel.conversations.collectAsStateWithLifecycle(initialValue = emptyList())
ConversationListView(conversations = conversations, onConversationSelected = onConversationSelected)
ConversationListView(
conversations = conversations,
onConversationSelected = onConversationSelected,
onSettingsInvoked = onSettingsInvoked,
)
}
@Composable
fun ConversationListView(
conversations: List<Conversation>,
onConversationSelected: (conversationID: String) -> Unit
onConversationSelected: (conversationID: String) -> Unit,
onSettingsInvoked: () -> Unit,
) {
val listState = rememberLazyListState()
Scaffold(
topBar = {
TopAppBar(title = { Text("Conversations") }, actions = {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.Rounded.Info, contentDescription = "Info")
IconButton(onClick = onSettingsInvoked) {
Icon(Icons.Rounded.Settings, contentDescription = "Settings")
}
})
}
@@ -173,5 +179,5 @@ fun ConversationListItemPreview() {
@Preview
@Composable
fun ConversationListScreenPreview() {
ConversationListScreen(onConversationSelected = {})
ConversationListScreen(onConversationSelected = {}, onSettingsInvoked = {})
}

View File

@@ -48,6 +48,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import net.buzzert.kordophone.backend.model.GUID
import net.buzzert.kordophone.backend.model.Message
import net.buzzert.kordophonedroid.ui.theme.KordophoneTopAppBar
private val IncomingChatBubbleShape = RoundedCornerShape(4.dp, 20.dp, 20.dp, 20.dp)
private val OutgoingChatBubbleShape = RoundedCornerShape(20.dp, 4.dp, 20.dp, 20.dp)
@@ -82,7 +83,7 @@ fun MessageListScreen(
)
}
Scaffold(topBar = { MessagesListTopAppBar(title = viewModel.title, backAction = backAction) }) { padding ->
Scaffold(topBar = { KordophoneTopAppBar(title = viewModel.title, backAction = backAction) }) { padding ->
MessageTranscript(
messages = messageItems,
paddingValues = padding,
@@ -93,19 +94,6 @@ fun MessageListScreen(
}
}
@Composable
fun MessagesListTopAppBar(title: String, backAction: () -> Unit) {
TopAppBar(
title = { Text(title) },
navigationIcon = {
IconButton(onClick = backAction) {
Icon(Icons.Filled.ArrowBack, null)
}
},
actions = {}
)
}
@Composable
fun MessageTranscript(
messages: List<MessageListViewItem>,

View File

@@ -0,0 +1,222 @@
package net.buzzert.kordophonedroid.ui.settings
import android.provider.Settings
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
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.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.hilt.navigation.compose.hiltViewModel
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import net.buzzert.kordophonedroid.R
import net.buzzert.kordophonedroid.ui.theme.KordophoneTopAppBar
@Composable
fun SettingsScreen(
viewModel: SettingsViewModel = hiltViewModel(),
backAction: () -> Unit,
) {
Scaffold(
topBar = {
KordophoneTopAppBar(
title = "Settings",
backAction = backAction,
)
},
) {
SettingsFormView(
viewModel = viewModel,
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(it)
.padding(6.dp)
)
}
}
@Composable
fun SettingsFormView(
viewModel: SettingsViewModel,
modifier: Modifier = Modifier
) {
val serverName = viewModel.serverPreference.collectAsState()
val userName = viewModel.usernamePreference.collectAsState()
Column(modifier) {
var serverNameInput by remember { mutableStateOf(TextFieldValue(serverName.value)) }
SettingsTextField(
name = "Server",
icon = R.drawable.storage,
state = serverName,
onSave = { viewModel.saveServerPreference(serverNameInput.text) }
) { state ->
TextField(serverNameInput, onValueChange = {
serverNameInput = it
})
}
SettingsTextField(
name = "Authentication",
icon = R.drawable.account_circle,
state = userName,
onSave = { /* TODO */ }
) {
// TODO
Text("Hoohah!")
}
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun <T> SettingsTextField(
name: String,
@DrawableRes icon: Int,
state: State<T>,
onSave: () -> Unit,
dialogContent: @Composable (State<T>) -> Unit,
) {
var showingDialog by remember { mutableStateOf(false) }
if (showingDialog) {
Dialog(
onDismissRequest = { showingDialog = false }
) {
EditDialog(
name = name,
onDismiss = {
onSave()
showingDialog = false
},
content = { dialogContent(state) }
)
}
}
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
,
onClick = {
showingDialog = true
},
) {
val valueString = state.value.toString().ifEmpty { "(Not set)" }
Column {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
modifier = Modifier.padding(vertical = 8.dp)
) {
Icon(
painterResource(id = icon),
contentDescription = "",
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.padding(8.dp)) {
// Title
Text(
text = name,
style = MaterialTheme.typography.body1,
textAlign = TextAlign.Start,
)
Spacer(modifier = Modifier.height(4.dp))
// Value
Text(
text = valueString,
style = MaterialTheme.typography.body2,
textAlign = TextAlign.Start,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.5f),
)
}
}
Divider()
}
}
}
@Composable
private fun EditDialog(
name: String,
onDismiss: () -> Unit,
content: @Composable () -> Unit,
) {
Surface() {
Column(
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.padding(16.dp)
) {
Text(name)
Spacer(modifier = Modifier.height(8.dp))
content()
Spacer(modifier = Modifier.height(8.dp))
Row {
Spacer(modifier = Modifier.weight(1f))
Button(onClick = {
onDismiss()
}) {
Text("Save")
}
}
}
}
}
@Preview
@Composable
fun SettingsPreview() {
SettingsScreen(backAction = {})
}

View File

@@ -0,0 +1,30 @@
package net.buzzert.kordophonedroid.ui.settings
import androidx.compose.runtime.MutableState
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
@HiltViewModel
class SettingsViewModel @Inject constructor() : ViewModel() {
private val _serverPreference: MutableStateFlow<String> = MutableStateFlow("")
var serverPreference = _serverPreference.asStateFlow()
private val _usernamePreference: MutableStateFlow<String> = MutableStateFlow("")
var usernamePreference = _usernamePreference.asStateFlow()
private val _passwordPreference: MutableStateFlow<String> = MutableStateFlow("")
var passwordPreference = _passwordPreference.asStateFlow()
fun saveServerPreference(serverName: String) {
_serverPreference.value = serverName
}
fun saveAuthenticationPreferences(username: String, password: String) {
_usernamePreference.value = username
_passwordPreference.value = password
}
}

View File

@@ -1,8 +1,14 @@
package net.buzzert.kordophonedroid.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.darkColors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
@@ -45,3 +51,17 @@ fun KordophoneTheme(
content = content
)
}
@Composable
fun KordophoneTopAppBar(title: String, backAction: () -> Unit) {
TopAppBar(
title = { Text(title) },
navigationIcon = {
IconButton(onClick = backAction) {
Icon(Icons.Filled.ArrowBack, null)
}
},
actions = {}
)
}

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M234,684Q285,645 348,622.5Q411,600 480,600Q549,600 612,622.5Q675,645 726,684Q761,643 780.5,591Q800,539 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,539 179.5,591Q199,643 234,684ZM480,520Q421,520 380.5,479.5Q340,439 340,380Q340,321 380.5,280.5Q421,240 480,240Q539,240 579.5,280.5Q620,321 620,380Q620,439 579.5,479.5Q539,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q533,800 580,784.5Q627,769 666,740Q627,711 580,695.5Q533,680 480,680Q427,680 380,695.5Q333,711 294,740Q333,769 380,784.5Q427,800 480,800ZM480,440Q506,440 523,423Q540,406 540,380Q540,354 523,337Q506,320 480,320Q454,320 437,337Q420,354 420,380Q420,406 437,423Q454,440 480,440ZM480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380ZM480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M120,800L120,640L840,640L840,800L120,800ZM200,760L280,760L280,680L200,680L200,760ZM120,320L120,160L840,160L840,320L120,320ZM200,280L280,280L280,200L200,200L200,280ZM120,560L120,400L840,400L840,560L120,560ZM200,520L280,520L280,440L200,440L200,520Z"/>
</vector>