mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-15 00:35:04 +01:00
Merge pull request #4259 from nextcloud/password_improvement
Password improvement
This commit is contained in:
commit
0354501b41
app/src/main
java/com/nextcloud/talk/conversationcreation
res
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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("")
|
||||||
|
18
app/src/main/res/drawable/baseline_lock_open_24.xml
Normal file
18
app/src/main/res/drawable/baseline_lock_open_24.xml
Normal 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>
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user