android, desktop: better handling of URI's (#3450)
This commit is contained in:
parent
8ce9dd7ab6
commit
f9b5c673c5
@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
actual fun Modifier.navigationBarsWithImePadding(): Modifier = navigationBarsWithImePadding()
|
actual fun Modifier.navigationBarsWithImePadding(): Modifier = navigationBarsWithImePadding()
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ actual fun ProvideWindowInsets(
|
|||||||
@Composable
|
@Composable
|
||||||
actual fun Modifier.desktopOnExternalDrag(
|
actual fun Modifier.desktopOnExternalDrag(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
onFiles: (List<String>) -> Unit,
|
onFiles: (List<File>) -> Unit,
|
||||||
onImage: (Painter) -> Unit,
|
onImage: (Painter) -> Unit,
|
||||||
onText: (String) -> Unit
|
onText: (String) -> Unit
|
||||||
): Modifier = this
|
): Modifier = this
|
||||||
|
@ -71,7 +71,7 @@ actual class VideoPlayer actual constructor(
|
|||||||
private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean {
|
private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean {
|
||||||
val filepath = getAppFilePath(uri)
|
val filepath = getAppFilePath(uri)
|
||||||
if (filepath == null || !File(filepath).exists()) {
|
if (filepath == null || !File(filepath).exists()) {
|
||||||
Log.e(TAG, "No such file: $uri")
|
Log.e(TAG, "No such file: $filepath")
|
||||||
brokenVideo.value = true
|
brokenVideo.value = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,6 @@ import kotlinx.serialization.encoding.Encoder
|
|||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URLDecoder
|
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.format.FormatStyle
|
import java.time.format.FormatStyle
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -2363,7 +2362,7 @@ data class CryptoFile(
|
|||||||
companion object {
|
companion object {
|
||||||
fun plain(f: String): CryptoFile = CryptoFile(f, null)
|
fun plain(f: String): CryptoFile = CryptoFile(f, null)
|
||||||
|
|
||||||
fun desktopPlain(f: URI): CryptoFile = CryptoFile(URLDecoder.decode(f.rawPath, "UTF-8"), null)
|
fun desktopPlain(f: URI): CryptoFile = CryptoFile(f.toFile().absolutePath, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ import chat.simplex.common.views.helpers.generalGetString
|
|||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import java.net.URLDecoder
|
||||||
|
import java.net.URLEncoder
|
||||||
|
|
||||||
expect val dataDir: File
|
expect val dataDir: File
|
||||||
expect val tmpDir: File
|
expect val tmpDir: File
|
||||||
@ -28,6 +30,10 @@ expect val remoteHostsDir: File
|
|||||||
|
|
||||||
expect fun desktopOpenDatabaseDir()
|
expect fun desktopOpenDatabaseDir()
|
||||||
|
|
||||||
|
fun createURIFromPath(absolutePath: String): URI = URI.create(URLEncoder.encode(absolutePath, "UTF-8"))
|
||||||
|
|
||||||
|
fun URI.toFile(): File = File(URLDecoder.decode(rawPath, "UTF-8").removePrefix("file:"))
|
||||||
|
|
||||||
fun copyFileToFile(from: File, to: URI, finally: () -> Unit) {
|
fun copyFileToFile(from: File, to: URI, finally: () -> Unit) {
|
||||||
try {
|
try {
|
||||||
to.outputStream().use { stream ->
|
to.outputStream().use { stream ->
|
||||||
|
@ -3,6 +3,7 @@ package chat.simplex.common.platform
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
expect fun Modifier.navigationBarsWithImePadding(): Modifier
|
expect fun Modifier.navigationBarsWithImePadding(): Modifier
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ expect fun ProvideWindowInsets(
|
|||||||
@Composable
|
@Composable
|
||||||
expect fun Modifier.desktopOnExternalDrag(
|
expect fun Modifier.desktopOnExternalDrag(
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onFiles: (List<String>) -> Unit = {},
|
onFiles: (List<File>) -> Unit = {},
|
||||||
onImage: (Painter) -> Unit = {},
|
onImage: (Painter) -> Unit = {},
|
||||||
onText: (String) -> Unit = {}
|
onText: (String) -> Unit = {}
|
||||||
): Modifier
|
): Modifier
|
||||||
|
@ -43,16 +43,13 @@ object VideoPlayerHolder {
|
|||||||
): VideoPlayer =
|
): VideoPlayer =
|
||||||
players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled) }
|
players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled) }
|
||||||
|
|
||||||
fun enableSound(enable: Boolean, fileName: String?, gallery: Boolean): Boolean =
|
private fun player(uri: URI?, gallery: Boolean): VideoPlayer? {
|
||||||
player(fileName, gallery)?.enableSound(enable) == true
|
uri ?: return null
|
||||||
|
return players.values.firstOrNull { player -> player.uri == uri && player.gallery == gallery }
|
||||||
private fun player(fileName: String?, gallery: Boolean): VideoPlayer? {
|
|
||||||
fileName ?: return null
|
|
||||||
return players.values.firstOrNull { player -> player.uri.path?.endsWith(fileName) == true && player.gallery == gallery }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun release(uri: URI, gallery: Boolean, remove: Boolean) =
|
fun release(uri: URI, gallery: Boolean, remove: Boolean) =
|
||||||
player(uri.path, gallery)?.release(remove).run { }
|
player(uri, gallery)?.release(remove).run { }
|
||||||
|
|
||||||
fun stopAll() {
|
fun stopAll() {
|
||||||
players.values.forEach { it.stop() }
|
players.values.forEach { it.stop() }
|
||||||
|
@ -501,7 +501,7 @@ fun ChatLayout(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.desktopOnExternalDrag(
|
.desktopOnExternalDrag(
|
||||||
enabled = !attachmentDisabled.value && rememberUpdatedState(chat.userCanSend).value,
|
enabled = !attachmentDisabled.value && rememberUpdatedState(chat.userCanSend).value,
|
||||||
onFiles = { paths -> composeState.onFilesAttached(paths.map { URI.create(it) }) },
|
onFiles = { paths -> composeState.onFilesAttached(paths.map { it.toURI() }) },
|
||||||
onImage = {
|
onImage = {
|
||||||
// TODO: file is not saved anywhere?!
|
// TODO: file is not saved anywhere?!
|
||||||
val tmpFile = File.createTempFile("image", ".bmp", tmpDir)
|
val tmpFile = File.createTempFile("image", ".bmp", tmpDir)
|
||||||
|
@ -104,5 +104,5 @@ private fun fileFilterDescription(input: String): String = when(input) {
|
|||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun URI.inputStream(): InputStream? = File(URI("file:" + toString().removePrefix("file:"))).inputStream()
|
actual fun URI.inputStream(): InputStream? = toFile().inputStream()
|
||||||
actual fun URI.outputStream(): OutputStream = File(URI("file:" + toString().removePrefix("file:"))).outputStream()
|
actual fun URI.outputStream(): OutputStream = toFile().outputStream()
|
||||||
|
@ -157,7 +157,7 @@ actual fun ImageBitmap.scale(width: Int, height: Int): ImageBitmap {
|
|||||||
|
|
||||||
// LALAL
|
// LALAL
|
||||||
actual fun isImage(uri: URI): Boolean {
|
actual fun isImage(uri: URI): Boolean {
|
||||||
val path = uri.path.lowercase()
|
val path = uri.toFile().path.lowercase()
|
||||||
return path.endsWith(".gif") ||
|
return path.endsWith(".gif") ||
|
||||||
path.endsWith(".webp") ||
|
path.endsWith(".webp") ||
|
||||||
path.endsWith(".png") ||
|
path.endsWith(".png") ||
|
||||||
@ -166,7 +166,7 @@ actual fun isImage(uri: URI): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actual fun isAnimImage(uri: URI, drawable: Any?): Boolean {
|
actual fun isAnimImage(uri: URI, drawable: Any?): Boolean {
|
||||||
val path = uri.path.lowercase()
|
val path = uri.toFile().path.lowercase()
|
||||||
return path.endsWith(".gif") || path.endsWith(".webp")
|
return path.endsWith(".gif") || path.endsWith(".webp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ import androidx.compose.foundation.contextMenuOpenDetector
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.*
|
import androidx.compose.ui.*
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
actual fun Modifier.navigationBarsWithImePadding(): Modifier = this
|
actual fun Modifier.navigationBarsWithImePadding(): Modifier = this
|
||||||
|
|
||||||
@ -19,13 +21,15 @@ actual fun ProvideWindowInsets(
|
|||||||
@Composable
|
@Composable
|
||||||
actual fun Modifier.desktopOnExternalDrag(
|
actual fun Modifier.desktopOnExternalDrag(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
onFiles: (List<String>) -> Unit,
|
onFiles: (List<File>) -> Unit,
|
||||||
onImage: (Painter) -> Unit,
|
onImage: (Painter) -> Unit,
|
||||||
onText: (String) -> Unit
|
onText: (String) -> Unit
|
||||||
): Modifier =
|
): Modifier =
|
||||||
onExternalDrag(enabled) {
|
onExternalDrag(enabled) {
|
||||||
when(val data = it.dragData) {
|
when(val data = it.dragData) {
|
||||||
is DragData.FilesList -> onFiles(data.readFiles())
|
// data.readFiles() returns filePath in URI format (where spaces replaces with %20). But it's an error-prone idea to work later
|
||||||
|
// with such format when everywhere we use absolutePath in File() format
|
||||||
|
is DragData.FilesList -> onFiles(data.readFiles().map { URI.create(it).toFile() })
|
||||||
is DragData.Image -> onImage(data.readImage())
|
is DragData.Image -> onImage(data.readImage())
|
||||||
is DragData.Text -> onText(data.readText())
|
is DragData.Text -> onText(data.readText())
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
|||||||
|
|
||||||
// Returns real duration of the track
|
// Returns real duration of the track
|
||||||
private fun start(fileSource: CryptoFile, seek: Int? = null, onProgressUpdate: (position: Int?, state: TrackState) -> Unit): Int? {
|
private fun start(fileSource: CryptoFile, seek: Int? = null, onProgressUpdate: (position: Int?, state: TrackState) -> Unit): Int? {
|
||||||
val absoluteFilePath = if (fileSource.isAbsolutePath) fileSource.filePath else getAppFilePath(fileSource.filePath)
|
val absoluteFilePath = if (fileSource.isAbsolutePath) fileSource.filePath else getAppFilePath(fileSource.filePath)
|
||||||
if (!File(absoluteFilePath).exists()) {
|
if (!File(absoluteFilePath).exists()) {
|
||||||
Log.e(TAG, "No such file: ${fileSource.filePath}")
|
Log.e(TAG, "No such file: ${fileSource.filePath}")
|
||||||
return null
|
return null
|
||||||
@ -46,16 +46,16 @@ actual object AudioPlayer: AudioPlayerInterface {
|
|||||||
VideoPlayerHolder.stopAll()
|
VideoPlayerHolder.stopAll()
|
||||||
RecorderInterface.stopRecording?.invoke()
|
RecorderInterface.stopRecording?.invoke()
|
||||||
val current = currentlyPlaying.value
|
val current = currentlyPlaying.value
|
||||||
if (current == null || current.first != fileSource) {
|
if (current == null || current.first != fileSource || !player.status().isPlayable) {
|
||||||
stopListener()
|
stopListener()
|
||||||
player.stop()
|
player.stop()
|
||||||
runCatching {
|
runCatching {
|
||||||
if (fileSource.cryptoArgs != null) {
|
if (fileSource.cryptoArgs != null) {
|
||||||
val tmpFile = fileSource.createTmpFileIfNeeded()
|
val tmpFile = fileSource.createTmpFileIfNeeded()
|
||||||
decryptCryptoFile(absoluteFilePath, fileSource.cryptoArgs, tmpFile.absolutePath)
|
decryptCryptoFile(absoluteFilePath, fileSource.cryptoArgs, tmpFile.absolutePath)
|
||||||
player.media().prepare(tmpFile.toURI().toString().replaceFirst("file:", "file://"))
|
player.media().prepare(tmpFile.absolutePath)
|
||||||
} else {
|
} else {
|
||||||
player.media().prepare(File(absoluteFilePath).toURI().toString().replaceFirst("file:", "file://"))
|
player.media().prepare(absoluteFilePath)
|
||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Log.e(TAG, it.stackTraceToString())
|
Log.e(TAG, it.stackTraceToString())
|
||||||
@ -171,7 +171,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
|||||||
var res: Int? = null
|
var res: Int? = null
|
||||||
try {
|
try {
|
||||||
val helperPlayer = AudioPlayerComponent().mediaPlayer()
|
val helperPlayer = AudioPlayerComponent().mediaPlayer()
|
||||||
helperPlayer.media().startPaused(File(unencryptedFilePath).toURI().toString().replaceFirst("file:", "file://"))
|
helperPlayer.media().startPaused(unencryptedFilePath)
|
||||||
res = helperPlayer.duration
|
res = helperPlayer.duration
|
||||||
helperPlayer.stop()
|
helperPlayer.stop()
|
||||||
helperPlayer.release()
|
helperPlayer.release()
|
||||||
|
@ -4,6 +4,7 @@ import androidx.compose.ui.platform.ClipboardManager
|
|||||||
import androidx.compose.ui.platform.UriHandler
|
import androidx.compose.ui.platform.UriHandler
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
|
import chat.simplex.common.views.helpers.generalGetString
|
||||||
import chat.simplex.common.views.helpers.withApi
|
import chat.simplex.common.views.helpers.withApi
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
@ -25,14 +26,16 @@ actual fun shareFile(text: String, fileSource: CryptoFile) {
|
|||||||
withApi {
|
withApi {
|
||||||
FileChooserLauncher(false) { to: URI? ->
|
FileChooserLauncher(false) { to: URI? ->
|
||||||
if (to != null) {
|
if (to != null) {
|
||||||
|
val absolutePath = if (fileSource.isAbsolutePath) fileSource.filePath else getAppFilePath(fileSource.filePath)
|
||||||
if (fileSource.cryptoArgs != null) {
|
if (fileSource.cryptoArgs != null) {
|
||||||
try {
|
try {
|
||||||
decryptCryptoFile(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs, to.path)
|
decryptCryptoFile(absolutePath, fileSource.cryptoArgs, to.toFile().absolutePath)
|
||||||
|
showToast(generalGetString(MR.strings.file_saved))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString())
|
Log.e(TAG, "Unable to decrypt crypto file: " + e.stackTraceToString())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
copyFileToFile(File(fileSource.filePath), to) {}
|
copyFileToFile(File(absolutePath), to) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.launch(fileSource.filePath)
|
}.launch(fileSource.filePath)
|
||||||
|
@ -52,7 +52,7 @@ actual class VideoPlayer actual constructor(
|
|||||||
private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean {
|
private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean {
|
||||||
val filepath = getAppFilePath(uri)
|
val filepath = getAppFilePath(uri)
|
||||||
if (filepath == null || !File(filepath).exists()) {
|
if (filepath == null || !File(filepath).exists()) {
|
||||||
Log.e(TAG, "No such file: $uri")
|
Log.e(TAG, "No such file: $filepath")
|
||||||
brokenVideo.value = true
|
brokenVideo.value = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -62,10 +62,9 @@ actual class VideoPlayer actual constructor(
|
|||||||
}
|
}
|
||||||
AudioPlayer.stop()
|
AudioPlayer.stop()
|
||||||
VideoPlayerHolder.stopAll()
|
VideoPlayerHolder.stopAll()
|
||||||
val playerFilePath = uri.toString().replaceFirst("file:", "file://")
|
|
||||||
if (listener.value == null) {
|
if (listener.value == null) {
|
||||||
runCatching {
|
runCatching {
|
||||||
player.media().prepare(playerFilePath)
|
player.media().prepare(uri.toFile().absolutePath)
|
||||||
if (seek != null) {
|
if (seek != null) {
|
||||||
player.seekTo(seek.toInt())
|
player.seekTo(seek.toInt())
|
||||||
}
|
}
|
||||||
@ -217,12 +216,12 @@ actual class VideoPlayer actual constructor(
|
|||||||
suspend fun getBitmapFromVideo(defaultPreview: ImageBitmap?, uri: URI?, withAlertOnException: Boolean = true): VideoPlayerInterface.PreviewAndDuration = withContext(playerThread.asCoroutineDispatcher()) {
|
suspend fun getBitmapFromVideo(defaultPreview: ImageBitmap?, uri: URI?, withAlertOnException: Boolean = true): VideoPlayerInterface.PreviewAndDuration = withContext(playerThread.asCoroutineDispatcher()) {
|
||||||
val mediaComponent = getOrCreateHelperPlayer()
|
val mediaComponent = getOrCreateHelperPlayer()
|
||||||
val player = mediaComponent.mediaPlayer()
|
val player = mediaComponent.mediaPlayer()
|
||||||
if (uri == null || !File(uri.rawPath).exists()) {
|
if (uri == null || !uri.toFile().exists()) {
|
||||||
if (withAlertOnException) showVideoDecodingException()
|
if (withAlertOnException) showVideoDecodingException()
|
||||||
|
|
||||||
return@withContext VideoPlayerInterface.PreviewAndDuration(preview = defaultPreview, timestamp = 0L, duration = 0L)
|
return@withContext VideoPlayerInterface.PreviewAndDuration(preview = defaultPreview, timestamp = 0L, duration = 0L)
|
||||||
}
|
}
|
||||||
player.media().startPaused(uri.toString().replaceFirst("file:", "file://"))
|
player.media().startPaused(uri.toFile().absolutePath)
|
||||||
val start = System.currentTimeMillis()
|
val start = System.currentTimeMillis()
|
||||||
var snap: BufferedImage? = null
|
var snap: BufferedImage? = null
|
||||||
while (snap == null && start + 5000 > System.currentTimeMillis()) {
|
while (snap == null && start + 5000 > System.currentTimeMillis()) {
|
||||||
|
@ -3,7 +3,7 @@ package chat.simplex.common.platform
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
fun isVideo(uri: URI): Boolean {
|
fun isVideo(uri: URI): Boolean {
|
||||||
val path = uri.path.lowercase()
|
val path = uri.toFile().path.lowercase()
|
||||||
return path.endsWith(".mov") ||
|
return path.endsWith(".mov") ||
|
||||||
path.endsWith(".avi") ||
|
path.endsWith(".avi") ||
|
||||||
path.endsWith(".mp4") ||
|
path.endsWith(".mp4") ||
|
||||||
|
@ -8,14 +8,12 @@ import androidx.compose.ui.unit.Density
|
|||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
import chat.simplex.common.platform.*
|
import chat.simplex.common.platform.*
|
||||||
import chat.simplex.common.simplexWindowState
|
import chat.simplex.common.simplexWindowState
|
||||||
import chat.simplex.res.MR
|
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import javax.imageio.ImageIO
|
import javax.imageio.ImageIO
|
||||||
import kotlin.io.encoding.Base64
|
import kotlin.io.encoding.Base64
|
||||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||||
import kotlin.io.path.toPath
|
|
||||||
|
|
||||||
private val bStyle = SpanStyle(fontWeight = FontWeight.Bold)
|
private val bStyle = SpanStyle(fontWeight = FontWeight.Bold)
|
||||||
private val iStyle = SpanStyle(fontStyle = FontStyle.Italic)
|
private val iStyle = SpanStyle(fontStyle = FontStyle.Italic)
|
||||||
@ -90,9 +88,9 @@ actual fun escapedHtmlToAnnotatedString(text: String, density: Density): Annotat
|
|||||||
actual fun getAppFileUri(fileName: String): URI {
|
actual fun getAppFileUri(fileName: String): URI {
|
||||||
val rh = chatModel.currentRemoteHost.value
|
val rh = chatModel.currentRemoteHost.value
|
||||||
return if (rh == null) {
|
return if (rh == null) {
|
||||||
URI(appFilesDir.toURI().toString() + "/" + fileName)
|
createURIFromPath(appFilesDir.absolutePath + "/" + fileName)
|
||||||
} else {
|
} else {
|
||||||
URI(dataDir.absolutePath + "/remote_hosts/" + rh.storePath + "/simplex_v1_files/" + fileName)
|
createURIFromPath(dataDir.absolutePath + "/remote_hosts/" + rh.storePath + "/simplex_v1_files/" + fileName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,11 +114,11 @@ actual suspend fun getLoadedImage(file: CIFile?): Pair<ImageBitmap, ByteArray>?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun getFileName(uri: URI): String? = uri.toPath().toFile().name
|
actual fun getFileName(uri: URI): String? = uri.toFile().name
|
||||||
|
|
||||||
actual fun getAppFilePath(uri: URI): String? = uri.path
|
actual fun getAppFilePath(uri: URI): String? = uri.toFile().absolutePath
|
||||||
|
|
||||||
actual fun getFileSize(uri: URI): Long? = uri.toPath().toFile().length()
|
actual fun getFileSize(uri: URI): Long? = uri.toFile().length()
|
||||||
|
|
||||||
actual fun getBitmapFromUri(uri: URI, withAlertOnException: Boolean): ImageBitmap? =
|
actual fun getBitmapFromUri(uri: URI, withAlertOnException: Boolean): ImageBitmap? =
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user