mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 09:44:15 +00:00
Merge branch 'stable'
This commit is contained in:
+1
@@ -47,6 +47,7 @@ class CallManager(val chatModel: ChatModel) {
|
||||
remoteHostId = invitation.remoteHostId,
|
||||
userProfile = userProfile,
|
||||
contact = invitation.contact,
|
||||
callUUID = invitation.callUUID,
|
||||
callState = CallState.InvitationAccepted,
|
||||
localMedia = invitation.callType.media,
|
||||
sharedKey = invitation.sharedKey,
|
||||
|
||||
+1
@@ -115,6 +115,7 @@ fun PreviewIncomingCallAlertLayout() {
|
||||
contact = Contact.sampleData,
|
||||
callType = CallType(media = CallMediaType.Audio, capabilities = CallCapabilities(encryption = false)),
|
||||
sharedKey = null,
|
||||
callUUID = "",
|
||||
callTs = Clock.System.now()
|
||||
),
|
||||
chatModel = ChatModel,
|
||||
|
||||
+2
@@ -13,6 +13,7 @@ data class Call(
|
||||
val remoteHostId: Long?,
|
||||
val userProfile: Profile,
|
||||
val contact: Contact,
|
||||
val callUUID: String?,
|
||||
val callState: CallState,
|
||||
val localMedia: CallMediaType,
|
||||
val localCapabilities: CallCapabilities? = null,
|
||||
@@ -105,6 +106,7 @@ sealed class WCallResponse {
|
||||
val contact: Contact,
|
||||
val callType: CallType,
|
||||
val sharedKey: String? = null,
|
||||
val callUUID: String,
|
||||
val callTs: Instant
|
||||
) {
|
||||
val callTypeText: String get() = generalGetString(when(callType.media) {
|
||||
|
||||
+1
-1
@@ -544,7 +544,7 @@ fun startChatCall(remoteHostId: Long?, chatInfo: ChatInfo, media: CallMediaType)
|
||||
if (chatInfo is ChatInfo.Direct) {
|
||||
val contactInfo = chatModel.controller.apiContactInfo(remoteHostId, chatInfo.contact.contactId)
|
||||
val profile = contactInfo?.second ?: chatModel.currentUser.value?.profile?.toProfile() ?: return@withBGApi
|
||||
chatModel.activeCall.value = Call(remoteHostId = remoteHostId, contact = chatInfo.contact, callState = CallState.WaitCapabilities, localMedia = media, userProfile = profile)
|
||||
chatModel.activeCall.value = Call(remoteHostId = remoteHostId, contact = chatInfo.contact, callUUID = null, callState = CallState.WaitCapabilities, localMedia = media, userProfile = profile)
|
||||
chatModel.showCallView.value = true
|
||||
chatModel.callCommand.add(WCallCommand.Capabilities(media))
|
||||
}
|
||||
|
||||
+85
-13
@@ -19,12 +19,76 @@ import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import kotlin.math.min
|
||||
|
||||
data class SemVer(
|
||||
val major: Int,
|
||||
val minor: Int,
|
||||
val patch: Int,
|
||||
val preRelease: String? = null,
|
||||
val buildNumber: Int? = null,
|
||||
): Comparable<SemVer?> {
|
||||
|
||||
val isNotStable: Boolean = preRelease != null
|
||||
|
||||
override fun compareTo(other: SemVer?): Int {
|
||||
if (other == null) return 1
|
||||
return when {
|
||||
major != other.major -> major.compareTo(other.major)
|
||||
minor != other.minor -> minor.compareTo(other.minor)
|
||||
patch != other.patch -> patch.compareTo(other.patch)
|
||||
preRelease != null && other.preRelease != null -> {
|
||||
val pr = preRelease.compareTo(other.preRelease, ignoreCase = true)
|
||||
when {
|
||||
pr != 0 -> pr
|
||||
buildNumber != null && other.buildNumber != null -> buildNumber.compareTo(other.buildNumber)
|
||||
buildNumber != null -> -1
|
||||
other.buildNumber != null -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
preRelease != null -> -1
|
||||
other.preRelease != null -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val regex = Regex("^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([A-Za-z]+)\\.(\\d+))?\$")
|
||||
fun from(tagName: String): SemVer? {
|
||||
val trimmed = tagName.trimStart { it == 'v' }
|
||||
val redacted = when {
|
||||
trimmed.contains('-') && trimmed.substringBefore('-').count { it == '.' } == 1 -> "${trimmed.substringBefore('-')}.0-${trimmed.substringAfter('-')}"
|
||||
trimmed.substringBefore('-').count { it == '.' } == 1 -> "${trimmed}.0"
|
||||
else -> trimmed
|
||||
}
|
||||
val group = regex.matchEntire(redacted)?.groups
|
||||
return if (group != null) {
|
||||
SemVer(
|
||||
major = group[1]?.value?.toIntOrNull() ?: return null,
|
||||
minor = group[2]?.value?.toIntOrNull() ?: return null,
|
||||
patch = group[3]?.value?.toIntOrNull() ?: return null,
|
||||
preRelease = group[4]?.value,
|
||||
buildNumber = group[5]?.value?.toIntOrNull(),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun fromCurrentVersionName(): SemVer? {
|
||||
val currentVersionName = if (appPlatform.isAndroid) BuildConfigCommon.ANDROID_VERSION_NAME else BuildConfigCommon.DESKTOP_VERSION_NAME
|
||||
return from(currentVersionName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class GitHubRelease(
|
||||
@@ -34,12 +98,18 @@ data class GitHubRelease(
|
||||
val htmlUrl: String,
|
||||
val name: String,
|
||||
val draft: Boolean,
|
||||
val prerelease: Boolean,
|
||||
@SerialName("prerelease")
|
||||
private val preRelease: Boolean,
|
||||
val body: String,
|
||||
@SerialName("published_at")
|
||||
val publishedAt: String,
|
||||
val assets: List<GitHubAsset>
|
||||
)
|
||||
) {
|
||||
@Transient
|
||||
val semVer: SemVer? = SemVer.from(tagName)
|
||||
|
||||
val isConsideredBeta: Boolean = preRelease || semVer == null || semVer.isNotStable
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class GitHubAsset(
|
||||
@@ -105,25 +175,25 @@ private fun createUpdateJob() {
|
||||
|
||||
fun checkForUpdate() {
|
||||
Log.d(TAG, "Checking for update")
|
||||
val currentSemVer = SemVer.fromCurrentVersionName()
|
||||
if (currentSemVer == null) {
|
||||
Log.e(TAG, "Current SemVer cannot be parsed")
|
||||
return
|
||||
}
|
||||
val client = setupHttpClient()
|
||||
try {
|
||||
val request = Request.Builder().url("https://api.github.com/repos/simplex-chat/simplex-chat/releases").addHeader("User-agent", "curl").build()
|
||||
client.newCall(request).execute().use { response ->
|
||||
response.body?.use {
|
||||
val body = it.string()
|
||||
val releases = json.decodeFromString<List<GitHubRelease>>(body).filterNot { it.draft }
|
||||
val releases = json.decodeFromString<List<GitHubRelease>>(body)
|
||||
val release = when (appPrefs.appUpdateChannel.get()) {
|
||||
AppUpdatesChannel.STABLE -> releases.firstOrNull { !it.prerelease }
|
||||
AppUpdatesChannel.BETA -> releases.firstOrNull()
|
||||
AppUpdatesChannel.STABLE -> releases.firstOrNull { r -> !r.draft && !r.isConsideredBeta && currentSemVer < r.semVer }
|
||||
AppUpdatesChannel.BETA -> releases.firstOrNull { r -> !r.draft && currentSemVer < r.semVer }
|
||||
AppUpdatesChannel.DISABLED -> return
|
||||
} ?: return
|
||||
val currentVersionName = "v" + (if (appPlatform.isAndroid) BuildConfigCommon.ANDROID_VERSION_NAME else BuildConfigCommon.DESKTOP_VERSION_NAME)
|
||||
val redactedCurrentVersionName = when {
|
||||
currentVersionName.contains('-') && currentVersionName.substringBefore('-').count { it == '.' } == 1 -> "${currentVersionName.substringBefore('-')}.0-${currentVersionName.substringAfter('-')}"
|
||||
currentVersionName.substringBefore('-').count { it == '.' } == 1 -> "${currentVersionName}.0"
|
||||
else -> currentVersionName
|
||||
}
|
||||
if (release.tagName == appPrefs.appSkippedUpdate.get() || release.tagName == currentVersionName || release.tagName == redactedCurrentVersionName) {
|
||||
|
||||
if (release == null || release.tagName == appPrefs.appSkippedUpdate.get()) {
|
||||
Log.d(TAG, "Skipping update because of the same version or skipped version")
|
||||
return
|
||||
}
|
||||
@@ -298,13 +368,15 @@ private suspend fun downloadAsset(asset: GitHubAsset) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isRunningFromAppImage(): Boolean = System.getenv("APPIMAGE") != null
|
||||
|
||||
private fun isRunningFromFlatpak(): Boolean = System.getenv("container") == "flatpak"
|
||||
|
||||
private fun chooseGitHubReleaseAssets(release: GitHubRelease): List<GitHubAsset> {
|
||||
val res = if (isRunningFromFlatpak()) {
|
||||
// No need to show download options for Flatpak users
|
||||
emptyList()
|
||||
} else if (Runtime.getRuntime().exec("which dpkg").onExit().join().exitValue() == 0) {
|
||||
} else if (!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 {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package chat.simplex.app
|
||||
|
||||
import chat.simplex.common.views.helpers.SemVer
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
// use this command for testing:
|
||||
// ./gradlew desktopTest
|
||||
class SemVerTest {
|
||||
@Test
|
||||
fun testValidSemVer() {
|
||||
assertEquals(SemVer.from("1.0.0"), SemVer(1, 0, 0))
|
||||
assertEquals(SemVer.from("1.0"), SemVer(1, 0, 0))
|
||||
assertEquals(SemVer.from("v1.0"), SemVer(1, 0, 0))
|
||||
assertEquals(SemVer.from("v1.0-beta.1"), SemVer(1, 0, 0, "beta", 1))
|
||||
val r = listOf<Pair<String, SemVer>>(
|
||||
"0.0.4" to SemVer(0, 0, 4),
|
||||
"1.2.3" to SemVer(1, 2, 3),
|
||||
"10.20.30" to SemVer(10, 20, 30),
|
||||
"1.0.0-alpha.1" to SemVer(1, 0, 0, "alpha", buildNumber = 1),
|
||||
"1.0.0" to SemVer(1, 0, 0),
|
||||
"2.0.0" to SemVer(2, 0, 0),
|
||||
"1.1.7" to SemVer(1, 1, 7),
|
||||
"2.0.1-alpha.1227" to SemVer(2, 0, 1, "alpha", 1227),
|
||||
)
|
||||
r.forEach { (value, correct) ->
|
||||
assertEquals(SemVer.from(value), correct)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testComparisonSemVer() {
|
||||
assert(SemVer(0, 1, 0) == SemVer.from("0.1.0"))
|
||||
assert(SemVer(1, 1, 0) == SemVer.from("v1.1.0"))
|
||||
assert(SemVer(0, 1, 0) > SemVer(0, 0, 1))
|
||||
assert(SemVer(1, 0, 0) > SemVer(0, 100, 100))
|
||||
assert(SemVer(0, 200, 0) > SemVer(0, 100, 100))
|
||||
assert(SemVer(0, 1, 0, "beta") > SemVer(0, 1, 0, "alpha"))
|
||||
assert(SemVer(0, 1, 0) > SemVer(0, 1, 0, "alpha"))
|
||||
assert(SemVer(0, 1, 0) > SemVer(0, 1, 0, "beta"))
|
||||
assert(SemVer(0, 1, 0) > SemVer(0, 1, 0, "beta.0"))
|
||||
assert(SemVer(0, 1, 0, "beta", 1) > SemVer(0, 1, 0, "beta", 0))
|
||||
assert(SemVer(0, 1, 0, "beta", 11) > SemVer(0, 1, 0, "beta", 10))
|
||||
assert(SemVer(0, 1, 0, "beta", 11) > SemVer(0, 1, 0, "beta", 9))
|
||||
assert(SemVer(0, 1, 0, "beta.1") > SemVer(0, 1, 0, "alpha.2"))
|
||||
assert(SemVer(1, 1, 0, "beta.1") > SemVer(0, 1, 0, "beta.1"))
|
||||
assert(SemVer(1, 0, 0) > SemVer(1, 0, 0, "beta.1"))
|
||||
assert(SemVer(1, 0, 0) > null)
|
||||
assert(SemVer.from("v6.0.0")!! > SemVer.from("v6.0.0-beta.3"))
|
||||
assert(SemVer.from("v6.0.0-beta.3")!! > SemVer.from("v6.0.0-beta.2"))
|
||||
assert(SemVer.from("0.1.0") == SemVer.from("0.1.0"))
|
||||
assert(SemVer.from("0.1.1")!! > SemVer.from("0.1.0"))
|
||||
assert(SemVer.from("0.2.1")!! > SemVer.from("0.1.1"))
|
||||
assert(SemVer.from("2.0.1")!! > SemVer.from("0.1.1"))
|
||||
assert(SemVer.from("0.1.1-beta.0")!! > SemVer.from("0.1.0-beta.0"))
|
||||
assert(SemVer.from("0.1.1-beta.0")!! == SemVer.from("0.1.1-beta.0"))
|
||||
assert(SemVer.from("0.1.1-beta.1")!! > SemVer.from("0.1.1-beta.0"))
|
||||
assert(SemVer.from("10.0.0-beta.12")!! > SemVer.from("1.1.1"))
|
||||
assert(SemVer.from("1.1.1-beta.120")!! > SemVer.from("1.1.1-alpha.9"))
|
||||
assert(SemVer.from("1.1.1-beta.120")!! > SemVer.from("1.1.1-alpha.120"))
|
||||
assert(SemVer.from("2.0.1")!! > SemVer.from("0.1.1"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user