diff --git a/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewFragment.kt b/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewFragment.kt index 8d028242a0e..dbcfaadb533 100644 --- a/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewFragment.kt @@ -32,8 +32,10 @@ import javax.inject.Inject class FileAttachmentPreviewFragment : DialogFragment() { private lateinit var filesList: ArrayList private var conversationName: String = "" - private var uploadFiles: (files: MutableList, caption: String, compressImages: Boolean) -> Unit = - { _, _, _ -> } + private var showFilePermissionsOption: Boolean = false + private var uploadFiles: + (files: MutableList, caption: String, compressImages: Boolean, allowUpdate: Boolean) -> Unit = + { _, _, _, _ -> } private var composeView: ComposeView? = null @Inject @@ -49,7 +51,14 @@ class FileAttachmentPreviewFragment : DialogFragment() { ViewModelProvider(this, viewModelFactory)[FileAttachmentPreviewViewModel::class.java] } - fun setListener(uploadFiles: (files: MutableList, caption: String, compressImages: Boolean) -> Unit) { + fun setListener( + uploadFiles: ( + files: MutableList, + caption: String, + compressImages: Boolean, + allowUpdate: Boolean + ) -> Unit + ) { this.uploadFiles = uploadFiles } @@ -57,6 +66,7 @@ class FileAttachmentPreviewFragment : DialogFragment() { arguments?.let { filesList = it.getStringArrayList(FILES_TO_UPLOAD_ARG)!! conversationName = it.getString(CONVERSATION_NAME_ARG, "") + showFilePermissionsOption = it.getBoolean(FILE_PERMISSIONS_OPTION_ARG, false) } composeView = ComposeView(requireContext()) @@ -102,9 +112,10 @@ class FileAttachmentPreviewFragment : DialogFragment() { viewModel = viewModel, conversationName = conversationName, initialCompressImages = appPreferences.compressUploadImages, + showFilePermissionsOption = showFilePermissionsOption, onDismiss = { dismiss() }, - onSend = { files, caption, compressImages -> - uploadFiles(files.toMutableList(), caption, compressImages) + onSend = { files, caption, compressImages, allowUpdate -> + uploadFiles(files.toMutableList(), caption, compressImages, allowUpdate) dismiss() } ) @@ -123,13 +134,19 @@ class FileAttachmentPreviewFragment : DialogFragment() { private const val LIGHT_LUMINANCE_THRESHOLD = 0.5f private const val FILES_TO_UPLOAD_ARG = "FILES_TO_UPLOAD_ARG" private const val CONVERSATION_NAME_ARG = "CONVERSATION_NAME_ARG" + private const val FILE_PERMISSIONS_OPTION_ARG = "FILE_PERMISSIONS_OPTION_ARG" @JvmStatic - fun newInstance(filesToUpload: MutableList, conversationName: String): FileAttachmentPreviewFragment { + fun newInstance( + filesToUpload: MutableList, + conversationName: String, + showFilePermissionsOption: Boolean = false + ): FileAttachmentPreviewFragment { val fileAttachmentFragment = FileAttachmentPreviewFragment() val args = Bundle() args.putStringArrayList(FILES_TO_UPLOAD_ARG, ArrayList(filesToUpload)) args.putString(CONVERSATION_NAME_ARG, conversationName) + args.putBoolean(FILE_PERMISSIONS_OPTION_ARG, showFilePermissionsOption) fileAttachmentFragment.arguments = args return fileAttachmentFragment } diff --git a/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewScreen.kt b/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewScreen.kt index ada48869f6f..97d06c56313 100644 --- a/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewScreen.kt +++ b/app/src/main/java/com/nextcloud/talk/attachmentpreview/FileAttachmentPreviewScreen.kt @@ -11,8 +11,12 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia +import androidx.compose.foundation.border 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.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -23,15 +27,23 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.EditOff +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SegmentedButton -import androidx.compose.material3.SegmentedButtonDefaults -import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.darkColorScheme @@ -47,8 +59,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -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.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -67,20 +79,22 @@ private const val APP_BAR_HORIZONTAL_PADDING_DP = 4 * hosted by [FileAttachmentPreviewFragment]. [viewModel] owns the file list and its (IO-derived) * descriptions so both survive configuration changes; everything else here is ephemeral UI state. */ -@Suppress("LongMethod") +@Suppress("LongMethod", "LongParameterList") @Composable internal fun FileAttachmentPreviewContent( viewModel: FileAttachmentPreviewViewModel, conversationName: String, initialCompressImages: Boolean, + showFilePermissionsOption: Boolean = false, onDismiss: () -> Unit, - onSend: (files: List, caption: String, compressImages: Boolean) -> Unit + onSend: (files: List, caption: String, compressImages: Boolean, allowUpdate: Boolean) -> Unit ) { val context = LocalContext.current val currentFiles = viewModel.files val hasCompressibleMedia = currentFiles.any { isCompressible(FileUtils.resolveMimeType(context, it.toUri())) } var caption by rememberSaveable { mutableStateOf("") } var compressImages by rememberSaveable { mutableStateOf(hasCompressibleMedia && initialCompressImages) } + var allowUpdate by rememberSaveable { mutableStateOf(false) } LaunchedEffect(currentFiles.toSet(), compressImages) { viewModel.describeFiles(compressImages) @@ -139,21 +153,34 @@ internal fun FileAttachmentPreviewContent( } } - if (hasCompressibleMedia) { - MediaQualitySegmentedButton( - highQuality = !compressImages, - onHighQualityChange = { highQuality -> compressImages = !highQuality }, + if (showFilePermissionsOption || hasCompressibleMedia) { + Row( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp) - ) + .padding(horizontal = 16.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally) + ) { + if (showFilePermissionsOption) { + FilePermissionOptionButton( + allowUpdate = allowUpdate, + onAllowUpdateChange = { allowUpdate = it } + ) + } + + if (hasCompressibleMedia) { + MediaQualityOptionButton( + highQuality = !compressImages, + onHighQualityChange = { highQuality -> compressImages = !highQuality } + ) + } + } } CaptionInputBar( caption = caption, onCaptionChange = { caption = it }, sendEnabled = currentFiles.isNotEmpty(), - onSend = { onSend(currentFiles.toList(), caption, compressImages) } + onSend = { onSend(currentFiles.toList(), caption, compressImages, allowUpdate) } ) } } @@ -198,39 +225,134 @@ private fun PreviewTopBar(conversationName: String, onDismiss: () -> Unit) { } } +private const val OPTION_BUTTON_ICON_SIZE_DP = 16 +private const val OPTION_BUTTON_HORIZONTAL_PADDING_DP = 12 +private const val OPTION_BUTTON_VERTICAL_PADDING_DP = 6 +private const val OPTION_BUTTON_LABEL_PADDING_DP = 4 +private const val TEXT_BADGE_BORDER_DP = 1 +private const val TEXT_BADGE_CORNER_RADIUS_DP = 4 +private const val TEXT_BADGE_HORIZONTAL_PADDING_DP = 3 + @Composable -private fun MediaQualitySegmentedButton( - highQuality: Boolean, - onHighQualityChange: (Boolean) -> Unit, - modifier: Modifier = Modifier -) { - SingleChoiceSegmentedButtonRow(modifier = modifier) { - SegmentedButton( - selected = highQuality, - onClick = { onHighQualityChange(true) }, - shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2), - icon = { - Icon( - painter = painterResource(R.drawable.high_quality_24px), - contentDescription = null, - modifier = Modifier.size(SegmentedButtonDefaults.IconSize) - ) - }, - label = { Text(stringResource(R.string.nc_media_quality_original)) } +private fun OptionButton(label: String, onClick: () -> Unit, icon: @Composable () -> Unit) { + Button( + onClick = onClick, + shape = CircleShape, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant, + contentColor = MaterialTheme.colorScheme.onSurfaceVariant + ), + contentPadding = PaddingValues( + horizontal = OPTION_BUTTON_HORIZONTAL_PADDING_DP.dp, + vertical = OPTION_BUTTON_VERTICAL_PADDING_DP.dp + ) + ) { + icon() + Text( + text = label, + style = MaterialTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.padding(horizontal = OPTION_BUTTON_LABEL_PADDING_DP.dp) ) - SegmentedButton( - selected = !highQuality, - onClick = { onHighQualityChange(false) }, - shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2), + Icon( + imageVector = Icons.Filled.ArrowDropDown, + contentDescription = null, + modifier = Modifier.size(OPTION_BUTTON_ICON_SIZE_DP.dp) + ) + } +} + +@Composable +private fun TextBadge(text: String) { + Text( + text = text, + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Bold, + color = LocalContentColor.current, + modifier = Modifier + .border( + width = TEXT_BADGE_BORDER_DP.dp, + color = LocalContentColor.current, + shape = RoundedCornerShape(TEXT_BADGE_CORNER_RADIUS_DP.dp) + ) + .padding(horizontal = TEXT_BADGE_HORIZONTAL_PADDING_DP.dp) + ) +} + +@Composable +private fun MediaQualityOptionButton(highQuality: Boolean, onHighQualityChange: (Boolean) -> Unit) { + var expanded by remember { mutableStateOf(false) } + + Box { + OptionButton( + label = stringResource( + if (highQuality) R.string.nc_media_quality_original else R.string.nc_media_quality_reduced + ), + onClick = { expanded = true }, + icon = { TextBadge(if (highQuality) "HD" else "SD") } + ) + DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + DropdownMenuItem( + text = { Text(stringResource(R.string.nc_media_quality_original)) }, + leadingIcon = { TextBadge("HD") }, + trailingIcon = { if (highQuality) Icon(Icons.Filled.Check, contentDescription = null) }, + onClick = { + onHighQualityChange(true) + expanded = false + } + ) + DropdownMenuItem( + text = { Text(stringResource(R.string.nc_media_quality_reduced)) }, + leadingIcon = { TextBadge("SD") }, + trailingIcon = { if (!highQuality) Icon(Icons.Filled.Check, contentDescription = null) }, + onClick = { + onHighQualityChange(false) + expanded = false + } + ) + } + } +} + +@Composable +private fun FilePermissionOptionButton(allowUpdate: Boolean, onAllowUpdateChange: (Boolean) -> Unit) { + var expanded by remember { mutableStateOf(false) } + + Box { + OptionButton( + label = stringResource( + if (allowUpdate) R.string.nc_file_permission_editable else R.string.nc_file_permission_view_only + ), + onClick = { expanded = true }, icon = { Icon( - painter = painterResource(R.drawable.high_quality_off_24px), + imageVector = if (allowUpdate) Icons.Filled.Edit else Icons.Filled.EditOff, contentDescription = null, - modifier = Modifier.size(SegmentedButtonDefaults.IconSize) + modifier = Modifier.size(OPTION_BUTTON_ICON_SIZE_DP.dp) ) - }, - label = { Text(stringResource(R.string.nc_media_quality_reduced)) } + } ) + DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + DropdownMenuItem( + text = { Text(stringResource(R.string.nc_file_permission_view_only)) }, + leadingIcon = { Icon(Icons.Filled.EditOff, contentDescription = null) }, + trailingIcon = { if (!allowUpdate) Icon(Icons.Filled.Check, contentDescription = null) }, + onClick = { + onAllowUpdateChange(false) + expanded = false + } + ) + DropdownMenuItem( + text = { Text(stringResource(R.string.nc_file_permission_editable)) }, + leadingIcon = { Icon(Icons.Filled.Edit, contentDescription = null) }, + trailingIcon = { if (allowUpdate) Icon(Icons.Filled.Check, contentDescription = null) }, + onClick = { + onAllowUpdateChange(true) + expanded = false + } + ) + } } } @@ -263,7 +385,7 @@ private fun FileAttachmentPreviewContentPreview() { conversationName = "Team Chat", initialCompressImages = true, onDismiss = {}, - onSend = { _, _, _ -> } + onSend = { _, _, _, _ -> } ) } } @@ -277,7 +399,7 @@ private fun FileAttachmentPreviewContentSingleFilePreview() { conversationName = "Team Chat", initialCompressImages = true, onDismiss = {}, - onSend = { _, _, _ -> } + onSend = { _, _, _, _ -> } ) } } diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index 2cad07cae7a..19578d31f25 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -2578,10 +2578,11 @@ class ChatActivity : val newFragment = FileAttachmentPreviewFragment.newInstance( filesToUpload.map { it.toString() }.toMutableList(), - currentConversation?.displayName ?: "" + currentConversation?.displayName ?: "", + CapabilitiesUtil.hasConversationSubfoldersForAttachments(spreedCapabilities) ) - newFragment.setListener { files, caption, compressImages -> - uploadFiles(files, caption, compressImages) + newFragment.setListener { files, caption, compressImages, allowUpdate -> + uploadFiles(files, caption, compressImages, allowUpdate) } newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG) } catch (e: IllegalStateException) { @@ -2663,10 +2664,11 @@ class ChatActivity : if (permissionUtil.isFilesPermissionGranted()) { val newFragment = FileAttachmentPreviewFragment.newInstance( filesToUpload, - currentConversation?.displayName ?: "" + currentConversation?.displayName ?: "", + CapabilitiesUtil.hasConversationSubfoldersForAttachments(spreedCapabilities) ) - newFragment.setListener { files, caption, compressImages -> - uploadFiles(files, caption, compressImages) + newFragment.setListener { files, caption, compressImages, allowUpdate -> + uploadFiles(files, caption, compressImages, allowUpdate) } newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG) } else { @@ -2775,7 +2777,12 @@ class ChatActivity : } } - private fun uploadFiles(files: MutableList, caption: String = "", compressImages: Boolean = false) { + private fun uploadFiles( + files: MutableList, + caption: String = "", + compressImages: Boolean = false, + allowUpdate: Boolean = false + ) { val uploadId = UUID.randomUUID().toString() for (i in 0 until files.size) { uploadFile( @@ -2787,7 +2794,8 @@ class ChatActivity : displayName = currentConversation?.displayName!!, compressImages = compressImages, uploadId = uploadId, - order = i + 1 + order = i + 1, + allowUpdate = allowUpdate ) } } @@ -4180,7 +4188,8 @@ class ChatActivity : displayName: String, compressImages: Boolean = false, uploadId: String? = null, - order: Int = 1 + order: Int = 1, + allowUpdate: Boolean = false ) { chatViewModel.uploadFile( fileUri, @@ -4191,7 +4200,8 @@ class ChatActivity : displayName, compressImages, uploadId, - order + order, + allowUpdate ) cancelReply() } diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt index d78fa8b8a22..88d2fa78ad5 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt @@ -2235,7 +2235,8 @@ class ChatViewModel @AssistedInject constructor( displayName: String, compressImages: Boolean = false, uploadId: String? = null, - order: Int = 1 + order: Int = 1, + allowUpdate: Boolean = false ) { val metaDataMap = mutableMapOf() var room = "" @@ -2295,7 +2296,8 @@ class ChatViewModel @AssistedInject constructor( metaData = metaData, referenceId = referenceId, internalConversationId = internalConversationId, - compressImages = compressImages + compressImages = compressImages, + allowUpdate = allowUpdate ) if (!isVoiceMessage) { diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index e3c2929f954..3096c5138ca 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -170,12 +170,14 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa val useConversationSubfolders = CapabilitiesUtil.hasConversationSubfoldersForAttachments( currentUser.capabilities!!.spreedCapability!! ) + val allowUpdate = inputData.getBoolean(ALLOW_UPDATE, false) file?.let { isChunkedUploading = it.length() > CHUNK_UPLOAD_THRESHOLD_SIZE } val uploadSuccess: Boolean = uploadFile( sourceFileUri = sourceFileUri, metaData = metaData, remotePath = remotePath, - useConversationSubfolders = useConversationSubfolders + useConversationSubfolders = useConversationSubfolders, + allowUpdate = allowUpdate ) if (uploadSuccess && (isStopped || isCancelled())) { @@ -253,12 +255,13 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa sourceFileUri: Uri, metaData: String?, remotePath: String, - useConversationSubfolders: Boolean + useConversationSubfolders: Boolean, + allowUpdate: Boolean ): Boolean = if (file == null) { false } else if (useConversationSubfolders) { - uploadUsingConversationSubfolders(sourceFileUri, metaData) + uploadUsingConversationSubfolders(sourceFileUri, metaData, allowUpdate) } else if (isChunkedUploading) { Log.d(TAG, "starting chunked upload because size is " + file!!.length()) val mimeType = context.contentResolver.getType(sourceFileUri)?.toMediaTypeOrNull() @@ -312,7 +315,11 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa return result } - private fun uploadUsingConversationSubfolders(sourceFileUri: Uri, metaData: String?): Boolean = + private fun uploadUsingConversationSubfolders( + sourceFileUri: Uri, + metaData: String?, + allowUpdate: Boolean + ): Boolean = runBlocking { val credentials = ApiUtils.getCredentials( currentUser.username, @@ -321,6 +328,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa val uploadId = UUID.randomUUID().toString() val fileNames = ProbeConversationAttachmentRequest().apply { fileNames = listOf(fileName) + this.allowUpdate = allowUpdate } val probeResponse = ncApiCoroutines.probeConversationAttachmentFolder( @@ -360,6 +368,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa referenceId = this@UploadAndShareFilesWorker.referenceId.orEmpty() talkMetaData = metaData fileName = predictedName + this.allowUpdate = allowUpdate } runCatching { @@ -458,6 +467,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa const val KEY_INTERNAL_CONVERSATION_ID = "INTERNAL_CONVERSATION_ID" const val PROGRESS_KEY = "UPLOAD_PROGRESS" private const val COMPRESS_IMAGES = "COMPRESS_IMAGES" + private const val ALLOW_UPDATE = "ALLOW_UPDATE" private const val CHUNK_UPLOAD_THRESHOLD_SIZE: Long = 1024 * 1024 // Total attempts allowed for a single upload (1 initial run + retries) before giving up on a @@ -523,7 +533,8 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa metaData: String?, referenceId: String = "", internalConversationId: String = "", - compressImages: Boolean = false + compressImages: Boolean = false, + allowUpdate: Boolean = false ): UUID { val data: Data = Data.Builder() .putString(DEVICE_SOURCE_FILE, fileUri) @@ -533,6 +544,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa .putString(KEY_REFERENCE_ID, referenceId) .putString(KEY_INTERNAL_CONVERSATION_ID, internalConversationId) .putBoolean(COMPRESS_IMAGES, compressImages) + .putBoolean(ALLOW_UPDATE, allowUpdate) .build() val uploadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadAndShareFilesWorker::class.java) .setInputData(data) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chatpostattachment/PostConversationAttachmentRequest.kt b/app/src/main/java/com/nextcloud/talk/models/json/chatpostattachment/PostConversationAttachmentRequest.kt index 6f49c5a7559..3184b48c83d 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chatpostattachment/PostConversationAttachmentRequest.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chatpostattachment/PostConversationAttachmentRequest.kt @@ -24,4 +24,7 @@ class PostConversationAttachmentRequest { @JsonField(name = ["fileName"]) var fileName: String? = null + + @JsonField(name = ["allowUpdate"]) + var allowUpdate: Boolean? = false } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chatprobeattachmentfolder/ProbeConversationAttachmentRequest.kt b/app/src/main/java/com/nextcloud/talk/models/json/chatprobeattachmentfolder/ProbeConversationAttachmentRequest.kt index d6f873240da..a23bc3c9eae 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chatprobeattachmentfolder/ProbeConversationAttachmentRequest.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chatprobeattachmentfolder/ProbeConversationAttachmentRequest.kt @@ -15,4 +15,7 @@ class ProbeConversationAttachmentRequest { @JsonField(name = ["fileNames"]) var fileNames: List? = null + + @JsonField(name = ["allowUpdate"]) + var allowUpdate: Boolean? = false } diff --git a/app/src/main/res/drawable/high_quality_24px.xml b/app/src/main/res/drawable/high_quality_24px.xml deleted file mode 100644 index d22e94581c6..00000000000 --- a/app/src/main/res/drawable/high_quality_24px.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/high_quality_off_24px.xml b/app/src/main/res/drawable/high_quality_off_24px.xml deleted file mode 100644 index a56255f6da2..00000000000 --- a/app/src/main/res/drawable/high_quality_off_24px.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 359b1887b85..8c4831830f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,6 +169,8 @@ How to translate with transifex: Original quality Reduced quality Sending original quality uses more mobile data. + View-only + Editable Close video playback read_privacy Tap to unlock