Browse Source

Merge pull request #4259 from nextcloud/password_improvement

Password improvement
Marcel Hibbe 7 months ago
parent
commit
0354501b41

+ 127 - 21
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))
             }
         }

+ 7 - 1
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<AutocompleteUser>) {
         _selectedParticipants.value = participants
     }
 
+    fun isPasswordEnabled(value: Boolean) {
+        _isPasswordEnabled.value = value
+    }
+
     fun updateSelectedImageUri(uri: Uri?) {
         _selectedImageUri.value = uri
     }
-
     private val _roomName = MutableStateFlow("")
     val roomName: StateFlow<String> = _roomName
     private val _password = MutableStateFlow("")

+ 18 - 0
app/src/main/res/drawable/baseline_lock_open_24.xml

@@ -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>

+ 4 - 0
app/src/main/res/values/strings.xml

@@ -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_failed">Cannot enable/disable guest access.</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_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>