Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.nextcloud.talk.models.json.invitation.InvitationOverall
import com.nextcloud.talk.models.json.participants.AddParticipantOverall
import com.nextcloud.talk.models.json.participants.TalkBan
import com.nextcloud.talk.models.json.participants.TalkBanOverall
import com.nextcloud.talk.models.json.passwordResult.PasswordResultOverall
import com.nextcloud.talk.models.json.profile.ProfileOverall
import com.nextcloud.talk.models.json.reactions.ReactionsOverall
import com.nextcloud.talk.models.json.status.StatusOverall
Expand Down Expand Up @@ -584,4 +585,12 @@ interface NcApiCoroutines {
@FormUrlEncoded
@POST
suspend fun reportRemoteWipeSuccess(@Url url: String, @Field("token") token: String): Response<Unit>

@FormUrlEncoded
@POST
suspend fun validatePassword(
@Header("Authorization") authorization: String,
@Url url: String,
@Field("password") password: String
): PasswordResultOverall
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
Expand All @@ -56,12 +57,12 @@ 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
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
Expand All @@ -82,6 +83,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import autodagger.AutoInjector
import coil.compose.AsyncImage
import com.nextcloud.talk.R
Expand All @@ -101,8 +103,11 @@ import com.nextcloud.talk.conversationcreation.viewmodel.ConversationCreationVie
import com.nextcloud.talk.extensions.getParcelableArrayListExtraProvider
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
import com.nextcloud.talk.passwordpolicy.PasswordPolicyField
import com.nextcloud.talk.passwordpolicy.isPasswordAccepted
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.copyPasswordToClipboard
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.PickImage
import com.nextcloud.talk.utils.bundle.BundleKeys
Expand Down Expand Up @@ -478,6 +483,7 @@ fun AddParticipants(
@Suppress("LongMethod")
@Composable
fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewModel) {
val context = LocalContext.current
val isGuestsAllowed = conversationCreationViewModel.isGuestsAllowed
val isConversationAvailableForRegisteredUsers = conversationCreationViewModel
.isConversationAvailableForRegisteredUsers
Expand Down Expand Up @@ -524,6 +530,20 @@ fun RoomCreationOptions(conversationCreationViewModel: ConversationCreationViewM
)
}

if (isGuestsAllowed && isPasswordSet) {
ConversationOption(
icon = R.drawable.ic_content_copy,
text = R.string.nc_copy_password,
onClick = {
copyPasswordToClipboard(
context = context,
label = context.resources.getString(R.string.nc_app_product_name),
password = conversationCreationViewModel.password.value
)
}
)
}

ConversationOption(
icon = R.drawable.baseline_format_list_bulleted_24,
text = R.string.nc_open_conversation_to_registered_users,
Expand Down Expand Up @@ -601,17 +621,20 @@ fun ConversationOption(
@Suppress("LongMethod")
@Composable
fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) {
var changedPassword by rememberSaveable { mutableStateOf("") }
var changedPassword by remember { mutableStateOf("") }
val passwordValidationState by conversationCreationViewModel.passwordValidation.state
.collectAsStateWithLifecycle()
Dialog(onDismissRequest = {
onDismiss()
}) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(375.dp)
.wrapContentHeight()
.padding(32.dp)
.clip(RoundedCornerShape(16.dp))
.background(color = colorResource(id = R.color.appbar))
.verticalScroll(rememberScrollState())
) {
Column(
modifier = Modifier
Expand All @@ -622,15 +645,15 @@ fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: Con
) {
Text(text = stringResource(id = R.string.nc_set_new_password), fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.height(16.dp))
OutlinedTextField(
value = changedPassword,
onValueChange = {
PasswordPolicyField(
password = changedPassword,
onPasswordChange = {
changedPassword = it
conversationCreationViewModel.passwordValidation.validate(it)
},
label = { Text(text = stringResource(id = R.string.nc_password)) },
singleLine = true
validationState = passwordValidationState,
label = stringResource(id = R.string.nc_password)
)
Spacer(modifier = Modifier.height(16.dp))

Column(
modifier = Modifier
Expand All @@ -642,9 +665,10 @@ fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: Con
TextButton(
onClick = {
conversationCreationViewModel.updatePassword(changedPassword)
conversationCreationViewModel.passwordValidation.reset()
onDismiss()
},
enabled = changedPassword.isNotEmpty() && changedPassword.isNotBlank(),
enabled = changedPassword.isNotBlank() && passwordValidationState.isPasswordAccepted,
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
) {
Text(text = stringResource(id = R.string.nc_change_password))
Expand All @@ -653,6 +677,7 @@ fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: Con
TextButton(
onClick = {
conversationCreationViewModel.updatePassword("")
conversationCreationViewModel.passwordValidation.reset()
onDismiss()
},
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
Expand All @@ -664,7 +689,10 @@ fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: Con
}
Spacer(modifier = Modifier.height(4.dp))
TextButton(
onClick = { onDismiss() },
onClick = {
conversationCreationViewModel.passwordValidation.reset()
onDismiss()
},
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
) {
Text(text = stringResource(id = R.string.nc_cancel))
Expand All @@ -677,34 +705,41 @@ fun ShowChangePassword(onDismiss: () -> Unit, conversationCreationViewModel: Con

@Composable
fun ShowPasswordDialog(onDismiss: () -> Unit, conversationCreationViewModel: ConversationCreationViewModel) {
var password by rememberSaveable { mutableStateOf("") }
var password by remember { mutableStateOf("") }
val passwordValidationState by conversationCreationViewModel.passwordValidation.state
.collectAsStateWithLifecycle()
AlertDialog(
containerColor = colorResource(id = R.color.dialog_background),
onDismissRequest = onDismiss,
title = { Text(text = stringResource(id = R.string.nc_set_password)) },
text = {
TextField(
value = password,
onValueChange = {
PasswordPolicyField(
password = password,
onPasswordChange = {
password = it
conversationCreationViewModel.passwordValidation.validate(it)
},
label = { Text(text = stringResource(id = R.string.nc_guest_access_password_dialog_hint)) }
validationState = passwordValidationState,
label = stringResource(id = R.string.nc_guest_access_password_dialog_hint)
)
},
confirmButton = {
TextButton(
enabled = password.isNotBlank() && passwordValidationState.isPasswordAccepted,
onClick = {
if (password.isNotEmpty() && password.isNotBlank()) {
conversationCreationViewModel.updatePassword(password)
onDismiss()
}
conversationCreationViewModel.updatePassword(password)
conversationCreationViewModel.passwordValidation.reset()
onDismiss()
}
) {
Text(text = stringResource(id = R.string.save))
}
},
dismissButton = {
TextButton(onClick = { onDismiss() }) {
TextButton(onClick = {
conversationCreationViewModel.passwordValidation.reset()
onDismiss()
}) {
Text(text = stringResource(id = R.string.nc_cancel))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,9 @@
package com.nextcloud.talk.conversationcreation.ui

import android.app.Activity
import android.content.ClipData
import android.content.ClipDescription
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.PersistableBundle
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.AlertDialog
Expand All @@ -32,6 +27,7 @@ import com.nextcloud.talk.models.json.conversations.ConversationEnums
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.ShareUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.copyPasswordToClipboard

/**
* Reacts to the outcome of creating a conversation: reports what could not be done, hands public
Expand Down Expand Up @@ -95,7 +91,7 @@ fun ShareCreatedConversation(
Column {
Text(text = stringResource(R.string.nc_conversation_created_public))
if (!password.isNullOrEmpty()) {
TextButton(onClick = { copyPassword(context, roomToken, password) }) {
TextButton(onClick = { copyPasswordToClipboard(context, roomToken, password) }) {
Text(text = stringResource(R.string.nc_copy_password))
}
}
Expand Down Expand Up @@ -133,15 +129,3 @@ fun openConversation(context: Context, roomToken: String) {
chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
context.startActivity(chatIntent)
}

private fun copyPassword(context: Context, roomToken: String, password: String) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(roomToken, password)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
clip.description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
}
clipboard.setPrimaryClip(clip)
Toast.makeText(context, context.getString(R.string.nc_password_copied), Toast.LENGTH_SHORT).show()
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import com.nextcloud.talk.conversationcreation.parametersOf
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.autocomplete.AutocompleteUser
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.passwordpolicy.PasswordPolicyValidator
import com.nextcloud.talk.repositories.passwordpolicy.PasswordPolicyRepository
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.SpreedFeatures
Expand All @@ -40,13 +42,16 @@ import javax.inject.Inject
class ConversationCreationViewModel @Inject constructor(
private val repository: ConversationCreationRepository,
private val conversationCreator: ConversationCreator,
private val passwordPolicyRepository: PasswordPolicyRepository,
private val currentUserProvider: CurrentUserProviderOld
) : ViewModel() {
private val _selectedParticipants = MutableStateFlow<List<AutocompleteUser>>(emptyList())
val selectedParticipants: StateFlow<List<AutocompleteUser>> = _selectedParticipants
private val roomViewState = MutableStateFlow<RoomUIState>(RoomUIState.None)
val creationState: StateFlow<RoomUIState> = roomViewState

val passwordValidation = PasswordPolicyValidator(passwordPolicyRepository, viewModelScope) { _currentUser }

private val _selectedImageUri = MutableStateFlow<Uri?>(null)
val selectedImageUri: StateFlow<Uri?> = _selectedImageUri

Expand Down
Loading
Loading