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