1
0
mirror of https://github.com/nextcloud/talk-android synced 2025-07-15 00:35:04 +01:00

Merge pull request from nextcloud/password_improvement

Password improvement
This commit is contained in:
Marcel Hibbe 2024-09-26 13:16:36 +02:00 committed by GitHub
commit 0354501b41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 156 additions and 22 deletions
app/src/main

View File

@ -20,25 +20,31 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -48,6 +54,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -55,7 +62,7 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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.colorResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import autodagger.AutoInjector import autodagger.AutoInjector
@ -436,6 +445,8 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM
.isConversationAvailableForRegisteredUsers.value .isConversationAvailableForRegisteredUsers.value
val isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value val isOpenForGuestAppUsers = conversationCreationViewModel.openForGuestAppUsers.value
val isPasswordSet = conversationCreationViewModel.isPasswordEnabled.value
Text( Text(
text = stringResource(id = R.string.nc_new_conversation_visibility).uppercase(), text = stringResource(id = R.string.nc_new_conversation_visibility).uppercase(),
fontSize = 14.sp, fontSize = 14.sp,
@ -452,15 +463,21 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM
} }
) )
}, },
showDialog = false,
conversationCreationViewModel = conversationCreationViewModel conversationCreationViewModel = conversationCreationViewModel
) )
if (isGuestsAllowed) { if (isGuestsAllowed && !isPasswordSet) {
ConversationOptions(
icon = R.drawable.baseline_lock_open_24,
text = R.string.nc_set_password,
conversationCreationViewModel = conversationCreationViewModel
)
}
if (isGuestsAllowed && isPasswordSet) {
ConversationOptions( ConversationOptions(
icon = R.drawable.ic_lock_grey600_24px, icon = R.drawable.ic_lock_grey600_24px,
text = R.string.nc_set_password, text = R.string.nc_change_password,
showDialog = true,
conversationCreationViewModel = conversationCreationViewModel conversationCreationViewModel = conversationCreationViewModel
) )
} }
@ -476,7 +493,6 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM
} }
) )
}, },
showDialog = false,
conversationCreationViewModel = conversationCreationViewModel conversationCreationViewModel = conversationCreationViewModel
) )
@ -491,7 +507,6 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM
} }
) )
}, },
showDialog = false,
conversationCreationViewModel = conversationCreationViewModel conversationCreationViewModel = conversationCreationViewModel
) )
} }
@ -502,19 +517,23 @@ fun ConversationOptions(
icon: Int? = null, icon: Int? = null,
text: Int, text: Int,
switch: @Composable (() -> Unit)? = null, switch: @Composable (() -> Unit)? = null,
showDialog: Boolean,
conversationCreationViewModel: ConversationCreationViewModel conversationCreationViewModel: ConversationCreationViewModel
) { ) {
var showPasswordDialog by remember { mutableStateOf(false) } var showPasswordDialog by rememberSaveable { mutableStateOf(false) }
var showPasswordChangeDialog by rememberSaveable { mutableStateOf(false) }
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp) .padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
.then( .then(
if (showDialog) { if (!conversationCreationViewModel.isPasswordEnabled.value) {
Modifier.clickable { Modifier.clickable {
showPasswordDialog = true showPasswordDialog = true
} }
} else if (conversationCreationViewModel.isPasswordEnabled.value) {
Modifier.clickable {
showPasswordChangeDialog = true
}
} else { } else {
Modifier Modifier
} }
@ -545,24 +564,99 @@ fun ConversationOptions(
conversationCreationViewModel = conversationCreationViewModel conversationCreationViewModel = conversationCreationViewModel
) )
} }
if (showPasswordChangeDialog) {
ShowChangePassword(
onDismiss = {
showPasswordChangeDialog = false
},
conversationCreationViewModel = conversationCreationViewModel
)
}
}
}
@Composable
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 @Composable
fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) { fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) {
var password by remember { mutableStateOf("") } var password by rememberSaveable { mutableStateOf("") }
AlertDialog( AlertDialog(
containerColor = colorResource(id = R.color.dialog_background), containerColor = colorResource(id = R.color.dialog_background),
onDismissRequest = onDismiss, 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)) }, title = { Text(text = stringResource(id = R.string.nc_set_password)) },
text = { text = {
TextField( TextField(
@ -573,8 +667,20 @@ fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: Con
label = { Text(text = stringResource(id = R.string.nc_guest_access_password_dialog_hint)) } 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 = { dismissButton = {
Button(onClick = { onDismiss() }) { TextButton(onClick = { onDismiss() }) {
Text(text = stringResource(id = R.string.nc_cancel)) Text(text = stringResource(id = R.string.nc_cancel))
} }
} }

View File

@ -38,14 +38,20 @@ class ConversationCreationViewModel @Inject constructor(
private val _currentUser = userManager.currentUser.blockingGet() private val _currentUser = userManager.currentUser.blockingGet()
val currentUser: User = _currentUser val currentUser: User = _currentUser
private val _isPasswordEnabled = mutableStateOf(false)
val isPasswordEnabled = _isPasswordEnabled
fun updateSelectedParticipants(participants: List<AutocompleteUser>) { fun updateSelectedParticipants(participants: List<AutocompleteUser>) {
_selectedParticipants.value = participants _selectedParticipants.value = participants
} }
fun isPasswordEnabled(value: Boolean) {
_isPasswordEnabled.value = value
}
fun updateSelectedImageUri(uri: Uri?) { fun updateSelectedImageUri(uri: Uri?) {
_selectedImageUri.value = uri _selectedImageUri.value = uri
} }
private val _roomName = MutableStateFlow("") private val _roomName = MutableStateFlow("")
val roomName: StateFlow<String> = _roomName val roomName: StateFlow<String> = _roomName
private val _password = MutableStateFlow("") private val _password = MutableStateFlow("")

View File

@ -0,0 +1,18 @@
<!--
~ Nextcloud Talk - Android Client
~
~ SPDX-FileCopyrightText: 2024 Google LLC
~ SPDX-License-Identifier: GPL-3.0-or-later
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:tint="#000000"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp">
<path android:fillColor="@android:color/white"
android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
</vector>

View File

@ -434,6 +434,10 @@ How to translate with transifex:
<string name="nc_guest_access_allow_summary">Allow guests to share a public link to join this conversation.</string> <string name="nc_guest_access_allow_summary">Allow guests to share a public link to join this conversation.</string>
<string name="nc_guest_access_allow_failed">Cannot enable/disable guest access.</string> <string name="nc_guest_access_allow_failed">Cannot enable/disable guest access.</string>
<string name="nc_set_password">Set Password</string> <string name="nc_set_password">Set Password</string>
<string name="nc_password">Password</string>
<string name="nc_change_password">Change Password</string>
<string name="nc_remove_password">Remove Password</string>
<string name="nc_set_new_password">Set new password</string>
<string name="nc_guest_access_password_title">Password protection</string> <string name="nc_guest_access_password_title">Password protection</string>
<string name="nc_guest_access_password_summary">Set a password to restrict who can use the public link.</string> <string name="nc_guest_access_password_summary">Set a password to restrict who can use the public link.</string>
<string name="nc_guest_access_password_dialog_title">Guest access password</string> <string name="nc_guest_access_password_dialog_title">Guest access password</string>