mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-17 00:01:53 +00:00
Merge branch 'master' into master-android
This commit is contained in:
@@ -51,9 +51,10 @@ struct AdvancedNetworkSettings: View {
|
||||
}
|
||||
.disabled(currentNetCfg == NetCfg.proxyDefaults)
|
||||
|
||||
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [2_500, 5_000, 10_000, 15_000, 20_000, 30_000], label: secondsLabel)
|
||||
intSettingPicker("Receiving concurrency", selection: $netCfg.rcvConcurrency, values: [1, 2, 4, 8, 12, 16, 24], label: "")
|
||||
timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel)
|
||||
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
|
||||
Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive)
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C22177A2BD40D1800A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217752BD40D1800A8B0E7 /* libgmp.a */; };
|
||||
5C22177B2BD40D1800A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217762BD40D1800A8B0E7 /* libgmpxx.a */; };
|
||||
5C22177C2BD40D1800A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217772BD40D1800A8B0E7 /* libffi.a */; };
|
||||
5C22177D2BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217782BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O-ghc9.6.3.a */; };
|
||||
5C22177E2BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217792BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -110,11 +115,6 @@
|
||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; };
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CC83D1A2BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CC83D152BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv.a */; };
|
||||
5CC83D1B2BCC504B00A0C558 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CC83D162BCC504B00A0C558 /* libgmpxx.a */; };
|
||||
5CC83D1C2BCC504B00A0C558 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CC83D172BCC504B00A0C558 /* libgmp.a */; };
|
||||
5CC83D1D2BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv-ghc9.6.4.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CC83D182BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv-ghc9.6.4.a */; };
|
||||
5CC83D1E2BCC504B00A0C558 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CC83D192BCC504B00A0C558 /* libffi.a */; };
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; };
|
||||
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
|
||||
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD67B8D2B0E858A00C510B1 /* hs_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -284,6 +284,11 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C2217752BD40D1800A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C2217762BD40D1800A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C2217772BD40D1800A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C2217782BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C2217792BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O.a"; sourceTree = "<group>"; };
|
||||
5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@@ -408,11 +413,6 @@
|
||||
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CC83D152BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv.a"; sourceTree = "<group>"; };
|
||||
5CC83D162BCC504B00A0C558 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CC83D172BCC504B00A0C558 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CC83D182BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv-ghc9.6.4.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv-ghc9.6.4.a"; sourceTree = "<group>"; };
|
||||
5CC83D192BCC504B00A0C558 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; };
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; };
|
||||
5CD67B8D2B0E858A00C510B1 /* hs_init.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hs_init.h; sourceTree = "<group>"; };
|
||||
@@ -535,13 +535,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CC83D1D2BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv-ghc9.6.4.a in Frameworks */,
|
||||
5CC83D1B2BCC504B00A0C558 /* libgmpxx.a in Frameworks */,
|
||||
5CC83D1C2BCC504B00A0C558 /* libgmp.a in Frameworks */,
|
||||
5CC83D1A2BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv.a in Frameworks */,
|
||||
5CC83D1E2BCC504B00A0C558 /* libffi.a in Frameworks */,
|
||||
5C22177B2BD40D1800A8B0E7 /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5C22177D2BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O-ghc9.6.3.a in Frameworks */,
|
||||
5C22177A2BD40D1800A8B0E7 /* libgmp.a in Frameworks */,
|
||||
5C22177C2BD40D1800A8B0E7 /* libffi.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5C22177E2BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -605,11 +605,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CC83D192BCC504B00A0C558 /* libffi.a */,
|
||||
5CC83D172BCC504B00A0C558 /* libgmp.a */,
|
||||
5CC83D162BCC504B00A0C558 /* libgmpxx.a */,
|
||||
5CC83D182BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv-ghc9.6.4.a */,
|
||||
5CC83D152BCC504B00A0C558 /* libHSsimplex-chat-5.7.0.0-AhbVfRKDsEZ5w5ND1HSTLv.a */,
|
||||
5C2217772BD40D1800A8B0E7 /* libffi.a */,
|
||||
5C2217752BD40D1800A8B0E7 /* libgmp.a */,
|
||||
5C2217762BD40D1800A8B0E7 /* libgmpxx.a */,
|
||||
5C2217782BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O-ghc9.6.3.a */,
|
||||
5C2217792BD40D1800A8B0E7 /* libHSsimplex-chat-5.7.0.0-KdKN1sKiHR7tY2gjTFt3O.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -1257,6 +1257,7 @@ public struct NetCfg: Codable, Equatable {
|
||||
public var tcpConnectTimeout: Int // microseconds
|
||||
public var tcpTimeout: Int // microseconds
|
||||
public var tcpTimeoutPerKb: Int // microseconds
|
||||
public var rcvConcurrency: Int // pool size
|
||||
public var tcpKeepAlive: KeepAliveOpts?
|
||||
public var smpPingInterval: Int // microseconds
|
||||
public var smpPingCount: Int // times
|
||||
@@ -1265,9 +1266,10 @@ public struct NetCfg: Codable, Equatable {
|
||||
public static let defaults: NetCfg = NetCfg(
|
||||
socksProxy: nil,
|
||||
sessionMode: TransportSessionMode.user,
|
||||
tcpConnectTimeout: 20_000_000,
|
||||
tcpConnectTimeout: 10_000_000,
|
||||
tcpTimeout: 15_000_000,
|
||||
tcpTimeoutPerKb: 10_000,
|
||||
rcvConcurrency: 12,
|
||||
tcpKeepAlive: KeepAliveOpts.defaults,
|
||||
smpPingInterval: 1200_000_000,
|
||||
smpPingCount: 3,
|
||||
@@ -1277,9 +1279,10 @@ public struct NetCfg: Codable, Equatable {
|
||||
public static let proxyDefaults: NetCfg = NetCfg(
|
||||
socksProxy: nil,
|
||||
sessionMode: TransportSessionMode.user,
|
||||
tcpConnectTimeout: 30_000_000,
|
||||
tcpConnectTimeout: 20_000_000,
|
||||
tcpTimeout: 20_000_000,
|
||||
tcpTimeoutPerKb: 15_000,
|
||||
rcvConcurrency: 8,
|
||||
tcpKeepAlive: KeepAliveOpts.defaults,
|
||||
smpPingInterval: 1200_000_000,
|
||||
smpPingCount: 3,
|
||||
|
||||
@@ -29,6 +29,7 @@ let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
|
||||
let GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT = "networkTCPConnectTimeout"
|
||||
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT = "networkTCPTimeout"
|
||||
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
|
||||
let GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
|
||||
let GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL = "networkSMPPingInterval"
|
||||
let GROUP_DEFAULT_NETWORK_SMP_PING_COUNT = "networkSMPPingCount"
|
||||
let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive"
|
||||
@@ -55,6 +56,7 @@ public func registerGroupDefaults() {
|
||||
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT: NetCfg.defaults.tcpTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb,
|
||||
GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount,
|
||||
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
|
||||
@@ -278,6 +280,7 @@ public func getNetCfg() -> NetCfg {
|
||||
let tcpConnectTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
|
||||
let tcpTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
|
||||
let tcpTimeoutPerKb = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
|
||||
let rcvConcurrency = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
|
||||
let smpPingInterval = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
|
||||
let smpPingCount = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
|
||||
let enableKeepAlive = groupDefaults.bool(forKey: GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE)
|
||||
@@ -297,6 +300,7 @@ public func getNetCfg() -> NetCfg {
|
||||
tcpConnectTimeout: tcpConnectTimeout,
|
||||
tcpTimeout: tcpTimeout,
|
||||
tcpTimeoutPerKb: tcpTimeoutPerKb,
|
||||
rcvConcurrency: rcvConcurrency,
|
||||
tcpKeepAlive: tcpKeepAlive,
|
||||
smpPingInterval: smpPingInterval,
|
||||
smpPingCount: smpPingCount,
|
||||
@@ -310,6 +314,7 @@ public func setNetCfg(_ cfg: NetCfg) {
|
||||
groupDefaults.set(cfg.tcpConnectTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
|
||||
groupDefaults.set(cfg.tcpTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
|
||||
groupDefaults.set(cfg.tcpTimeoutPerKb, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
|
||||
groupDefaults.set(cfg.rcvConcurrency, forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
|
||||
groupDefaults.set(cfg.smpPingInterval, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
|
||||
groupDefaults.set(cfg.smpPingCount, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
|
||||
if let tcpKeepAlive = cfg.tcpKeepAlive {
|
||||
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
package chat.simplex.common.views.call
|
||||
|
||||
import android.content.Context
|
||||
import android.media.*
|
||||
import android.media.AudioManager.OnCommunicationDeviceChangedListener
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.common.platform.*
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
interface CallAudioDeviceManagerInterface {
|
||||
val devices: State<List<AudioDeviceInfo>>
|
||||
val currentDevice: MutableState<AudioDeviceInfo?>
|
||||
fun start()
|
||||
fun stop()
|
||||
// AudioDeviceInfo.AudioDeviceType
|
||||
fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean)
|
||||
// AudioDeviceInfo.AudioDeviceType
|
||||
fun selectDevice(id: Int)
|
||||
|
||||
companion object {
|
||||
fun new(): CallAudioDeviceManagerInterface =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PostSCallAudioDeviceManager()
|
||||
} else {
|
||||
PreSCallAudioDeviceManager()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
class PostSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
|
||||
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
|
||||
|
||||
private val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val oldDevices = devices.value
|
||||
devices.value = am.availableCommunicationDevices
|
||||
Log.d(TAG, "Added audio devices2: ${devices.value.map { it.type }}")
|
||||
|
||||
if (devices.value.size - oldDevices.size > 0) {
|
||||
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
devices.value = am.availableCommunicationDevices
|
||||
}
|
||||
}
|
||||
|
||||
private val listener: OnCommunicationDeviceChangedListener = OnCommunicationDeviceChangedListener { device ->
|
||||
devices.value = am.availableCommunicationDevices
|
||||
currentDevice.value = device
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
am.addOnCommunicationDeviceChangedListener(Executors.newSingleThreadExecutor(), listener)
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
am.removeOnCommunicationDeviceChangedListener(listener)
|
||||
}
|
||||
|
||||
override fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean) {
|
||||
Log.d(TAG, "selectLastExternalDeviceOrDefault: set audio mode, speaker enabled: $speaker")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
val commDevice = am.communicationDevice
|
||||
if (keepAnyNonEarpiece && commDevice != null && commDevice.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
|
||||
// some external device or speaker selected already, no need to change it
|
||||
return
|
||||
}
|
||||
|
||||
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
val externalDevice = devices.value.lastOrNull { it.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && it.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
|
||||
// External device already selected
|
||||
if (externalDevice != null && externalDevice.type == am.communicationDevice?.type) {
|
||||
return
|
||||
}
|
||||
if (externalDevice != null) {
|
||||
am.setCommunicationDevice(externalDevice)
|
||||
} else if (am.communicationDevice?.type != preferredSecondaryDevice) {
|
||||
am.availableCommunicationDevices.firstOrNull { it.type == preferredSecondaryDevice }?.let {
|
||||
am.setCommunicationDevice(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectDevice(id: Int) {
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
val device = devices.value.lastOrNull { it.id == id }
|
||||
if (device != null && am.communicationDevice?.id != id ) {
|
||||
am.setCommunicationDevice(device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PreSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
|
||||
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
|
||||
|
||||
private val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val wasSize = devices.value.size
|
||||
devices.value += addedDevices.filter { it.hasSupportedType() }
|
||||
val addedCount = devices.value.size - wasSize
|
||||
//if (addedCount > 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, false)
|
||||
//}
|
||||
}
|
||||
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
val wasSize = devices.value.size
|
||||
devices.value = devices.value.filterNot { removedDevices.any { rm -> rm.id == it.id } }
|
||||
//val removedCount = wasSize - devices.value.size
|
||||
//if (devices.value.count { it.hasSupportedType() } == 2 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, true)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
}
|
||||
|
||||
override fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean) {
|
||||
Log.d(TAG, "selectLastExternalDeviceOrDefault: set audio mode, speaker enabled: $speaker")
|
||||
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
val externalDevice = devices.value.lastOrNull { it.hasSupportedType() && it.isSource && it.isSink && it.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && it.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
|
||||
if (externalDevice != null) {
|
||||
selectDevice(externalDevice.id)
|
||||
} else {
|
||||
am.stopBluetoothSco()
|
||||
am.isWiredHeadsetOn = false
|
||||
am.isSpeakerphoneOn = preferredSecondaryDevice == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
|
||||
am.isBluetoothScoOn = false
|
||||
val newCurrentDevice = devices.value.firstOrNull { it.type == preferredSecondaryDevice }
|
||||
adaptToCurrentlyActiveDevice(newCurrentDevice)
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectDevice(id: Int) {
|
||||
val device = devices.value.lastOrNull { it.id == id }
|
||||
val isExternalDevice = device != null && device.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && device.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
if (isExternalDevice) {
|
||||
am.isSpeakerphoneOn = false
|
||||
if (device?.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
|
||||
am.isWiredHeadsetOn = true
|
||||
am.stopBluetoothSco()
|
||||
am.isBluetoothScoOn = false
|
||||
} else {
|
||||
am.isWiredHeadsetOn = false
|
||||
am.startBluetoothSco()
|
||||
am.isBluetoothScoOn = true
|
||||
}
|
||||
adaptToCurrentlyActiveDevice(device)
|
||||
} else {
|
||||
am.stopBluetoothSco()
|
||||
am.isWiredHeadsetOn = false
|
||||
am.isSpeakerphoneOn = device?.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
|
||||
am.isBluetoothScoOn = false
|
||||
adaptToCurrentlyActiveDevice(device)
|
||||
}
|
||||
}
|
||||
|
||||
private fun adaptToCurrentlyActiveDevice(newCurrentDevice: AudioDeviceInfo?) {
|
||||
currentDevice.value = newCurrentDevice
|
||||
}
|
||||
|
||||
private fun AudioDeviceInfo.hasSupportedType(): Boolean = when (type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> true
|
||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> true
|
||||
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> true
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET -> true
|
||||
AudioDeviceInfo.TYPE_BLE_SPEAKER -> true
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
val AudioDeviceInfo.icon: ImageResource
|
||||
get() = when (this.type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.images.ic_volume_down
|
||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> MR.images.ic_volume_up
|
||||
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET,
|
||||
AudioDeviceInfo.TYPE_BLE_SPEAKER -> MR.images.ic_bluetooth
|
||||
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> MR.images.ic_headphones
|
||||
|
||||
AudioDeviceInfo.TYPE_USB_HEADSET, AudioDeviceInfo.TYPE_USB_DEVICE -> MR.images.ic_usb
|
||||
else -> MR.images.ic_brand_awareness_filled
|
||||
}
|
||||
|
||||
val AudioDeviceInfo.name: StringResource?
|
||||
get() = when (this.type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.strings.audio_device_earpiece
|
||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> MR.strings.audio_device_speaker
|
||||
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET,
|
||||
AudioDeviceInfo.TYPE_BLE_SPEAKER -> null // Use product name instead
|
||||
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> MR.strings.audio_device_wired_headphones
|
||||
|
||||
AudioDeviceInfo.TYPE_USB_HEADSET, AudioDeviceInfo.TYPE_USB_DEVICE -> null // Use product name instead
|
||||
else -> null // Use product name instead
|
||||
}
|
||||
|
||||
+56
-73
@@ -26,8 +26,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -39,14 +38,14 @@ import androidx.webkit.WebViewAssetLoader
|
||||
import androidx.webkit.WebViewClientCompat
|
||||
import chat.simplex.common.helpers.showAllowPermissionInSettingsAlert
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.Contact
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import com.google.accompanist.permissions.*
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
@@ -71,7 +70,6 @@ fun activeCallDestroyWebView() = withApi {
|
||||
actual fun ActiveCallView() {
|
||||
val call = remember { chatModel.activeCall }.value
|
||||
val scope = rememberCoroutineScope()
|
||||
val audioViaBluetooth = rememberSaveable { mutableStateOf(false) }
|
||||
val proximityLock = remember {
|
||||
val pm = (androidAppContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
|
||||
if (pm.isWakeLockLevelSupported(PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
|
||||
@@ -87,41 +85,16 @@ actual fun ActiveCallView() {
|
||||
wasConnected.value = true
|
||||
}
|
||||
}
|
||||
val callAudioDeviceManager = remember { CallAudioDeviceManagerInterface.new() }
|
||||
DisposableEffect(Unit) {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
var btDeviceCount = 0
|
||||
val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val addedCount = addedDevices.count { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
|
||||
btDeviceCount += addedCount
|
||||
audioViaBluetooth.value = btDeviceCount > 0
|
||||
if (addedCount > 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
setCallSound(chatModel.activeCall.value?.soundSpeaker ?: return, audioViaBluetooth)
|
||||
}
|
||||
}
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
val removedCount = removedDevices.count { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
|
||||
btDeviceCount -= removedCount
|
||||
audioViaBluetooth.value = btDeviceCount > 0
|
||||
if (btDeviceCount == 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
setCallSound(chatModel.activeCall.value?.soundSpeaker ?: return, audioViaBluetooth)
|
||||
}
|
||||
}
|
||||
}
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
callAudioDeviceManager.start()
|
||||
onDispose {
|
||||
CallSoundsPlayer.stop()
|
||||
if (wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate()
|
||||
}
|
||||
dropAudioManagerOverrides()
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
callAudioDeviceManager.stop()
|
||||
if (proximityLock?.isHeld == true) {
|
||||
proximityLock.release()
|
||||
}
|
||||
@@ -147,7 +120,7 @@ actual fun ActiveCallView() {
|
||||
val callType = CallType(call.localMedia, r.capabilities)
|
||||
chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) }
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
CallSoundsPlayer.startConnectingCallSound(scope)
|
||||
activeCallWaitDeliveryReceipt(scope)
|
||||
}
|
||||
@@ -168,7 +141,7 @@ actual fun ActiveCallView() {
|
||||
val callStatus = json.decodeFromString<WebRTCCallStatus>("\"${r.state.connectionState}\"")
|
||||
if (callStatus == WebRTCCallStatus.Connected) {
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Connected, connectedAt = Clock.System.now()) }
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
}
|
||||
withBGApi { chatModel.controller.apiCallStatus(callRh, call.contact, callStatus) }
|
||||
} catch (e: Throwable) {
|
||||
@@ -177,7 +150,7 @@ actual fun ActiveCallView() {
|
||||
is WCallResponse.Connected -> {
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Connected, connectionInfo = r.connectionInfo) }
|
||||
scope.launch {
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
}
|
||||
}
|
||||
is WCallResponse.End -> {
|
||||
@@ -223,7 +196,7 @@ actual fun ActiveCallView() {
|
||||
else -> false
|
||||
}
|
||||
if (call != null && showOverlay) {
|
||||
ActiveCallOverlay(call, chatModel, audioViaBluetooth)
|
||||
ActiveCallOverlay(call, chatModel, callAudioDeviceManager)
|
||||
}
|
||||
}
|
||||
val context = LocalContext.current
|
||||
@@ -249,19 +222,21 @@ actual fun ActiveCallView() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, audioViaBluetooth: MutableState<Boolean>) {
|
||||
private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceManager: CallAudioDeviceManagerInterface) {
|
||||
ActiveCallOverlayLayout(
|
||||
call = call,
|
||||
speakerCanBeEnabled = !audioViaBluetooth.value,
|
||||
devices = remember { callAudioDeviceManager.devices }.value,
|
||||
currentDevice = remember { callAudioDeviceManager.currentDevice },
|
||||
dismiss = { withBGApi { chatModel.callManager.endCall(call) } },
|
||||
toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = !call.audioEnabled)) },
|
||||
selectDevice = { callAudioDeviceManager.selectDevice(it.id) },
|
||||
toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Video, enable = !call.videoEnabled)) },
|
||||
toggleSound = {
|
||||
var call = chatModel.activeCall.value
|
||||
if (call != null) {
|
||||
call = call.copy(soundSpeaker = !call.soundSpeaker)
|
||||
chatModel.activeCall.value = call
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
}
|
||||
},
|
||||
flipCamera = { chatModel.callCommand.add(WCallCommand.Camera(call.localCamera.flipped)) }
|
||||
@@ -272,42 +247,18 @@ private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, audioViaBluetoot
|
||||
fun ActiveCallOverlayDisabled(call: Call) {
|
||||
ActiveCallOverlayLayout(
|
||||
call = call,
|
||||
speakerCanBeEnabled = false,
|
||||
devices = emptyList(),
|
||||
currentDevice = remember { mutableStateOf(null) },
|
||||
enabled = false,
|
||||
dismiss = {},
|
||||
toggleAudio = {},
|
||||
selectDevice = {},
|
||||
toggleVideo = {},
|
||||
toggleSound = {},
|
||||
flipCamera = {}
|
||||
)
|
||||
}
|
||||
|
||||
private fun setCallSound(speaker: Boolean, audioViaBluetooth: MutableState<Boolean>) {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
Log.d(TAG, "setCallSound: set audio mode, speaker enabled: $speaker")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val btDevice = am.availableCommunicationDevices.lastOrNull { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
|
||||
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
if (btDevice != null) {
|
||||
am.setCommunicationDevice(btDevice)
|
||||
} else if (am.communicationDevice?.type != preferredSecondaryDevice) {
|
||||
am.availableCommunicationDevices.firstOrNull { it.type == preferredSecondaryDevice }?.let {
|
||||
am.setCommunicationDevice(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (audioViaBluetooth.value) {
|
||||
am.isSpeakerphoneOn = false
|
||||
am.startBluetoothSco()
|
||||
} else {
|
||||
am.stopBluetoothSco()
|
||||
am.isSpeakerphoneOn = speaker
|
||||
}
|
||||
am.isBluetoothScoOn = am.isBluetoothScoAvailableOffCall && audioViaBluetooth.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun dropAudioManagerOverrides() {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
am.mode = AudioManager.MODE_NORMAL
|
||||
@@ -323,10 +274,12 @@ private fun dropAudioManagerOverrides() {
|
||||
@Composable
|
||||
private fun ActiveCallOverlayLayout(
|
||||
call: Call,
|
||||
speakerCanBeEnabled: Boolean,
|
||||
devices: List<AudioDeviceInfo>,
|
||||
currentDevice: State<AudioDeviceInfo?>,
|
||||
enabled: Boolean = true,
|
||||
dismiss: () -> Unit,
|
||||
toggleAudio: () -> Unit,
|
||||
selectDevice: (AudioDeviceInfo) -> Unit,
|
||||
toggleVideo: () -> Unit,
|
||||
toggleSound: () -> Unit,
|
||||
flipCamera: () -> Unit
|
||||
@@ -339,6 +292,32 @@ private fun ActiveCallOverlayLayout(
|
||||
}
|
||||
}
|
||||
Column(Modifier.padding(horizontal = DEFAULT_PADDING)) {
|
||||
@Composable
|
||||
fun SelectSoundDevice() {
|
||||
if (devices.size == 2 &&
|
||||
devices.all { it.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE || it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } ||
|
||||
currentDevice.value == null ||
|
||||
devices.none { it.id == currentDevice.value?.id }
|
||||
) {
|
||||
ToggleSoundButton(call, enabled, toggleSound)
|
||||
} else {
|
||||
ExposedDropDownSettingWithIcon(
|
||||
devices.map { Triple(it, it.icon, if (it.name != null) generalGetString(it.name!!) else it.productName.toString()) },
|
||||
currentDevice,
|
||||
fontSize = 18.sp,
|
||||
iconSize = 40.dp,
|
||||
listIconSize = 30.dp,
|
||||
iconColor = Color(0xFFFFFFD8),
|
||||
minWidth = 300.dp,
|
||||
onSelected = {
|
||||
if (it != null) {
|
||||
selectDevice(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when (media) {
|
||||
CallMediaType.Video -> {
|
||||
VideoCallInfoView(call)
|
||||
@@ -347,7 +326,7 @@ private fun ActiveCallOverlayLayout(
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().padding(horizontal = 6.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
|
||||
ToggleAudioButton(call, enabled, toggleAudio)
|
||||
Spacer(Modifier.size(40.dp))
|
||||
SelectSoundDevice()
|
||||
IconButton(onClick = dismiss, enabled = enabled) {
|
||||
Icon(painterResource(MR.images.ic_call_end_filled), stringResource(MR.strings.icon_descr_hang_up), tint = if (enabled) Color.Red else MaterialTheme.colors.secondary, modifier = Modifier.size(64.dp))
|
||||
}
|
||||
@@ -385,7 +364,7 @@ private fun ActiveCallOverlayLayout(
|
||||
}
|
||||
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
|
||||
Box(Modifier.padding(end = 32.dp)) {
|
||||
ToggleSoundButton(call, speakerCanBeEnabled && enabled, toggleSound)
|
||||
SelectSoundDevice()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,9 +759,11 @@ fun PreviewActiveCallOverlayVideo() {
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "tcp")
|
||||
)
|
||||
),
|
||||
speakerCanBeEnabled = true,
|
||||
devices = emptyList(),
|
||||
currentDevice = remember { mutableStateOf(null) },
|
||||
dismiss = {},
|
||||
toggleAudio = {},
|
||||
selectDevice = {},
|
||||
toggleVideo = {},
|
||||
toggleSound = {},
|
||||
flipCamera = {}
|
||||
@@ -807,9 +788,11 @@ fun PreviewActiveCallOverlayAudio() {
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "udp")
|
||||
)
|
||||
),
|
||||
speakerCanBeEnabled = true,
|
||||
devices = emptyList(),
|
||||
currentDevice = remember { mutableStateOf(null) },
|
||||
dismiss = {},
|
||||
toggleAudio = {},
|
||||
selectDevice = {},
|
||||
toggleVideo = {},
|
||||
toggleSound = {},
|
||||
flipCamera = {}
|
||||
|
||||
+10
-2
@@ -135,6 +135,7 @@ class AppPreferences {
|
||||
val networkTCPConnectTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT, NetCfg.defaults.tcpConnectTimeout, NetCfg.proxyDefaults.tcpConnectTimeout)
|
||||
val networkTCPTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT, NetCfg.defaults.tcpTimeout, NetCfg.proxyDefaults.tcpTimeout)
|
||||
val networkTCPTimeoutPerKb = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB, NetCfg.defaults.tcpTimeoutPerKb, NetCfg.proxyDefaults.tcpTimeoutPerKb)
|
||||
val networkRcvConcurrency = mkIntPreference(SHARED_PREFS_NETWORK_RCV_CONCURRENCY, NetCfg.defaults.rcvConcurrency)
|
||||
val networkSMPPingInterval = mkLongPreference(SHARED_PREFS_NETWORK_SMP_PING_INTERVAL, NetCfg.defaults.smpPingInterval)
|
||||
val networkSMPPingCount = mkIntPreference(SHARED_PREFS_NETWORK_SMP_PING_COUNT, NetCfg.defaults.smpPingCount)
|
||||
val networkEnableKeepAlive = mkBoolPreference(SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE, NetCfg.defaults.enableKeepAlive)
|
||||
@@ -304,6 +305,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT = "NetworkTCPConnectTimeout"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT = "NetworkTCPTimeout"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
|
||||
private const val SHARED_PREFS_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
|
||||
private const val SHARED_PREFS_NETWORK_SMP_PING_INTERVAL = "NetworkSMPPingInterval"
|
||||
private const val SHARED_PREFS_NETWORK_SMP_PING_COUNT = "NetworkSMPPingCount"
|
||||
private const val SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE = "NetworkEnableKeepAlive"
|
||||
@@ -2305,6 +2307,7 @@ object ChatController {
|
||||
val tcpConnectTimeout = appPrefs.networkTCPConnectTimeout.get()
|
||||
val tcpTimeout = appPrefs.networkTCPTimeout.get()
|
||||
val tcpTimeoutPerKb = appPrefs.networkTCPTimeoutPerKb.get()
|
||||
val rcvConcurrency = appPrefs.networkRcvConcurrency.get()
|
||||
val smpPingInterval = appPrefs.networkSMPPingInterval.get()
|
||||
val smpPingCount = appPrefs.networkSMPPingCount.get()
|
||||
val enableKeepAlive = appPrefs.networkEnableKeepAlive.get()
|
||||
@@ -2324,6 +2327,7 @@ object ChatController {
|
||||
tcpConnectTimeout = tcpConnectTimeout,
|
||||
tcpTimeout = tcpTimeout,
|
||||
tcpTimeoutPerKb = tcpTimeoutPerKb,
|
||||
rcvConcurrency = rcvConcurrency,
|
||||
tcpKeepAlive = tcpKeepAlive,
|
||||
smpPingInterval = smpPingInterval,
|
||||
smpPingCount = smpPingCount
|
||||
@@ -2341,6 +2345,7 @@ object ChatController {
|
||||
appPrefs.networkTCPConnectTimeout.set(cfg.tcpConnectTimeout)
|
||||
appPrefs.networkTCPTimeout.set(cfg.tcpTimeout)
|
||||
appPrefs.networkTCPTimeoutPerKb.set(cfg.tcpTimeoutPerKb)
|
||||
appPrefs.networkRcvConcurrency.set(cfg.rcvConcurrency)
|
||||
appPrefs.networkSMPPingInterval.set(cfg.smpPingInterval)
|
||||
appPrefs.networkSMPPingCount.set(cfg.smpPingCount)
|
||||
if (cfg.tcpKeepAlive != null) {
|
||||
@@ -3034,6 +3039,7 @@ data class NetCfg(
|
||||
val tcpConnectTimeout: Long, // microseconds
|
||||
val tcpTimeout: Long, // microseconds
|
||||
val tcpTimeoutPerKb: Long, // microseconds
|
||||
val rcvConcurrency: Int, // pool size
|
||||
val tcpKeepAlive: KeepAliveOpts?,
|
||||
val smpPingInterval: Long, // microseconds
|
||||
val smpPingCount: Int,
|
||||
@@ -3058,9 +3064,10 @@ data class NetCfg(
|
||||
hostMode = HostMode.OnionViaSocks,
|
||||
requiredHostMode = false,
|
||||
sessionMode = TransportSessionMode.User,
|
||||
tcpConnectTimeout = 20_000_000,
|
||||
tcpConnectTimeout = 10_000_000,
|
||||
tcpTimeout = 15_000_000,
|
||||
tcpTimeoutPerKb = 10_000,
|
||||
rcvConcurrency = 12,
|
||||
tcpKeepAlive = KeepAliveOpts.defaults,
|
||||
smpPingInterval = 1200_000_000,
|
||||
smpPingCount = 3
|
||||
@@ -3072,9 +3079,10 @@ data class NetCfg(
|
||||
hostMode = HostMode.OnionViaSocks,
|
||||
requiredHostMode = false,
|
||||
sessionMode = TransportSessionMode.User,
|
||||
tcpConnectTimeout = 30_000_000,
|
||||
tcpConnectTimeout = 20_000_000,
|
||||
tcpTimeout = 20_000_000,
|
||||
tcpTimeoutPerKb = 15_000,
|
||||
rcvConcurrency = 8,
|
||||
tcpKeepAlive = KeepAliveOpts.defaults,
|
||||
smpPingInterval = 1200_000_000,
|
||||
smpPingCount = 3
|
||||
|
||||
+55
@@ -14,6 +14,7 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.res.MR
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSetting(
|
||||
@@ -79,6 +80,60 @@ fun <T> ExposedDropDownSetting(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSettingWithIcon(
|
||||
values: List<Triple<T, ImageResource, String>>,
|
||||
selection: State<T>,
|
||||
fontSize: TextUnit = 16.sp,
|
||||
iconSize: Dp = 40.dp,
|
||||
listIconSize: Dp = 30.dp,
|
||||
iconColor: Color = MenuTextColor,
|
||||
enabled: State<Boolean> = mutableStateOf(true),
|
||||
minWidth: Dp = 200.dp,
|
||||
onSelected: (T) -> Unit
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded.value,
|
||||
onExpandedChange = {
|
||||
expanded.value = !expanded.value && enabled.value
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(start = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
val choice = values.first { it.first == selection.value }
|
||||
Icon(painterResource(choice.second), choice.third, Modifier.size(iconSize), tint = iconColor)
|
||||
}
|
||||
DefaultExposedDropdownMenu(
|
||||
modifier = Modifier.widthIn(min = minWidth),
|
||||
expanded = expanded,
|
||||
) {
|
||||
values.forEach { selectionOption ->
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onSelected(selectionOption.first)
|
||||
expanded.value = false
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)
|
||||
) {
|
||||
Icon(painterResource(selectionOption.second), selectionOption.third, Modifier.size(listIconSize))
|
||||
Spacer(Modifier.width(15.dp))
|
||||
Text(
|
||||
selectionOption.third,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MenuTextColor,
|
||||
fontSize = fontSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSettingRow(
|
||||
title: String,
|
||||
|
||||
+13
-1
@@ -34,6 +34,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
val networkTCPConnectTimeout = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout) }
|
||||
val networkTCPTimeout = remember { mutableStateOf(currentCfgVal.tcpTimeout) }
|
||||
val networkTCPTimeoutPerKb = remember { mutableStateOf(currentCfgVal.tcpTimeoutPerKb) }
|
||||
var networkRcvConcurrency = remember { mutableStateOf(currentCfgVal.rcvConcurrency) }
|
||||
val networkSMPPingInterval = remember { mutableStateOf(currentCfgVal.smpPingInterval) }
|
||||
val networkSMPPingCount = remember { mutableStateOf(currentCfgVal.smpPingCount) }
|
||||
val networkEnableKeepAlive = remember { mutableStateOf(currentCfgVal.enableKeepAlive) }
|
||||
@@ -68,6 +69,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
tcpConnectTimeout = networkTCPConnectTimeout.value,
|
||||
tcpTimeout = networkTCPTimeout.value,
|
||||
tcpTimeoutPerKb = networkTCPTimeoutPerKb.value,
|
||||
rcvConcurrency = networkRcvConcurrency.value,
|
||||
tcpKeepAlive = tcpKeepAlive,
|
||||
smpPingInterval = networkSMPPingInterval.value,
|
||||
smpPingCount = networkSMPPingCount.value
|
||||
@@ -78,6 +80,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
networkTCPConnectTimeout.value = cfg.tcpConnectTimeout
|
||||
networkTCPTimeout.value = cfg.tcpTimeout
|
||||
networkTCPTimeoutPerKb.value = cfg.tcpTimeoutPerKb
|
||||
networkRcvConcurrency.value = cfg.rcvConcurrency
|
||||
networkSMPPingInterval.value = cfg.smpPingInterval
|
||||
networkSMPPingCount.value = cfg.smpPingCount
|
||||
networkEnableKeepAlive.value = cfg.enableKeepAlive
|
||||
@@ -110,6 +113,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
networkTCPConnectTimeout,
|
||||
networkTCPTimeout,
|
||||
networkTCPTimeoutPerKb,
|
||||
networkRcvConcurrency,
|
||||
networkSMPPingInterval,
|
||||
networkSMPPingCount,
|
||||
networkEnableKeepAlive,
|
||||
@@ -128,6 +132,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
networkTCPConnectTimeout: MutableState<Long>,
|
||||
networkTCPTimeout: MutableState<Long>,
|
||||
networkTCPTimeoutPerKb: MutableState<Long>,
|
||||
networkRcvConcurrency: MutableState<Int>,
|
||||
networkSMPPingInterval: MutableState<Long>,
|
||||
networkSMPPingCount: MutableState<Int>,
|
||||
networkEnableKeepAlive: MutableState<Boolean>,
|
||||
@@ -154,7 +159,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeout,
|
||||
listOf(7_500000, 10_000000, 15_000000, 20_000000, 30_000_000, 45_000_000), secondsLabel
|
||||
listOf(5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
@@ -170,6 +175,12 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
listOf(2_500, 5_000, 10_000, 15_000, 20_000, 30_000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
IntSettingRow(
|
||||
stringResource(MR.strings.network_option_rcv_concurrency), networkRcvConcurrency,
|
||||
listOf(1, 2, 4, 8, 12, 16, 24), ""
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_ping_interval), networkSMPPingInterval,
|
||||
@@ -418,6 +429,7 @@ fun PreviewAdvancedNetworkSettingsLayout() {
|
||||
networkTCPConnectTimeout = remember { mutableStateOf(10_000000) },
|
||||
networkTCPTimeout = remember { mutableStateOf(10_000000) },
|
||||
networkTCPTimeoutPerKb = remember { mutableStateOf(10_000) },
|
||||
networkRcvConcurrency = remember { mutableStateOf(8) },
|
||||
networkSMPPingInterval = remember { mutableStateOf(10_000000) },
|
||||
networkSMPPingCount = remember { mutableStateOf(3) },
|
||||
networkEnableKeepAlive = remember { mutableStateOf(true) },
|
||||
|
||||
@@ -836,6 +836,9 @@
|
||||
<string name="permissions_grant_in_settings">Grant in settings</string>
|
||||
<string name="permissions_find_in_settings_and_grant">Find this permission in Android settings and grant it manually.</string>
|
||||
<string name="permissions_open_settings">Open settings</string>
|
||||
<string name="audio_device_earpiece">Earpiece</string>
|
||||
<string name="audio_device_speaker">Speaker</string>
|
||||
<string name="audio_device_wired_headphones">Headphones</string>
|
||||
|
||||
<!-- SimpleXInfo -->
|
||||
<string name="next_generation_of_private_messaging">The next generation of private messaging</string>
|
||||
@@ -1456,6 +1459,7 @@
|
||||
<string name="network_option_tcp_connection_timeout">TCP connection timeout</string>
|
||||
<string name="network_option_protocol_timeout">Protocol timeout</string>
|
||||
<string name="network_option_protocol_timeout_per_kb">Protocol timeout per KB</string>
|
||||
<string name="network_option_rcv_concurrency">Receiving concurrency</string>
|
||||
<string name="network_option_ping_interval">PING interval</string>
|
||||
<string name="network_option_ping_count">PING count</string>
|
||||
<string name="network_option_enable_tcp_keep_alive">Enable TCP keep-alive</string>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M451-98v-313L257.5-217.5 217-258l222.5-222L217-702.5l40.5-40.5L451-549.5v-313h28.5L691-651 520-480.5l171 171L479.5-98H451Zm57.5-451.5 102-101.5-102-99.5v201Zm0 339.5 102-99.5-102-101.5v201Z"/></svg>
|
||||
|
After Width: | Height: | Size: 295 B |
+1
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M653-454.5V-512h142.5v57.5H653Zm32.5 290-117-86 32.5-45 117 86-32.5 45Zm-84.5-500-32.5-45 117-86 32.5 45-117 86ZM125-363v-234h157.5l196-196v626l-196-196H125Z"/></svg>
|
||||
|
After Width: | Height: | Size: 263 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M327-124.5H182q-22.969 0-40.234-17.266Q124.5-159.031 124.5-182v-298q0-74.09 28-138.795 28-64.705 76-112.705t112.684-76q64.684-28 138.75-28t138.816 28q64.75 28 112.75 76t76 112.705q28 64.705 28 138.795v298q0 22.969-17.266 40.234Q800.969-124.5 778-124.5H633v-290h145V-480q0-124.193-86.937-211.096Q604.125-778 479.884-778t-211.063 86.904Q182-604.193 182-480v65.5h145v290ZM269.5-357H182v175h87.5v-175Zm421 0v175H778v-175h-87.5Zm-421 0H182h87.5Zm421 0H778h-87.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 563 B |
+1
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M670-462.5v-163L569.5-525 544-550.5 667.5-674 544-791l25.5-25.5L670-716v-163h16.5l118 121-86 84 86 92.5-118 119H670Zm39.5-74 44-45-44-44v89Zm0-179.5 44-42-44-51.5v93.5Zm81 591Q677-125 558-180.5T338-338Q236-439 180.5-557.75T125-790.692q0-18.808 12.714-31.558Q150.429-835 169.5-835H306q14 0 23.75 9.75t13.75 24.75l26.929 123.641Q372-663.5 369.5-652q-2.5 11.5-10.229 19.226L261-533q26 44 54.688 81.658Q344.375-413.683 379-380q36.5 38 77.25 69.323Q497-279.353 542-255l95.544-98q9.456-10.5 21.357-14.25T682.5-369l117.362 25.438Q815-340 825-327.801q10 12.198 10 27.301v131q0 19.071-12.714 31.786Q809.571-125 790.5-125ZM232-585.5l81-82-23.5-110H183q1.5 41.5 13 88.25t36 103.75Zm364 358q40 19 88.166 31t93.334 14v-107l-102-21.5-79.5 83.5Zm-364-358Zm364 358Z"/></svg>
|
||||
|
After Width: | Height: | Size: 855 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M480.25-84.5q-31.013 0-50.631-19.559Q410-123.619 410-155q0-21.5 10.75-39.25t30.75-28.197V-334H303q-22.969 0-40.234-17.266Q245.5-368.531 245.5-391.5v-109q-20.5-9-31.25-26.359-10.75-17.358-10.75-39.358 0-31.664 19.535-51.224Q242.569-637 273.916-637t50.965 19.559q19.619 19.56 19.619 51.352 0 22.089-10.75 39.339T303-500.5v109h148.5v-338H373l107-145 107 145h-78v338h148V-496h-41.5v-140.5h141V-496h-42v104.5q0 22.969-17.266 40.234Q679.969-334 657-334H509v111.5q19.95 10.152 30.975 28.733Q551-175.187 551-154.912q0 31.531-19.869 50.971Q511.263-84.5 480.25-84.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 662 B |
+1
-1
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: c00c223f3bb295a62d8507e453bbeac61d102e3a
|
||||
tag: 3d40393ae83260245c7122b850fb42cd4243deba
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name: simplex-chat
|
||||
version: 5.7.0.0
|
||||
version: 5.7.0.1
|
||||
#synopsis:
|
||||
#description:
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."c00c223f3bb295a62d8507e453bbeac61d102e3a" = "0zbsz70rjhvrlkkiwnw43v7lg6r05lp5rwk7jmnn21zfjib4l621";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."3d40393ae83260245c7122b850fb42cd4243deba" = "0hkfz5k8kn1lqq0ms0b266pzsk48pq1gkfm7wgw6w5mr1pw149ay";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: simplex-chat
|
||||
version: 5.7.0.0
|
||||
version: 5.7.0.1
|
||||
category: Web, System, Services, Cryptography
|
||||
homepage: https://github.com/simplex-chat/simplex-chat#readme
|
||||
author: simplex.chat
|
||||
|
||||
Reference in New Issue
Block a user