android, desktop: withLongRunningApi when needed (#3710)
This commit is contained in:
parent
ab9a6dcab5
commit
5d8bb24d1c
@ -104,7 +104,7 @@ class SimplexService: Service() {
|
|||||||
if (wakeLock != null || isStartingService) return
|
if (wakeLock != null || isStartingService) return
|
||||||
val self = this
|
val self = this
|
||||||
isStartingService = true
|
isStartingService = true
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
val chatController = ChatController
|
val chatController = ChatController
|
||||||
waitDbMigrationEnds(chatController)
|
waitDbMigrationEnds(chatController)
|
||||||
try {
|
try {
|
||||||
@ -114,7 +114,7 @@ class SimplexService: Service() {
|
|||||||
Log.w(chat.simplex.app.TAG, "SimplexService: problem with the database: $chatDbStatus")
|
Log.w(chat.simplex.app.TAG, "SimplexService: problem with the database: $chatDbStatus")
|
||||||
showPassphraseNotification(chatDbStatus)
|
showPassphraseNotification(chatDbStatus)
|
||||||
safeStopService()
|
safeStopService()
|
||||||
return@withBGApi
|
return@withLongRunningApi
|
||||||
}
|
}
|
||||||
saveServiceState(self, ServiceState.STARTED)
|
saveServiceState(self, ServiceState.STARTED)
|
||||||
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
||||||
|
@ -8,9 +8,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import chat.simplex.common.R
|
import chat.simplex.common.R
|
||||||
import chat.simplex.common.platform.SoundPlayerInterface
|
import chat.simplex.common.platform.SoundPlayerInterface
|
||||||
import chat.simplex.common.platform.androidAppContext
|
import chat.simplex.common.platform.androidAppContext
|
||||||
import chat.simplex.common.views.helpers.withScope
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
|
|
||||||
object SoundPlayer: SoundPlayerInterface {
|
object SoundPlayer: SoundPlayerInterface {
|
||||||
private var player: MediaPlayer? = null
|
private var player: MediaPlayer? = null
|
||||||
@ -31,7 +29,7 @@ object SoundPlayer: SoundPlayerInterface {
|
|||||||
val vibrator = ContextCompat.getSystemService(androidAppContext, Vibrator::class.java)
|
val vibrator = ContextCompat.getSystemService(androidAppContext, Vibrator::class.java)
|
||||||
val effect = VibrationEffect.createOneShot(250, VibrationEffect.DEFAULT_AMPLITUDE)
|
val effect = VibrationEffect.createOneShot(250, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||||
playing = true
|
playing = true
|
||||||
withScope(scope) {
|
scope.launch {
|
||||||
while (playing) {
|
while (playing) {
|
||||||
if (sound) player?.start()
|
if (sound) player?.start()
|
||||||
vibrator?.vibrate(effect)
|
vibrator?.vibrate(effect)
|
||||||
|
@ -383,7 +383,7 @@ private fun DisabledBackgroundCallsButton() {
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(bottom = 24.dp)
|
.padding(bottom = 24.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
withBGApi {
|
withLongRunningApi {
|
||||||
show = !platform.androidAskToAllowBackgroundCalls()
|
show = !platform.androidAskToAllowBackgroundCalls()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ actual fun QRCodeScanner(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
!cameraPermissionState.hasPermission -> {
|
!cameraPermissionState.hasPermission -> {
|
||||||
Button({ withBGApi { cameraPermissionState.launchPermissionRequest() } }, modifier = modifier, colors = buttonColors) {
|
Button({ cameraPermissionState.launchPermissionRequest() }, modifier = modifier, colors = buttonColors) {
|
||||||
Icon(painterResource(MR.images.ic_camera_enhance), null)
|
Icon(painterResource(MR.images.ic_camera_enhance), null)
|
||||||
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
|
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
|
||||||
Text(stringResource(MR.strings.enable_camera_access))
|
Text(stringResource(MR.strings.enable_camera_access))
|
||||||
|
@ -102,7 +102,7 @@ fun AppearanceScope.AppearanceLayout(
|
|||||||
val state = rememberSaveable { mutableStateOf(languagePref.get() ?: "system") }
|
val state = rememberSaveable { mutableStateOf(languagePref.get() ?: "system") }
|
||||||
LangSelector(state) {
|
LangSelector(state) {
|
||||||
state.value = it
|
state.value = it
|
||||||
withBGApi {
|
withApi {
|
||||||
delay(200)
|
delay(200)
|
||||||
val activity = context as? Activity
|
val activity = context as? Activity
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
|
@ -55,7 +55,7 @@ abstract class NtfManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openChatAction(userId: Long?, chatId: ChatId) {
|
fun openChatAction(userId: Long?, chatId: ChatId) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
awaitChatStartedIfNeeded(chatModel)
|
awaitChatStartedIfNeeded(chatModel)
|
||||||
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
||||||
// TODO include remote host ID in desktop notifications?
|
// TODO include remote host ID in desktop notifications?
|
||||||
@ -70,7 +70,7 @@ abstract class NtfManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showChatsAction(userId: Long?) {
|
fun showChatsAction(userId: Long?) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
awaitChatStartedIfNeeded(chatModel)
|
awaitChatStartedIfNeeded(chatModel)
|
||||||
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
|
||||||
// TODO include remote host ID in desktop notifications?
|
// TODO include remote host ID in desktop notifications?
|
||||||
|
@ -267,7 +267,7 @@ fun ComposeView(
|
|||||||
fun loadLinkPreview(url: String, wait: Long? = null) {
|
fun loadLinkPreview(url: String, wait: Long? = null) {
|
||||||
if (pendingLinkUrl.value == url) {
|
if (pendingLinkUrl.value == url) {
|
||||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
if (wait != null) delay(wait)
|
if (wait != null) delay(wait)
|
||||||
val lp = getLinkPreview(url)
|
val lp = getLinkPreview(url)
|
||||||
if (lp != null && pendingLinkUrl.value == url) {
|
if (lp != null && pendingLinkUrl.value == url) {
|
||||||
@ -551,7 +551,7 @@ fun ComposeView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun sendMessage(ttl: Int?) {
|
fun sendMessage(ttl: Int?) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
sendMessageAsync(null, false, ttl)
|
sendMessageAsync(null, false, ttl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea
|
|||||||
},
|
},
|
||||||
inviteMembers = {
|
inviteMembers = {
|
||||||
allowModifyMembers = false
|
allowModifyMembers = false
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
for (contactId in selectedContacts) {
|
for (contactId in selectedContacts) {
|
||||||
val member = chatModel.controller.apiAddMember(rhId, groupInfo.groupId, contactId, selectedRole.value)
|
val member = chatModel.controller.apiAddMember(rhId, groupInfo.groupId, contactId, selectedRole.value)
|
||||||
if (member != null) {
|
if (member != null) {
|
||||||
|
@ -94,7 +94,7 @@ fun CIFileView(
|
|||||||
FileProtocol.LOCAL -> {}
|
FileProtocol.LOCAL -> {}
|
||||||
}
|
}
|
||||||
file.fileStatus is CIFileStatus.RcvComplete || (file.fileStatus is CIFileStatus.SndStored && file.fileProtocol == FileProtocol.LOCAL) -> {
|
file.fileStatus is CIFileStatus.RcvComplete || (file.fileStatus is CIFileStatus.SndStored && file.fileProtocol == FileProtocol.LOCAL) -> {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 60_000, deadlock = 600_000) {
|
||||||
var filePath = getLoadedFilePath(file)
|
var filePath = getLoadedFilePath(file)
|
||||||
if (chatModel.connectedToRemote() && filePath == null) {
|
if (chatModel.connectedToRemote() && filePath == null) {
|
||||||
file.loadRemoteFile(true)
|
file.loadRemoteFile(true)
|
||||||
|
@ -41,7 +41,7 @@ fun CIVideoView(
|
|||||||
val filePath = remember(file, CIFile.cachedRemoteFileRequests.toList()) { mutableStateOf(getLoadedFilePath(file)) }
|
val filePath = remember(file, CIFile.cachedRemoteFileRequests.toList()) { mutableStateOf(getLoadedFilePath(file)) }
|
||||||
if (chatModel.connectedToRemote()) {
|
if (chatModel.connectedToRemote()) {
|
||||||
LaunchedEffect(file) {
|
LaunchedEffect(file) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 60_000, deadlock = 600_000) {
|
||||||
if (file != null && file.loaded && getLoadedFilePath(file) == null) {
|
if (file != null && file.loaded && getLoadedFilePath(file) == null) {
|
||||||
file.loadRemoteFile(false)
|
file.loadRemoteFile(false)
|
||||||
filePath.value = getLoadedFilePath(file)
|
filePath.value = getLoadedFilePath(file)
|
||||||
|
@ -213,7 +213,7 @@ fun ChatItemView(
|
|||||||
showMenu.value = false
|
showMenu.value = false
|
||||||
}
|
}
|
||||||
if (chatModel.connectedToRemote() && fileSource == null) {
|
if (chatModel.connectedToRemote() && fileSource == null) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 60_000, deadlock = 600_000) {
|
||||||
cItem.file?.loadRemoteFile(true)
|
cItem.file?.loadRemoteFile(true)
|
||||||
fileSource = getLoadedFileSource(cItem.file)
|
fileSource = getLoadedFileSource(cItem.file)
|
||||||
shareIfExists()
|
shareIfExists()
|
||||||
|
@ -62,7 +62,7 @@ fun DatabaseEncryptionView(m: ChatModel) {
|
|||||||
initialRandomDBPassphrase,
|
initialRandomDBPassphrase,
|
||||||
progressIndicator,
|
progressIndicator,
|
||||||
onConfirmEncrypt = {
|
onConfirmEncrypt = {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
encryptDatabase(currentKey, newKey, confirmNewKey, initialRandomDBPassphrase, useKeychain, storedKey, progressIndicator)
|
encryptDatabase(currentKey, newKey, confirmNewKey, initialRandomDBPassphrase, useKeychain, storedKey, progressIndicator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -368,7 +368,7 @@ fun chatArchiveTitle(chatArchiveTime: Instant, chatLastStart: Instant): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatDbChanged: MutableState<Boolean>, progressIndicator: MutableState<Boolean>? = null) {
|
fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatDbChanged: MutableState<Boolean>, progressIndicator: MutableState<Boolean>? = null) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
try {
|
try {
|
||||||
progressIndicator?.value = true
|
progressIndicator?.value = true
|
||||||
if (chatDbChanged.value) {
|
if (chatDbChanged.value) {
|
||||||
@ -378,12 +378,12 @@ fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatDbChanged
|
|||||||
if (m.chatDbStatus.value !is DBMigrationResult.OK) {
|
if (m.chatDbStatus.value !is DBMigrationResult.OK) {
|
||||||
/** Hide current view and show [DatabaseErrorView] */
|
/** Hide current view and show [DatabaseErrorView] */
|
||||||
ModalManager.closeAllModalsEverywhere()
|
ModalManager.closeAllModalsEverywhere()
|
||||||
return@withBGApi
|
return@withLongRunningApi
|
||||||
}
|
}
|
||||||
val user = m.currentUser.value
|
val user = m.currentUser.value
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
ModalManager.closeAllModalsEverywhere()
|
ModalManager.closeAllModalsEverywhere()
|
||||||
return@withBGApi
|
return@withLongRunningApi
|
||||||
} else {
|
} else {
|
||||||
m.controller.startChat(user)
|
m.controller.startChat(user)
|
||||||
}
|
}
|
||||||
@ -581,7 +581,7 @@ private fun importArchive(
|
|||||||
progressIndicator.value = true
|
progressIndicator.value = true
|
||||||
val archivePath = saveArchiveFromURI(importedArchiveURI)
|
val archivePath = saveArchiveFromURI(importedArchiveURI)
|
||||||
if (archivePath != null) {
|
if (archivePath != null) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 60_000, deadlock = 180_000) {
|
||||||
try {
|
try {
|
||||||
m.controller.apiDeleteStorage()
|
m.controller.apiDeleteStorage()
|
||||||
try {
|
try {
|
||||||
|
@ -16,7 +16,7 @@ class ProcessedErrors <T: AgentErrorType>(val interval: Long) {
|
|||||||
|
|
||||||
fun newError(error: T, offerRestart: Boolean) {
|
fun newError(error: T, offerRestart: Boolean) {
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
timer = withBGApi {
|
timer = withLongRunningApi(slow = 70_000, deadlock = 130_000) {
|
||||||
val delayBeforeNext = (lastShownTimestamp + interval) - System.currentTimeMillis()
|
val delayBeforeNext = (lastShownTimestamp + interval) - System.currentTimeMillis()
|
||||||
if ((lastShownOfferRestart || !offerRestart) && delayBeforeNext >= 0) {
|
if ((lastShownOfferRestart || !offerRestart) && delayBeforeNext >= 0) {
|
||||||
delay(delayBeforeNext)
|
delay(delayBeforeNext)
|
||||||
|
@ -27,11 +27,9 @@ import kotlin.math.*
|
|||||||
|
|
||||||
private val singleThreadDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val singleThreadDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
|
|
||||||
fun withApi(action: suspend CoroutineScope.() -> Unit): Job = withScope(GlobalScope, action)
|
fun withApi(action: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
|
||||||
fun withScope(scope: CoroutineScope, action: suspend CoroutineScope.() -> Unit): Job =
|
|
||||||
Exception().let {
|
Exception().let {
|
||||||
scope.launch { withContext(Dispatchers.Main, block = { wrapWithLogging(action, it) }) }
|
CoroutineScope(Dispatchers.Main).launch(block = { wrapWithLogging(action, it) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun withBGApi(action: suspend CoroutineScope.() -> Unit): Job =
|
fun withBGApi(action: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
@ -49,7 +49,7 @@ fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (LAResult) -> Unit) {
|
private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (LAResult) -> Unit) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
try {
|
try {
|
||||||
/** Waiting until [initChatController] finishes */
|
/** Waiting until [initChatController] finishes */
|
||||||
while (m.ctrlInitInProgress.value) {
|
while (m.ctrlInitInProgress.value) {
|
||||||
@ -78,7 +78,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (
|
|||||||
displayNamePref.set(null)
|
displayNamePref.set(null)
|
||||||
reinitChatController()
|
reinitChatController()
|
||||||
if (m.currentUser.value != null) {
|
if (m.currentUser.value != null) {
|
||||||
return@withBGApi
|
return@withLongRunningApi
|
||||||
}
|
}
|
||||||
var profile: Profile? = null
|
var profile: Profile? = null
|
||||||
if (!displayName.isNullOrEmpty()) {
|
if (!displayName.isNullOrEmpty()) {
|
||||||
|
@ -50,7 +50,7 @@ fun SetupDatabasePassphrase(m: ChatModel) {
|
|||||||
confirmNewKey,
|
confirmNewKey,
|
||||||
progressIndicator,
|
progressIndicator,
|
||||||
onConfirmEncrypt = {
|
onConfirmEncrypt = {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
if (m.chatRunning.value == true) {
|
if (m.chatRunning.value == true) {
|
||||||
// Stop chat if it's started before doing anything
|
// Stop chat if it's started before doing anything
|
||||||
stopChatAsync(m)
|
stopChatAsync(m)
|
||||||
|
@ -96,7 +96,7 @@ fun PrivacySettingsView(
|
|||||||
val currentUser = chatModel.currentUser.value
|
val currentUser = chatModel.currentUser.value
|
||||||
if (currentUser != null) {
|
if (currentUser != null) {
|
||||||
fun setSendReceiptsContacts(enable: Boolean, clearOverrides: Boolean) {
|
fun setSendReceiptsContacts(enable: Boolean, clearOverrides: Boolean) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
val mrs = UserMsgReceiptSettings(enable, clearOverrides)
|
val mrs = UserMsgReceiptSettings(enable, clearOverrides)
|
||||||
chatModel.controller.apiSetUserContactReceipts(currentUser, mrs)
|
chatModel.controller.apiSetUserContactReceipts(currentUser, mrs)
|
||||||
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
||||||
@ -119,7 +119,7 @@ fun PrivacySettingsView(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setSendReceiptsGroups(enable: Boolean, clearOverrides: Boolean) {
|
fun setSendReceiptsGroups(enable: Boolean, clearOverrides: Boolean) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
|
||||||
val mrs = UserMsgReceiptSettings(enable, clearOverrides)
|
val mrs = UserMsgReceiptSettings(enable, clearOverrides)
|
||||||
chatModel.controller.apiSetUserGroupReceipts(currentUser, mrs)
|
chatModel.controller.apiSetUserGroupReceipts(currentUser, mrs)
|
||||||
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
||||||
|
@ -211,15 +211,13 @@ actual object SoundPlayer: SoundPlayerInterface {
|
|||||||
var playing = false
|
var playing = false
|
||||||
|
|
||||||
override fun start(scope: CoroutineScope, sound: Boolean) {
|
override fun start(scope: CoroutineScope, sound: Boolean) {
|
||||||
withBGApi {
|
val tmpFile = File(tmpDir, UUID.randomUUID().toString())
|
||||||
val tmpFile = File(tmpDir, UUID.randomUUID().toString())
|
tmpFile.deleteOnExit()
|
||||||
tmpFile.deleteOnExit()
|
SoundPlayer::class.java.getResource("/media/ring_once.mp3").openStream()!!.use { it.copyTo(tmpFile.outputStream()) }
|
||||||
SoundPlayer::class.java.getResource("/media/ring_once.mp3").openStream()!!.use { it.copyTo(tmpFile.outputStream()) }
|
playing = true
|
||||||
playing = true
|
scope.launch {
|
||||||
while (playing) {
|
while (playing && sound) {
|
||||||
if (sound) {
|
AudioPlayer.play(CryptoFile.plain(tmpFile.absolutePath), mutableStateOf(true), mutableStateOf(0), mutableStateOf(0), true)
|
||||||
AudioPlayer.play(CryptoFile.plain(tmpFile.absolutePath), mutableStateOf(true), mutableStateOf(0), mutableStateOf(0), true)
|
|
||||||
}
|
|
||||||
delay(3500)
|
delay(3500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserL
|
|||||||
}
|
}
|
||||||
var fileSource = getLoadedFileSource(cItem.file)
|
var fileSource = getLoadedFileSource(cItem.file)
|
||||||
if (chatModel.connectedToRemote() && fileSource == null) {
|
if (chatModel.connectedToRemote() && fileSource == null) {
|
||||||
withBGApi {
|
withLongRunningApi(slow = 60_000, deadlock = 600_000) {
|
||||||
cItem.file?.loadRemoteFile(true)
|
cItem.file?.loadRemoteFile(true)
|
||||||
fileSource = getLoadedFileSource(cItem.file)
|
fileSource = getLoadedFileSource(cItem.file)
|
||||||
saveIfExists()
|
saveIfExists()
|
||||||
@ -51,7 +51,7 @@ actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserL
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager) = withBGApi {
|
actual fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager) = withLongRunningApi(slow = 60_000, deadlock = 600_000) {
|
||||||
var fileSource = getLoadedFileSource(cItem.file)
|
var fileSource = getLoadedFileSource(cItem.file)
|
||||||
if (chatModel.connectedToRemote() && fileSource == null) {
|
if (chatModel.connectedToRemote() && fileSource == null) {
|
||||||
cItem.file?.loadRemoteFile(true)
|
cItem.file?.loadRemoteFile(true)
|
||||||
@ -63,10 +63,10 @@ actual fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager) = w
|
|||||||
val tmpFile = File(tmpDir, fileSource.filePath)
|
val tmpFile = File(tmpDir, fileSource.filePath)
|
||||||
tmpFile.deleteOnExit()
|
tmpFile.deleteOnExit()
|
||||||
try {
|
try {
|
||||||
decryptCryptoFile(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs ?: return@withBGApi, tmpFile.absolutePath)
|
decryptCryptoFile(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs ?: return@withLongRunningApi, tmpFile.absolutePath)
|
||||||
} 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())
|
||||||
return@withBGApi
|
return@withLongRunningApi
|
||||||
}
|
}
|
||||||
tmpFile.absolutePath
|
tmpFile.absolutePath
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,7 +51,7 @@ fun AppearanceScope.AppearanceLayout(
|
|||||||
val state = rememberSaveable { mutableStateOf(languagePref.get() ?: "system") }
|
val state = rememberSaveable { mutableStateOf(languagePref.get() ?: "system") }
|
||||||
LangSelector(state) {
|
LangSelector(state) {
|
||||||
state.value = it
|
state.value = it
|
||||||
withBGApi {
|
withApi {
|
||||||
delay(200)
|
delay(200)
|
||||||
if (it == "system") {
|
if (it == "system") {
|
||||||
languagePref.set(null)
|
languagePref.set(null)
|
||||||
|
Loading…
Reference in New Issue
Block a user