mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
ui: types and stubs to encrypt local files (#3003)
* ui: types and stubs to encrypt local files * ios: encrypt automatically received images in local storage * encrypt sent images, marked to be received via NSE * ios: encrypt sent and received local voice files * encrypt sent and received local files * fix NSE * remove comment * decrypt files in background thread
This commit is contained in:
committed by
GitHub
parent
a27f30ce12
commit
748572ace9
+15
-2
@@ -2024,7 +2024,7 @@ class CIFile(
|
||||
val fileId: Long,
|
||||
val fileName: String,
|
||||
val fileSize: Long,
|
||||
val filePath: String? = null,
|
||||
val fileSource: CryptoFile? = null,
|
||||
val fileStatus: CIFileStatus,
|
||||
val fileProtocol: FileProtocol
|
||||
) {
|
||||
@@ -2072,10 +2072,23 @@ class CIFile(
|
||||
filePath: String? = "test.txt",
|
||||
fileStatus: CIFileStatus = CIFileStatus.RcvComplete
|
||||
): CIFile =
|
||||
CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, filePath = filePath, fileStatus = fileStatus, fileProtocol = FileProtocol.XFTP)
|
||||
CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, fileSource = if (filePath == null) null else CryptoFile.plain(filePath), fileStatus = fileStatus, fileProtocol = FileProtocol.XFTP)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class CryptoFile(
|
||||
val filePath: String,
|
||||
val cryptoArgs: CryptoFileArgs?
|
||||
) {
|
||||
companion object {
|
||||
fun plain(f: String): CryptoFile = CryptoFile(f, null)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class CryptoFileArgs(val fileKey: String, val fileNonce: String)
|
||||
|
||||
class CancelAction(
|
||||
val uiActionId: StringResource,
|
||||
val alert: AlertInfo
|
||||
|
||||
+11
-10
@@ -586,7 +586,7 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiSendMessage(type: ChatType, id: Long, file: String? = null, quotedItemId: Long? = null, mc: MsgContent, live: Boolean = false, ttl: Int? = null): AChatItem? {
|
||||
suspend fun apiSendMessage(type: ChatType, id: Long, file: CryptoFile? = null, quotedItemId: Long? = null, mc: MsgContent, live: Boolean = false, ttl: Int? = null): AChatItem? {
|
||||
val cmd = CC.ApiSendMessage(type, id, file, quotedItemId, mc, live, ttl)
|
||||
val r = sendCmd(cmd)
|
||||
return when (r) {
|
||||
@@ -1079,8 +1079,8 @@ object ChatController {
|
||||
return false
|
||||
}
|
||||
|
||||
suspend fun apiReceiveFile(fileId: Long, inline: Boolean? = null, auto: Boolean = false): AChatItem? {
|
||||
val r = sendCmd(CC.ReceiveFile(fileId, inline))
|
||||
suspend fun apiReceiveFile(fileId: Long, encrypted: Boolean, inline: Boolean? = null, auto: Boolean = false): AChatItem? {
|
||||
val r = sendCmd(CC.ReceiveFile(fileId, encrypted, inline))
|
||||
return when (r) {
|
||||
is CR.RcvFileAccepted -> r.chatItem
|
||||
is CR.RcvFileAcceptedSndCancelled -> {
|
||||
@@ -1413,7 +1413,8 @@ object ChatController {
|
||||
((mc is MsgContent.MCImage && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV)
|
||||
|| (mc is MsgContent.MCVideo && file.fileSize <= MAX_VIDEO_SIZE_AUTO_RCV)
|
||||
|| (mc is MsgContent.MCVoice && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && file.fileStatus !is CIFileStatus.RcvAccepted))) {
|
||||
withApi { receiveFile(r.user, file.fileId, auto = true) }
|
||||
// TODO encrypt images and voice
|
||||
withApi { receiveFile(r.user, file.fileId, encrypted = false, auto = true) }
|
||||
}
|
||||
if (cItem.showNotification && (allowedToShowNotification() || chatModel.chatId.value != cInfo.id)) {
|
||||
ntfManager.notifyMessageReceived(r.user, cInfo, cItem)
|
||||
@@ -1647,8 +1648,8 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun receiveFile(user: UserLike, fileId: Long, auto: Boolean = false) {
|
||||
val chatItem = apiReceiveFile(fileId, auto = auto)
|
||||
suspend fun receiveFile(user: UserLike, fileId: Long, encrypted: Boolean, auto: Boolean = false) {
|
||||
val chatItem = apiReceiveFile(fileId, encrypted = encrypted, auto = auto)
|
||||
if (chatItem != null) {
|
||||
chatItemSimpleUpdate(user, chatItem)
|
||||
}
|
||||
@@ -1804,7 +1805,7 @@ sealed class CC {
|
||||
class ApiGetChats(val userId: Long): CC()
|
||||
class ApiGetChat(val type: ChatType, val id: Long, val pagination: ChatPagination, val search: String = ""): CC()
|
||||
class ApiGetChatItemInfo(val type: ChatType, val id: Long, val itemId: Long): CC()
|
||||
class ApiSendMessage(val type: ChatType, val id: Long, val file: String?, val quotedItemId: Long?, val mc: MsgContent, val live: Boolean, val ttl: Int?): CC()
|
||||
class ApiSendMessage(val type: ChatType, val id: Long, val file: CryptoFile?, val quotedItemId: Long?, val mc: MsgContent, val live: Boolean, val ttl: Int?): CC()
|
||||
class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val mc: MsgContent, val live: Boolean): CC()
|
||||
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC()
|
||||
class ApiDeleteMemberChatItem(val groupId: Long, val groupMemberId: Long, val itemId: Long): CC()
|
||||
@@ -1867,7 +1868,7 @@ sealed class CC {
|
||||
class ApiRejectContact(val contactReqId: Long): CC()
|
||||
class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC()
|
||||
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
|
||||
class ReceiveFile(val fileId: Long, val inline: Boolean?): CC()
|
||||
class ReceiveFile(val fileId: Long, val encrypted: Boolean, val inline: Boolean?): CC()
|
||||
class CancelFile(val fileId: Long): CC()
|
||||
class ShowVersion(): CC()
|
||||
|
||||
@@ -1972,7 +1973,7 @@ sealed class CC {
|
||||
is ApiCallStatus -> "/_call status @${contact.apiId} ${callStatus.value}"
|
||||
is ApiChatRead -> "/_read chat ${chatRef(type, id)} from=${range.from} to=${range.to}"
|
||||
is ApiChatUnread -> "/_unread chat ${chatRef(type, id)} ${onOff(unreadChat)}"
|
||||
is ReceiveFile -> if (inline == null) "/freceive $fileId" else "/freceive $fileId inline=${onOff(inline)}"
|
||||
is ReceiveFile -> "/freceive $fileId encrypt=${onOff(encrypted)}" + (if (inline == null) "" else " inline=${onOff(inline)}")
|
||||
is CancelFile -> "/fcancel $fileId"
|
||||
is ShowVersion -> "/version"
|
||||
}
|
||||
@@ -2134,7 +2135,7 @@ sealed class ChatPagination {
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class ComposedMessage(val filePath: String?, val quotedItemId: Long?, val msgContent: MsgContent)
|
||||
class ComposedMessage(val fileSource: CryptoFile?, val quotedItemId: Long?, val msgContent: MsgContent)
|
||||
|
||||
@Serializable
|
||||
class XFTPFileConfig(val minFileSize: Long)
|
||||
|
||||
+3
-2
@@ -62,8 +62,9 @@ fun getAppFilePath(fileName: String): String {
|
||||
}
|
||||
|
||||
fun getLoadedFilePath(file: CIFile?): String? {
|
||||
return if (file?.filePath != null && file.loaded) {
|
||||
val filePath = getAppFilePath(file.filePath)
|
||||
val f = file?.fileSource?.filePath
|
||||
return if (f != null && file.loaded) {
|
||||
val filePath = getAppFilePath(f)
|
||||
if (File(filePath).exists()) filePath else null
|
||||
} else {
|
||||
null
|
||||
|
||||
+6
-6
@@ -244,8 +244,8 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
|
||||
}
|
||||
}
|
||||
},
|
||||
receiveFile = { fileId ->
|
||||
withApi { chatModel.controller.receiveFile(user, fileId) }
|
||||
receiveFile = { fileId, encrypted ->
|
||||
withApi { chatModel.controller.receiveFile(user, fileId, encrypted) }
|
||||
},
|
||||
cancelFile = { fileId ->
|
||||
withApi { chatModel.controller.cancelFile(user, fileId) }
|
||||
@@ -403,7 +403,7 @@ fun ChatLayout(
|
||||
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
||||
loadPrevMessages: (ChatInfo) -> Unit,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
cancelFile: (Long) -> Unit,
|
||||
joinGroup: (Long) -> Unit,
|
||||
startCall: (CallMediaType) -> Unit,
|
||||
@@ -656,7 +656,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
||||
loadPrevMessages: (ChatInfo) -> Unit,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
cancelFile: (Long) -> Unit,
|
||||
joinGroup: (Long) -> Unit,
|
||||
acceptCall: (Contact) -> Unit,
|
||||
@@ -1257,7 +1257,7 @@ fun PreviewChatLayout() {
|
||||
showMemberInfo = { _, _ -> },
|
||||
loadPrevMessages = { _ -> },
|
||||
deleteMessage = { _, _ -> },
|
||||
receiveFile = {},
|
||||
receiveFile = { _, _ -> },
|
||||
cancelFile = {},
|
||||
joinGroup = {},
|
||||
startCall = {},
|
||||
@@ -1324,7 +1324,7 @@ fun PreviewGroupChatLayout() {
|
||||
showMemberInfo = { _, _ -> },
|
||||
loadPrevMessages = { _ -> },
|
||||
deleteMessage = { _, _ -> },
|
||||
receiveFile = {},
|
||||
receiveFile = { _, _ -> },
|
||||
cancelFile = {},
|
||||
joinGroup = {},
|
||||
startCall = {},
|
||||
|
||||
+7
-6
@@ -317,7 +317,7 @@ fun ComposeView(
|
||||
chatModel.filesToDelete.clear()
|
||||
}
|
||||
|
||||
suspend fun send(cInfo: ChatInfo, mc: MsgContent, quoted: Long?, file: String? = null, live: Boolean = false, ttl: Int?): ChatItem? {
|
||||
suspend fun send(cInfo: ChatInfo, mc: MsgContent, quoted: Long?, file: CryptoFile? = null, live: Boolean = false, ttl: Int?): ChatItem? {
|
||||
val aChatItem = chatModel.controller.apiSendMessage(
|
||||
type = cInfo.chatType,
|
||||
id = cInfo.apiId,
|
||||
@@ -331,7 +331,7 @@ fun ComposeView(
|
||||
chatModel.addChatItem(cInfo, aChatItem.chatItem)
|
||||
return aChatItem.chatItem
|
||||
}
|
||||
if (file != null) removeFile(file)
|
||||
if (file != null) removeFile(file.filePath)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ fun ComposeView(
|
||||
sent = updateMessage(liveMessage.chatItem, cInfo, live)
|
||||
} else {
|
||||
val msgs: ArrayList<MsgContent> = ArrayList()
|
||||
val files: ArrayList<String> = ArrayList()
|
||||
val files: ArrayList<CryptoFile> = ArrayList()
|
||||
when (val preview = cs.preview) {
|
||||
ComposePreview.NoPreview -> msgs.add(MsgContent.MCText(msgText))
|
||||
is ComposePreview.CLinkPreview -> msgs.add(checkLinkPreview())
|
||||
@@ -413,7 +413,7 @@ fun ComposeView(
|
||||
val file = when (it) {
|
||||
is UploadContent.SimpleImage -> saveImage(it.uri)
|
||||
is UploadContent.AnimatedImage -> saveAnimImage(it.uri)
|
||||
is UploadContent.Video -> saveFileFromUri(it.uri)
|
||||
is UploadContent.Video -> saveFileFromUri(it.uri, encrypted = false)
|
||||
}
|
||||
if (file != null) {
|
||||
files.add(file)
|
||||
@@ -432,12 +432,13 @@ fun ComposeView(
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.move(tmpFile.toPath(), actualFile.toPath())
|
||||
}
|
||||
files.add(actualFile.name)
|
||||
// TODO encrypt voice files
|
||||
files.add(CryptoFile.plain(actualFile.name))
|
||||
deleteUnusedFiles()
|
||||
msgs.add(MsgContent.MCVoice(if (msgs.isEmpty()) msgText else "", preview.durationMs / 1000))
|
||||
}
|
||||
is ComposePreview.FilePreview -> {
|
||||
val file = saveFileFromUri(preview.uri)
|
||||
val file = saveFileFromUri(preview.uri, encrypted = false)
|
||||
if (file != null) {
|
||||
files.add((file))
|
||||
msgs.add(MsgContent.MCFile(if (msgs.isEmpty()) msgText else ""))
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ import java.net.URI
|
||||
fun CIFileView(
|
||||
file: CIFile?,
|
||||
edited: Boolean,
|
||||
receiveFile: (Long) -> Unit
|
||||
receiveFile: (Long, Boolean) -> Unit
|
||||
) {
|
||||
val saveFileLauncher = rememberSaveFileLauncher(ciFile = file)
|
||||
|
||||
@@ -71,7 +71,7 @@ fun CIFileView(
|
||||
when (file.fileStatus) {
|
||||
is CIFileStatus.RcvInvitation -> {
|
||||
if (fileSizeValid()) {
|
||||
receiveFile(file.fileId)
|
||||
receiveFile(file.fileId, false)
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.large_file),
|
||||
|
||||
+3
-2
@@ -31,7 +31,7 @@ fun CIImageView(
|
||||
file: CIFile?,
|
||||
imageProvider: () -> ImageGalleryProvider,
|
||||
showMenu: MutableState<Boolean>,
|
||||
receiveFile: (Long) -> Unit
|
||||
receiveFile: (Long, Boolean) -> Unit
|
||||
) {
|
||||
@Composable
|
||||
fun progressIndicator() {
|
||||
@@ -152,7 +152,8 @@ fun CIImageView(
|
||||
when (file.fileStatus) {
|
||||
CIFileStatus.RcvInvitation ->
|
||||
if (fileSizeValid()) {
|
||||
receiveFile(file.fileId)
|
||||
// TODO encrypt image
|
||||
receiveFile(file.fileId, false)
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.large_file),
|
||||
|
||||
+5
-5
@@ -31,7 +31,7 @@ fun CIVideoView(
|
||||
file: CIFile?,
|
||||
imageProvider: () -> ImageGalleryProvider,
|
||||
showMenu: MutableState<Boolean>,
|
||||
receiveFile: (Long) -> Unit
|
||||
receiveFile: (Long, Boolean) -> Unit
|
||||
) {
|
||||
Box(
|
||||
Modifier.layoutId(CHAT_IMAGE_LAYOUT_ID),
|
||||
@@ -54,7 +54,7 @@ fun CIVideoView(
|
||||
if (file != null) {
|
||||
when (file.fileStatus) {
|
||||
CIFileStatus.RcvInvitation ->
|
||||
receiveFileIfValidSize(file, receiveFile)
|
||||
receiveFileIfValidSize(file, encrypted = false, receiveFile)
|
||||
CIFileStatus.RcvAccepted ->
|
||||
when (file.fileProtocol) {
|
||||
FileProtocol.XFTP ->
|
||||
@@ -80,7 +80,7 @@ fun CIVideoView(
|
||||
DurationProgress(file, remember { mutableStateOf(false) }, remember { mutableStateOf(duration * 1000L) }, remember { mutableStateOf(0L) }/*, soundEnabled*/)
|
||||
}
|
||||
if (file?.fileStatus is CIFileStatus.RcvInvitation) {
|
||||
PlayButton(error = false, { showMenu.value = true }) { receiveFileIfValidSize(file, receiveFile) }
|
||||
PlayButton(error = false, { showMenu.value = true }) { receiveFileIfValidSize(file, encrypted = false, receiveFile) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,9 +301,9 @@ private fun fileSizeValid(file: CIFile?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun receiveFileIfValidSize(file: CIFile, receiveFile: (Long) -> Unit) {
|
||||
private fun receiveFileIfValidSize(file: CIFile, encrypted: Boolean, receiveFile: (Long, Boolean) -> Unit) {
|
||||
if (fileSizeValid(file)) {
|
||||
receiveFile(file.fileId)
|
||||
receiveFile(file.fileId, encrypted)
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.large_file),
|
||||
|
||||
+11
-9
@@ -37,18 +37,19 @@ fun CIVoiceView(
|
||||
ci: ChatItem,
|
||||
timedMessagesTTL: Int?,
|
||||
longClick: () -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(top = if (hasText) 14.dp else 4.dp, bottom = if (hasText) 14.dp else 6.dp, start = if (hasText) 6.dp else 0.dp, end = if (hasText) 6.dp else 0.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (file != null) {
|
||||
val filePath = remember(file.filePath, file.fileStatus) { getLoadedFilePath(file) }
|
||||
var brokenAudio by rememberSaveable(file.filePath) { mutableStateOf(false) }
|
||||
val audioPlaying = rememberSaveable(file.filePath) { mutableStateOf(false) }
|
||||
val progress = rememberSaveable(file.filePath) { mutableStateOf(0) }
|
||||
val duration = rememberSaveable(file.filePath) { mutableStateOf(providedDurationSec * 1000) }
|
||||
val f = file.fileSource?.filePath
|
||||
val filePath = remember(f, file.fileStatus) { getLoadedFilePath(file) }
|
||||
var brokenAudio by rememberSaveable(f) { mutableStateOf(false) }
|
||||
val audioPlaying = rememberSaveable(f) { mutableStateOf(false) }
|
||||
val progress = rememberSaveable(f) { mutableStateOf(0) }
|
||||
val duration = rememberSaveable(f) { mutableStateOf(providedDurationSec * 1000) }
|
||||
val play = {
|
||||
AudioPlayer.play(filePath, audioPlaying, progress, duration, true)
|
||||
brokenAudio = !audioPlaying.value
|
||||
@@ -94,7 +95,7 @@ private fun VoiceLayout(
|
||||
play: () -> Unit,
|
||||
pause: () -> Unit,
|
||||
longClick: () -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
onProgressChanged: (Int) -> Unit,
|
||||
) {
|
||||
@Composable
|
||||
@@ -248,7 +249,7 @@ private fun VoiceMsgIndicator(
|
||||
play: () -> Unit,
|
||||
pause: () -> Unit,
|
||||
longClick: () -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
) {
|
||||
val strokeWidth = with(LocalDensity.current) { 3.dp.toPx() }
|
||||
val strokeColor = MaterialTheme.colors.primary
|
||||
@@ -268,7 +269,8 @@ private fun VoiceMsgIndicator(
|
||||
}
|
||||
} else {
|
||||
if (file?.fileStatus is CIFileStatus.RcvInvitation) {
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, true, error, { receiveFile(file.fileId) }, {}, longClick = longClick)
|
||||
// TODO encrypt voice
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, true, error, { receiveFile(file.fileId, false) }, {}, longClick = longClick)
|
||||
} else if (file?.fileStatus is CIFileStatus.RcvTransfer
|
||||
|| file?.fileStatus is CIFileStatus.RcvAccepted
|
||||
) {
|
||||
|
||||
+3
-3
@@ -48,7 +48,7 @@ fun ChatItemView(
|
||||
useLinkPreviews: Boolean,
|
||||
linkMode: SimplexLinkMode,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
cancelFile: (Long) -> Unit,
|
||||
joinGroup: (Long) -> Unit,
|
||||
acceptCall: (Contact) -> Unit,
|
||||
@@ -566,7 +566,7 @@ fun PreviewChatItemView() {
|
||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
deleteMessage = { _, _ -> },
|
||||
receiveFile = {},
|
||||
receiveFile = { _, _ -> },
|
||||
cancelFile = {},
|
||||
joinGroup = {},
|
||||
acceptCall = { _ -> },
|
||||
@@ -595,7 +595,7 @@ fun PreviewChatItemViewDeletedContent() {
|
||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
|
||||
deleteMessage = { _, _ -> },
|
||||
receiveFile = {},
|
||||
receiveFile = { _, _ -> },
|
||||
cancelFile = {},
|
||||
joinGroup = {},
|
||||
acceptCall = { _ -> },
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ fun FramedItemView(
|
||||
imageProvider: (() -> ImageGalleryProvider)? = null,
|
||||
linkMode: SimplexLinkMode,
|
||||
showMenu: MutableState<Boolean>,
|
||||
receiveFile: (Long) -> Unit,
|
||||
receiveFile: (Long, Boolean) -> Unit,
|
||||
onLinkLongClick: (link: String) -> Unit = {},
|
||||
scrollToItem: (Long) -> Unit = {},
|
||||
) {
|
||||
|
||||
+10
-7
@@ -95,12 +95,13 @@ fun getThemeFromUri(uri: URI, withAlertOnException: Boolean = true): ThemeOverri
|
||||
return null
|
||||
}
|
||||
|
||||
fun saveImage(uri: URI): String? {
|
||||
fun saveImage(uri: URI): CryptoFile? {
|
||||
val bitmap = getBitmapFromUri(uri) ?: return null
|
||||
return saveImage(bitmap)
|
||||
}
|
||||
|
||||
fun saveImage(image: ImageBitmap): String? {
|
||||
fun saveImage(image: ImageBitmap): CryptoFile? {
|
||||
// TODO encrypt image
|
||||
return try {
|
||||
val ext = if (image.hasAlpha()) "png" else "jpg"
|
||||
val dataResized = resizeImageToDataSize(image, ext == "png", maxDataSize = MAX_IMAGE_SIZE)
|
||||
@@ -110,14 +111,15 @@ fun saveImage(image: ImageBitmap): String? {
|
||||
dataResized.writeTo(output)
|
||||
output.flush()
|
||||
output.close()
|
||||
fileToSave
|
||||
CryptoFile.plain(fileToSave)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Util.kt saveImage error: ${e.stackTraceToString()}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun saveAnimImage(uri: URI): String? {
|
||||
fun saveAnimImage(uri: URI): CryptoFile? {
|
||||
// TODO encrypt image
|
||||
return try {
|
||||
val filename = getFileName(uri)?.lowercase()
|
||||
var ext = when {
|
||||
@@ -135,7 +137,7 @@ fun saveAnimImage(uri: URI): String? {
|
||||
input?.copyTo(output)
|
||||
}
|
||||
}
|
||||
fileToSave
|
||||
CryptoFile.plain(fileToSave)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Util.kt saveAnimImage error: ${e.message}")
|
||||
null
|
||||
@@ -144,15 +146,16 @@ fun saveAnimImage(uri: URI): String? {
|
||||
|
||||
expect suspend fun saveTempImageUncompressed(image: ImageBitmap, asPng: Boolean): File?
|
||||
|
||||
fun saveFileFromUri(uri: URI): String? {
|
||||
fun saveFileFromUri(uri: URI, encrypted: Boolean): CryptoFile? {
|
||||
return try {
|
||||
val inputStream = uri.inputStream()
|
||||
val fileToSave = getFileName(uri)
|
||||
// TODO encrypt file if "encrypted" is true
|
||||
if (inputStream != null && fileToSave != null) {
|
||||
val destFileName = uniqueCombine(fileToSave)
|
||||
val destFile = File(getAppFilePath(destFileName))
|
||||
Files.copy(inputStream, destFile.toPath())
|
||||
destFileName
|
||||
CryptoFile.plain(destFileName)
|
||||
} else {
|
||||
Log.e(TAG, "Util.kt saveFileFromUri null inputStream")
|
||||
null
|
||||
|
||||
Reference in New Issue
Block a user