diff --git a/android/app/src/main/java/xyz/kusoft/qmax/MainActivity.kt b/android/app/src/main/java/xyz/kusoft/qmax/MainActivity.kt index 04bb4b0..642093e 100644 --- a/android/app/src/main/java/xyz/kusoft/qmax/MainActivity.kt +++ b/android/app/src/main/java/xyz/kusoft/qmax/MainActivity.kt @@ -2510,7 +2510,7 @@ private fun MaterialsMediaCell( private fun MaterialsFileList(items: List, vm: QMaxViewModel) { LazyColumn(Modifier.fillMaxSize()) { items(items, key = { it.attachment.id }) { item -> - FileAttachment(item.attachment, vm) + FileAttachment(item.attachment, vm, onLongPress = {}) HorizontalDivider(color = Color(0xFFE7EEF3)) } } @@ -2521,7 +2521,7 @@ private fun MaterialsVoiceList(items: List, session: QMaxSes LazyColumn(Modifier.fillMaxSize()) { items(items, key = { it.attachment.id }) { item -> val url = "${session.serverUrl.trimEnd('/')}${item.attachment.downloadPath}" - VoiceAttachment(item.attachment, url, session.accessToken) + VoiceAttachment(item.attachment, url, session.accessToken, onLongPress = {}) HorizontalDivider(color = Color(0xFFE7EEF3)) } } @@ -2645,7 +2645,8 @@ private fun MessageRow( session = session, vm = vm, imagePreviewSources = imagePreviewSources, - mediaShape = shape + mediaShape = shape, + onLongPress = { actionsOpen = true } ) } else { sortedAttachments.forEach { attachment -> @@ -2654,7 +2655,8 @@ private fun MessageRow( session = session, vm = vm, imagePreviewSources = imagePreviewSources, - mediaShape = shape + mediaShape = shape, + onLongPress = { actionsOpen = true } ) } } @@ -2936,7 +2938,8 @@ private fun MediaAlbumGrid( session: QMaxSession, vm: QMaxViewModel, imagePreviewSources: List, - mediaShape: RoundedCornerShape + mediaShape: RoundedCornerShape, + onLongPress: () -> Unit ) { val visible = attachments.take(4) val extraCount = (attachments.size - visible.size).coerceAtLeast(0) @@ -2956,6 +2959,7 @@ private fun MediaAlbumGrid( vm = vm, imagePreviewSources = imagePreviewSources, extraCount = extraCount.takeIf { index == visible.lastIndex }, + onLongPress = onLongPress, modifier = Modifier.weight(1f).aspectRatio(1f) ) } @@ -2967,6 +2971,7 @@ private fun MediaAlbumGrid( vm = vm, imagePreviewSources = imagePreviewSources, extraCount = null, + onLongPress = onLongPress, modifier = Modifier.fillMaxWidth().aspectRatio(1.85f) ) Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) { @@ -2977,6 +2982,7 @@ private fun MediaAlbumGrid( vm = vm, imagePreviewSources = imagePreviewSources, extraCount = extraCount.takeIf { index == 1 }, + onLongPress = onLongPress, modifier = Modifier.weight(1f).aspectRatio(1f) ) } @@ -2993,6 +2999,7 @@ private fun MediaAlbumGrid( vm = vm, imagePreviewSources = imagePreviewSources, extraCount = extraCount.takeIf { isLastVisibleCell }, + onLongPress = onLongPress, modifier = Modifier.weight(1f).aspectRatio(1f) ) } @@ -3007,12 +3014,14 @@ private fun MediaAlbumGrid( } @Composable +@OptIn(ExperimentalFoundationApi::class) private fun MediaAlbumCell( attachment: AttachmentDto, session: QMaxSession, vm: QMaxViewModel, imagePreviewSources: List, extraCount: Int?, + onLongPress: () -> Unit, modifier: Modifier = Modifier ) { val url = rememberAttachmentUrl(session, attachment) @@ -3037,7 +3046,10 @@ private fun MediaAlbumCell( contentScale = ContentScale.Crop, modifier = Modifier .fillMaxSize() - .clickable { vm.openImage(previewSource, imagePreviewSources) }, + .combinedClickable( + onClick = { vm.openImage(previewSource, imagePreviewSources) }, + onLongClick = onLongPress + ), onSuccess = { imageLoaded = true imageFailed = false @@ -3091,26 +3103,29 @@ private fun AttachmentView( session: QMaxSession, vm: QMaxViewModel, imagePreviewSources: List, - mediaShape: RoundedCornerShape + mediaShape: RoundedCornerShape, + onLongPress: () -> Unit ) { val url = rememberAttachmentUrl(session, attachment) val state by vm.state val cachedImagePath = state.cachedImagePaths[attachment.id] when { - attachment.isContactAttachment() -> ContactAttachment(attachment, vm) + attachment.isContactAttachment() -> ContactAttachment(attachment, vm, onLongPress) attachment.isEmojiAttachment() -> EmojiAttachment( attachment, url, session.accessToken, cachedImagePath, - vm + vm, + onLongPress ) attachment.isStickerAttachment() -> StickerAttachment( attachment, url, session.accessToken, cachedImagePath, - vm + vm, + onLongPress ) attachment.isImageAttachment() -> ImageAttachment( attachment, @@ -3119,21 +3134,24 @@ private fun AttachmentView( cachedImagePath, vm, imagePreviewSources, - mediaShape + mediaShape, + onLongPress ) - attachment.isVideoAttachment() -> VideoAttachment(attachment, url, session.accessToken) - attachment.isVoiceAttachment() || attachment.isAudioAttachment() -> VoiceAttachment(attachment, url, session.accessToken) - else -> FileAttachment(attachment, vm) + attachment.isVideoAttachment() -> VideoAttachment(attachment, url, session.accessToken, onLongPress) + attachment.isVoiceAttachment() || attachment.isAudioAttachment() -> VoiceAttachment(attachment, url, session.accessToken, onLongPress) + else -> FileAttachment(attachment, vm, onLongPress) } } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun StickerAttachment( attachment: AttachmentDto, url: String, token: String, cachedImagePath: String?, - vm: QMaxViewModel + vm: QMaxViewModel, + onLongPress: () -> Unit ) { LaunchedEffect(attachment.id, url) { vm.cacheImage(attachment) @@ -3145,7 +3163,10 @@ private fun StickerAttachment( modifier = Modifier .size(156.dp) .clip(RoundedCornerShape(6.dp)) - .clickable { vm.openImage(cachedImagePath ?: url, listOf(cachedImagePath ?: url)) }, + .combinedClickable( + onClick = { vm.openImage(cachedImagePath ?: url, listOf(cachedImagePath ?: url)) }, + onLongClick = onLongPress + ), contentAlignment = Alignment.Center ) { AsyncImage( @@ -3168,13 +3189,15 @@ private fun StickerAttachment( } } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun EmojiAttachment( attachment: AttachmentDto, url: String, token: String, cachedImagePath: String?, - vm: QMaxViewModel + vm: QMaxViewModel, + onLongPress: () -> Unit ) { LaunchedEffect(attachment.id, url) { vm.cacheImage(attachment) @@ -3185,10 +3208,16 @@ private fun EmojiAttachment( model = model, contentDescription = attachment.fileName, contentScale = ContentScale.Fit, - modifier = Modifier.size(emojiSize) + modifier = Modifier + .size(emojiSize) + .combinedClickable( + onClick = {}, + onLongClick = onLongPress + ) ) } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun ImageAttachment( attachment: AttachmentDto, @@ -3197,7 +3226,8 @@ private fun ImageAttachment( cachedImagePath: String?, vm: QMaxViewModel, imagePreviewSources: List, - mediaShape: RoundedCornerShape + mediaShape: RoundedCornerShape, + onLongPress: () -> Unit ) { LaunchedEffect(attachment.id, url) { vm.cacheImage(attachment) @@ -3213,7 +3243,10 @@ private fun ImageAttachment( .aspectRatio(aspectRatio) .clip(mediaShape) .background(Color(0xFFE1E8ED)) - .clickable { vm.openImage(previewSource, imagePreviewSources) } + .combinedClickable( + onClick = { vm.openImage(previewSource, imagePreviewSources) }, + onLongClick = onLongPress + ) ) { AsyncImage( model = model, @@ -3283,14 +3316,19 @@ private fun MediaImagePlaceholder(failed: Boolean) { } } +@OptIn(ExperimentalFoundationApi::class) @Composable -private fun VideoAttachment(attachment: AttachmentDto, url: String, token: String) { +private fun VideoAttachment(attachment: AttachmentDto, url: String, token: String, onLongPress: () -> Unit) { val player = rememberAuthorizedPlayer(url = url, token = token) Box( modifier = Modifier .fillMaxWidth() .aspectRatio(16f / 9f) .background(Color.Black) + .combinedClickable( + onClick = {}, + onLongClick = onLongPress + ) ) { AndroidView( factory = { context -> @@ -3321,11 +3359,23 @@ private fun VideoAttachment(attachment: AttachmentDto, url: String, token: Strin ) } } + Surface( + color = Color(0x99000000), + shape = CircleShape, + modifier = Modifier + .align(Alignment.TopEnd) + .padding(6.dp) + ) { + IconButton(onClick = onLongPress, modifier = Modifier.size(36.dp)) { + Icon(Icons.AutoMirrored.Filled.Forward, contentDescription = "Переслать", tint = Color.White) + } + } } } @Composable -private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: String) { +@OptIn(ExperimentalFoundationApi::class) +private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: String, onLongPress: () -> Unit) { val player = rememberAuthorizedPlayer(url = url, token = token) var isPlaying by remember { mutableStateOf(false) } var position by remember { mutableStateOf(0L) } @@ -3343,6 +3393,10 @@ private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: Strin Row( modifier = Modifier .fillMaxWidth() + .combinedClickable( + onClick = {}, + onLongClick = onLongPress + ) .padding(horizontal = 2.dp, vertical = 2.dp), verticalAlignment = Alignment.CenterVertically ) { @@ -3398,15 +3452,19 @@ private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: Strin } } +@OptIn(ExperimentalFoundationApi::class) @Composable -private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel) { +private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel, onLongPress: () -> Unit) { val title = attachment.displayFileName() .removeSuffix(".vcf") .ifBlank { "\u041a\u043e\u043d\u0442\u0430\u043a\u0442" } Row( modifier = Modifier .fillMaxWidth() - .clickable { vm.openAttachment(attachment) } + .combinedClickable( + onClick = { vm.openAttachment(attachment) }, + onLongClick = onLongPress + ) .padding(horizontal = 4.dp, vertical = 7.dp), verticalAlignment = Alignment.CenterVertically ) { @@ -3438,8 +3496,9 @@ private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel) { } } +@OptIn(ExperimentalFoundationApi::class) @Composable -private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel) { +private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel, onLongPress: () -> Unit) { val title = attachment.displayFileName() val subtitle = if (attachment.fileSizeBytes > 0L) { "${formatFileSize(attachment.fileSizeBytes)} \u2022 \u041d\u0430\u0436\u043c\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c" @@ -3449,7 +3508,10 @@ private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel) { Row( modifier = Modifier .fillMaxWidth() - .clickable { vm.openAttachment(attachment) } + .combinedClickable( + onClick = { vm.openAttachment(attachment) }, + onLongClick = onLongPress + ) .padding(horizontal = 4.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically ) {