diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt index 74bf4d6150..b45db2f035 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationActivity.kt @@ -20,25 +20,31 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.result.ActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme 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.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize 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.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -48,6 +54,7 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -55,7 +62,7 @@ import androidx.compose.runtime.DisposableEffect 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 @@ -67,9 +74,11 @@ import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog import androidx.core.view.WindowCompat import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector @@ -436,6 +445,8 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM .isConversationAvailableForRegisteredUsers.value val isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value + val isPasswordSet = conversationCreationViewModel.isPasswordEnabled.value + Text( text = stringResource(id = R.string.nc_new_conversation_visibility).uppercase(), fontSize = 14.sp, @@ -452,15 +463,21 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM } ) }, - showDialog = false, conversationCreationViewModel = conversationCreationViewModel ) - if (isGuestsAllowed) { + if (isGuestsAllowed && !isPasswordSet) { ConversationOptions( - icon = R.drawable.ic_lock_grey600_24px, + icon = R.drawable.baseline_lock_open_24, text = R.string.nc_set_password, - showDialog = true, + conversationCreationViewModel = conversationCreationViewModel + ) + } + + if (isGuestsAllowed && isPasswordSet) { + ConversationOptions( + icon = R.drawable.ic_lock_grey600_24px, + text = R.string.nc_change_password, conversationCreationViewModel = conversationCreationViewModel ) } @@ -476,7 +493,6 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM } ) }, - showDialog = false, conversationCreationViewModel = conversationCreationViewModel ) @@ -491,7 +507,6 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM } ) }, - showDialog = false, conversationCreationViewModel = conversationCreationViewModel ) } @@ -502,19 +517,23 @@ fun ConversationOptions( icon: Int? = null, text: Int, switch: @Composable (() -> Unit)? = null, - showDialog: Boolean, conversationCreationViewModel: ConversationCreationViewModel ) { - var showPasswordDialog by remember { mutableStateOf(false) } + var showPasswordDialog by rememberSaveable { mutableStateOf(false) } + var showPasswordChangeDialog by rememberSaveable { mutableStateOf(false) } Row( modifier = Modifier .fillMaxWidth() .padding(start = 16.dp, end = 16.dp, bottom = 8.dp) .then( - if (showDialog) { + if (!conversationCreationViewModel.isPasswordEnabled.value) { Modifier.clickable { showPasswordDialog = true } + } else if (conversationCreationViewModel.isPasswordEnabled.value) { + Modifier.clickable { + showPasswordChangeDialog = true + } } else { Modifier } @@ -545,24 +564,99 @@ fun ConversationOptions( conversationCreationViewModel = conversationCreationViewModel ) } + if (showPasswordChangeDialog) { + ShowChangePassword( + onDismiss = { + showPasswordChangeDialog = false + }, + conversationCreationViewModel = conversationCreationViewModel + ) + } } } @Composable -fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { - var password by remember { mutableStateOf("") } +fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { + var changedPassword by rememberSaveable { mutableStateOf("") } + Dialog(onDismissRequest = { + onDismiss() + }) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(375.dp) + .padding(32.dp) + .clip(RoundedCornerShape(16.dp)) + .background(color = colorResource(id = R.color.appbar)) + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 16.dp, horizontal = 16.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = stringResource(id = R.string.nc_set_new_password), fontWeight = FontWeight.SemiBold) + Spacer(modifier = Modifier.height(16.dp)) + OutlinedTextField( + value = changedPassword, + onValueChange = { + changedPassword = it + }, + label = { Text(text = stringResource(id = R.string.nc_password)) }, + singleLine = true + ) + Spacer(modifier = Modifier.height(16.dp)) + + Column( + modifier = Modifier.fillMaxWidth() + .padding(vertical = 8.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + TextButton( + onClick = { + conversationCreationViewModel.updatePassword(changedPassword) + conversationCreationViewModel.isPasswordEnabled.value = true + onDismiss() + }, + enabled = changedPassword.isNotEmpty() && changedPassword.isNotBlank(), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp) + ) { + Text(text = stringResource(id = R.string.nc_change_password)) + } + Spacer(modifier = Modifier.height(4.dp)) + TextButton( + onClick = { + conversationCreationViewModel.isPasswordEnabled.value = false + onDismiss() + }, + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp) + ) { + Text( + text = stringResource(id = R.string.nc_remove_password), + color = colorResource(id = R.color.nc_darkRed) + ) + } + Spacer(modifier = Modifier.height(4.dp)) + TextButton( + onClick = { onDismiss() }, + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp) + ) { + Text(text = stringResource(id = R.string.nc_cancel)) + } + } + } + } + } +} +@Composable +fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { + var password by rememberSaveable { mutableStateOf("") } AlertDialog( containerColor = colorResource(id = R.color.dialog_background), onDismissRequest = onDismiss, - confirmButton = { - Button(onClick = { - conversationCreationViewModel.updatePassword(password) - onDismiss() - }) { - Text(text = stringResource(id = R.string.save)) - } - }, title = { Text(text = stringResource(id = R.string.nc_set_password)) }, text = { TextField( @@ -573,8 +667,20 @@ fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: Con label = { Text(text = stringResource(id = R.string.nc_guest_access_password_dialog_hint)) } ) }, + confirmButton = { + TextButton( + onClick = { + if (password.isNotEmpty() && password.isNotBlank()) { + conversationCreationViewModel.updatePassword(password) + conversationCreationViewModel.isPasswordEnabled(true) + } + } + ) { + Text(text = stringResource(id = R.string.save)) + } + }, dismissButton = { - Button(onClick = { onDismiss() }) { + TextButton(onClick = { onDismiss() }) { Text(text = stringResource(id = R.string.nc_cancel)) } } diff --git a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt index 0fa663d889..66421bb540 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationcreation/ConversationCreationViewModel.kt @@ -38,14 +38,20 @@ class ConversationCreationViewModel @Inject constructor( private val _currentUser = userManager.currentUser.blockingGet() val currentUser: User = _currentUser + private val _isPasswordEnabled = mutableStateOf(false) + val isPasswordEnabled = _isPasswordEnabled + fun updateSelectedParticipants(participants: List) { _selectedParticipants.value = participants } + fun isPasswordEnabled(value: Boolean) { + _isPasswordEnabled.value = value + } + fun updateSelectedImageUri(uri: Uri?) { _selectedImageUri.value = uri } - private val _roomName = MutableStateFlow("") val roomName: StateFlow = _roomName private val _password = MutableStateFlow("") diff --git a/app/src/main/res/drawable/baseline_lock_open_24.xml b/app/src/main/res/drawable/baseline_lock_open_24.xml new file mode 100644 index 0000000000..ed16256a53 --- /dev/null +++ b/app/src/main/res/drawable/baseline_lock_open_24.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c23acb5fbe..b6d2d6469f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -434,6 +434,10 @@ How to translate with transifex: Allow guests to share a public link to join this conversation. Cannot enable/disable guest access. Set Password + Password + Change Password + Remove Password + Set new password Password protection Set a password to restrict who can use the public link. Guest access password