mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 08:25:22 +00:00
Camera take picture (#484)
* camera capture * icon change * image preview great * remove string
This commit is contained in:
@@ -152,18 +152,8 @@ class ChatViewModel(
|
||||
}
|
||||
|
||||
fun cancelMediaSend(messageId: String) {
|
||||
val transferId = synchronized(transferMessageMap) { messageTransferMap[messageId] }
|
||||
if (transferId != null) {
|
||||
val cancelled = meshService.cancelFileTransfer(transferId)
|
||||
if (cancelled) {
|
||||
// Remove the message from chat upon explicit cancel
|
||||
messageManager.removeMessageById(messageId)
|
||||
synchronized(transferMessageMap) {
|
||||
transferMessageMap.remove(transferId)
|
||||
messageTransferMap.remove(messageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delegate to MediaSendingManager which tracks transfer IDs and cleans up UI state
|
||||
mediaSendingManager.cancelMediaSend(messageId)
|
||||
}
|
||||
|
||||
private fun loadAndInitialize() {
|
||||
|
||||
@@ -275,6 +275,9 @@ class MediaSendingManager(
|
||||
if (transferId != null) {
|
||||
val cancelled = meshService.cancelFileTransfer(transferId)
|
||||
if (cancelled) {
|
||||
// Try to remove cached local file for this message (if any)
|
||||
runCatching { findMessagePathById(messageId)?.let { java.io.File(it).delete() } }
|
||||
|
||||
// Remove the message from chat upon explicit cancel
|
||||
messageManager.removeMessageById(messageId)
|
||||
synchronized(transferMessageMap) {
|
||||
@@ -285,6 +288,20 @@ class MediaSendingManager(
|
||||
}
|
||||
}
|
||||
|
||||
private fun findMessagePathById(messageId: String): String? {
|
||||
// Search main timeline
|
||||
state.getMessagesValue().firstOrNull { it.id == messageId }?.content?.let { return it }
|
||||
// Search private chats
|
||||
state.getPrivateChatsValue().values.forEach { list ->
|
||||
list.firstOrNull { it.id == messageId }?.content?.let { return it }
|
||||
}
|
||||
// Search channel messages
|
||||
state.getChannelMessagesValue().values.forEach { list ->
|
||||
list.firstOrNull { it.id == messageId }?.content?.let { return it }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Update progress for a transfer
|
||||
*/
|
||||
|
||||
@@ -1,26 +1,37 @@
|
||||
package com.bitchat.android.ui.media
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Camera
|
||||
import androidx.compose.material.icons.filled.Photo
|
||||
import androidx.compose.material.icons.filled.PhotoCamera
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.FileProvider
|
||||
import com.bitchat.android.features.media.ImageUtils
|
||||
import java.io.File
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ImagePickerButton(
|
||||
modifier: Modifier = Modifier,
|
||||
onImageReady: (String) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var capturedImagePath by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
val imagePicker = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.GetContent()
|
||||
) { uri: android.net.Uri? ->
|
||||
@@ -29,17 +40,57 @@ fun ImagePickerButton(
|
||||
if (!outPath.isNullOrBlank()) onImageReady(outPath)
|
||||
}
|
||||
}
|
||||
|
||||
val takePictureLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.TakePicture()
|
||||
) { success ->
|
||||
val path = capturedImagePath
|
||||
if (success && !path.isNullOrBlank()) {
|
||||
// Downscale + correct orientation, then send; delete original
|
||||
val outPath = com.bitchat.android.features.media.ImageUtils.downscalePathAndSaveToAppFiles(context, path)
|
||||
if (!outPath.isNullOrBlank()) {
|
||||
onImageReady(outPath)
|
||||
}
|
||||
runCatching { File(path).delete() }
|
||||
} else {
|
||||
// Cleanup on cancel/failure
|
||||
path?.let { runCatching { File(it).delete() } }
|
||||
}
|
||||
capturedImagePath = null
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = { imagePicker.launch("image/*") },
|
||||
modifier = modifier.size(32.dp)
|
||||
fun startCameraCapture() {
|
||||
try {
|
||||
val dir = File(context.filesDir, "images/outgoing").apply { mkdirs() }
|
||||
val file = File(dir, "camera_${System.currentTimeMillis()}.jpg")
|
||||
val uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
context.packageName + ".fileprovider",
|
||||
file
|
||||
)
|
||||
capturedImagePath = file.absolutePath
|
||||
takePictureLauncher.launch(uri)
|
||||
} catch (_: Exception) {
|
||||
// Ignore errors; no-op
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(32.dp)
|
||||
.combinedClickable(
|
||||
onClick = { imagePicker.launch("image/*") },
|
||||
onLongClick = { startCameraCapture() }
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Photo,
|
||||
imageVector = Icons.Filled.PhotoCamera,
|
||||
contentDescription = stringResource(com.bitchat.android.R.string.pick_image),
|
||||
tint = Color.Gray,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// No custom preview: native camera UI handles confirmation
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user