Merge branch 'stable'

This commit is contained in:
Evgeny Poberezkin committed 2026-05-28 23:19:54 +01:00
commit 507a4de61c
35 files changed
+1023 -222

No files matched your search

@@ -10,7 +10,7 @@ val desktopPlatform = detectDesktopPlatform()
enum class DesktopPlatform(val libExtension: String, val configPath: String, val dataPath: String, val githubAssetName: String) {
LINUX_X86_64("so", unixConfigPath, unixDataPath, "simplex-desktop-x86_64.AppImage"),
LINUX_AARCH64("so", unixConfigPath, unixDataPath, " simplex-desktop-aarch64.AppImage"),
LINUX_AARCH64("so", unixConfigPath, unixDataPath, "simplex-desktop-aarch64.AppImage"),
WINDOWS_X86_64("dll", System.getenv("AppData") + File.separator + "SimpleX", System.getenv("AppData") + File.separator + "SimpleX", "simplex-desktop-windows-x86_64.msi"),
MAC_X86_64("dylib", unixConfigPath, unixDataPath, "simplex-desktop-macos-x86_64.dmg"),
MAC_AARCH64("dylib", unixConfigPath, unixDataPath, "simplex-desktop-macos-aarch64.dmg");
@@ -6,6 +6,7 @@ import androidx.compose.ui.graphics.*
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import kotlinx.coroutines.*
import org.jetbrains.compose.videoplayer.SkiaBitmapVideoSurface
import uk.co.caprica.vlcj.media.VideoOrientation
import uk.co.caprica.vlcj.player.base.*
import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent
@@ -214,7 +215,7 @@ 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(previewThread.asCoroutineDispatcher()) {
val mediaComponent = getOrCreateHelperPlayer()
val player = mediaComponent.mediaPlayer()
if (uri == null || !uri.toFile().exists()) {
@@ -222,12 +223,12 @@ actual class VideoPlayer actual constructor(
return@withContext VideoPlayerInterface.PreviewAndDuration(preview = defaultPreview, timestamp = 0L, duration = 0L)
}
val surface = SkiaBitmapVideoSurface()
player.videoSurface().set(surface)
player.media().startPaused(uri.toFile().absolutePath)
val start = System.currentTimeMillis()
var snap: BufferedImage? = null
while (snap == null && start + 1500 > System.currentTimeMillis()) {
snap = player.snapshots()?.get()
delay(50)
val snap = withTimeoutOrNull(1500L) {
while (surface.bitmap.value == null) delay(50)
surface.bitmap.value!!.toAwtImage()
}
val orientation = player.media().info().videoTracks().firstOrNull()?.orientation()
if (orientation == null) {
@@ -255,6 +256,7 @@ actual class VideoPlayer actual constructor(
}
val playerThread = Executors.newSingleThreadExecutor()
private val previewThread = Executors.newSingleThreadExecutor()
private val playersPool: ArrayList<Component> = ArrayList()
private val helperPlayersPool: ArrayList<CallbackMediaPlayerComponent> = ArrayList()
@@ -26,6 +26,8 @@ import java.io.Closeable
import java.io.File
import java.net.InetSocketAddress
import java.net.Proxy
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import kotlin.math.min
data class SemVer(
@@ -376,7 +378,7 @@ private fun chooseGitHubReleaseAssets(release: GitHubRelease): List<GitHubAsset>
val res = if (isRunningFromFlatpak()) {
// No need to show download options for Flatpak users
emptyList()
} else if (!isRunningFromAppImage() && Runtime.getRuntime().exec("which dpkg").onExit().join().exitValue() == 0) {
} else if (desktopPlatform.isLinux() && !isRunningFromAppImage() && Runtime.getRuntime().exec("which dpkg").onExit().join().exitValue() == 0) {
// Show all available .deb packages and user will choose the one that works on his system (for Debian derivatives)
release.assets.filter { it.name.lowercase().endsWith(".deb") }
} else {
@@ -388,18 +390,42 @@ private fun chooseGitHubReleaseAssets(release: GitHubRelease): List<GitHubAsset>
private suspend fun installAppUpdate(file: File) = withContext(Dispatchers.IO) {
when {
desktopPlatform.isLinux() -> {
val process = Runtime.getRuntime().exec("xdg-open ${file.absolutePath}").onExit().join()
val startedInstallation = process.exitValue() == 0 && process.children().count() > 0
if (!startedInstallation) {
Log.e(TAG, "Error starting installation: ${process.inputReader().use { it.readLines().joinToString("\n") }}${process.errorStream.use { String(it.readAllBytes()) }}")
// Failed to start installation. show directory with the file for manual installation
desktopOpenDir(file.parentFile)
val appImagePath = System.getenv("APPIMAGE")
if (appImagePath != null) {
// Replace the running AppImage crash-safely: copy onto the target's own
// filesystem first (an atomic rename only works within one filesystem, and
// the download lives in the temp dir which is usually a different one),
// then atomically move the staged file onto $APPIMAGE.
val target = File(appImagePath)
val staging = File(target.parentFile, ".${target.name}.update")
try {
Files.copy(file.toPath(), staging.toPath(), StandardCopyOption.REPLACE_EXISTING)
staging.setExecutable(true, false)
Files.move(staging.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING)
file.delete()
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_check_for_updates_installed_successfully_title),
text = generalGetString(MR.strings.app_check_for_updates_installed_successfully_desc)
)
} catch (e: Exception) {
Log.e(TAG, "Failed to replace AppImage: ${e.stackTraceToString()}")
staging.delete()
desktopOpenDir(file.parentFile)
}
} else {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_check_for_updates_installed_successfully_title),
text = generalGetString(MR.strings.app_check_for_updates_installed_successfully_desc)
)
file.delete()
val process = Runtime.getRuntime().exec("xdg-open ${file.absolutePath}").onExit().join()
val startedInstallation = process.exitValue() == 0 && process.children().count() > 0
if (!startedInstallation) {
Log.e(TAG, "Error starting installation: ${process.inputReader().use { it.readLines().joinToString("\n") }}${process.errorStream.use { String(it.readAllBytes()) }}")
// Failed to start installation. show directory with the file for manual installation
desktopOpenDir(file.parentFile)
} else {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_check_for_updates_installed_successfully_title),
text = generalGetString(MR.strings.app_check_for_updates_installed_successfully_desc)
)
file.delete()
}
}
}
desktopPlatform.isWindows() -> {