android, desktop: run with stopped chat (#3624)
* android, desktop: run with stopped chat * way to prevent starting a chat in case of not saved database key * rename * change position of a call * new way of doing the same * better * exit process --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
parent
c9b1d54f13
commit
e6b5727003
@ -13,7 +13,6 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import chat.simplex.common.platform.Log
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
@ -21,12 +20,13 @@ import chat.simplex.common.AppLock
|
|||||||
import chat.simplex.common.helpers.requiresIgnoringBattery
|
import chat.simplex.common.helpers.requiresIgnoringBattery
|
||||||
import chat.simplex.common.model.ChatController
|
import chat.simplex.common.model.ChatController
|
||||||
import chat.simplex.common.model.NotificationsMode
|
import chat.simplex.common.model.NotificationsMode
|
||||||
import chat.simplex.common.platform.androidAppContext
|
import chat.simplex.common.platform.*
|
||||||
import chat.simplex.common.views.helpers.*
|
import chat.simplex.common.views.helpers.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import dev.icerock.moko.resources.compose.painterResource
|
import dev.icerock.moko.resources.compose.painterResource
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
// based on:
|
// based on:
|
||||||
// https://robertohuertas.com/2019/06/29/android_foreground_services/
|
// https://robertohuertas.com/2019/06/29/android_foreground_services/
|
||||||
@ -173,6 +173,11 @@ class SimplexService: Service() {
|
|||||||
// Just to make sure that after restart of the app the user will need to re-authenticate
|
// Just to make sure that after restart of the app the user will need to re-authenticate
|
||||||
AppLock.clearAuthState()
|
AppLock.clearAuthState()
|
||||||
|
|
||||||
|
if (appPreferences.chatStopped.get()) {
|
||||||
|
stopSelf()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
|
||||||
// If notification service isn't enabled or battery optimization isn't disabled, we shouldn't restart the service
|
// If notification service isn't enabled or battery optimization isn't disabled, we shouldn't restart the service
|
||||||
if (!SimplexApp.context.allowToStartServiceAfterAppExit()) {
|
if (!SimplexApp.context.allowToStartServiceAfterAppExit()) {
|
||||||
return
|
return
|
||||||
|
@ -97,12 +97,14 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
|
|||||||
mainActivity.get()?.recreate()
|
mainActivity.get()?.recreate()
|
||||||
} else {
|
} else {
|
||||||
mainActivity.get()?.apply {
|
mainActivity.get()?.apply {
|
||||||
window
|
runOnUiThread {
|
||||||
?.decorView
|
window
|
||||||
?.findViewById<ViewGroup>(android.R.id.content)
|
?.decorView
|
||||||
?.removeViewAt(0)
|
?.findViewById<ViewGroup>(android.R.id.content)
|
||||||
setContent {
|
?.removeViewAt(0)
|
||||||
AppScreen()
|
setContent {
|
||||||
|
AppScreen()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,7 @@ class AppPreferences {
|
|||||||
val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null)
|
val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null)
|
||||||
val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null)
|
val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null)
|
||||||
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
||||||
|
val chatStopped = mkBoolPreference(SHARED_PREFS_CHAT_STOPPED, false)
|
||||||
val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false)
|
val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false)
|
||||||
val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false)
|
val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false)
|
||||||
val networkUseSocksProxy = mkBoolPreference(SHARED_PREFS_NETWORK_USE_SOCKS_PROXY, false)
|
val networkUseSocksProxy = mkBoolPreference(SHARED_PREFS_NETWORK_USE_SOCKS_PROXY, false)
|
||||||
@ -273,6 +274,7 @@ class AppPreferences {
|
|||||||
private const val SHARED_PREFS_APP_LANGUAGE = "AppLanguage"
|
private const val SHARED_PREFS_APP_LANGUAGE = "AppLanguage"
|
||||||
private const val SHARED_PREFS_ONBOARDING_STAGE = "OnboardingStage"
|
private const val SHARED_PREFS_ONBOARDING_STAGE = "OnboardingStage"
|
||||||
private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart"
|
private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart"
|
||||||
|
private const val SHARED_PREFS_CHAT_STOPPED = "ChatStopped"
|
||||||
private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools"
|
private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools"
|
||||||
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
|
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
|
||||||
private const val SHARED_PREFS_NETWORK_USE_SOCKS_PROXY = "NetworkUseSocksProxy"
|
private const val SHARED_PREFS_NETWORK_USE_SOCKS_PROXY = "NetworkUseSocksProxy"
|
||||||
@ -346,14 +348,8 @@ object ChatController {
|
|||||||
try {
|
try {
|
||||||
if (chatModel.chatRunning.value == true) return
|
if (chatModel.chatRunning.value == true) return
|
||||||
apiSetNetworkConfig(getNetCfg())
|
apiSetNetworkConfig(getNetCfg())
|
||||||
apiSetTempFolder(coreTmpDir.absolutePath)
|
|
||||||
apiSetFilesFolder(appFilesDir.absolutePath)
|
|
||||||
if (appPlatform.isDesktop) {
|
|
||||||
apiSetRemoteHostsFolder(remoteHostsDir.absolutePath)
|
|
||||||
}
|
|
||||||
apiSetXFTPConfig(getXFTPCfg())
|
|
||||||
apiSetEncryptLocalFiles(appPrefs.privacyEncryptLocalFiles.get())
|
|
||||||
val justStarted = apiStartChat()
|
val justStarted = apiStartChat()
|
||||||
|
appPrefs.chatStopped.set(false)
|
||||||
val users = listUsers(null)
|
val users = listUsers(null)
|
||||||
chatModel.users.clear()
|
chatModel.users.clear()
|
||||||
chatModel.users.addAll(users)
|
chatModel.users.addAll(users)
|
||||||
@ -365,6 +361,9 @@ object ChatController {
|
|||||||
chatModel.chatRunning.value = true
|
chatModel.chatRunning.value = true
|
||||||
startReceiver()
|
startReceiver()
|
||||||
setLocalDeviceName(appPrefs.deviceNameForRemoteAccess.get()!!)
|
setLocalDeviceName(appPrefs.deviceNameForRemoteAccess.get()!!)
|
||||||
|
if (appPreferences.onboardingStage.get() == OnboardingStage.OnboardingComplete && !chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.get()) {
|
||||||
|
chatModel.setDeliveryReceipts.value = true
|
||||||
|
}
|
||||||
Log.d(TAG, "startChat: started")
|
Log.d(TAG, "startChat: started")
|
||||||
} else {
|
} else {
|
||||||
updatingChatsMutex.withLock {
|
updatingChatsMutex.withLock {
|
||||||
@ -383,13 +382,6 @@ object ChatController {
|
|||||||
Log.d(TAG, "user: null")
|
Log.d(TAG, "user: null")
|
||||||
try {
|
try {
|
||||||
if (chatModel.chatRunning.value == true) return
|
if (chatModel.chatRunning.value == true) return
|
||||||
apiSetTempFolder(coreTmpDir.absolutePath)
|
|
||||||
apiSetFilesFolder(appFilesDir.absolutePath)
|
|
||||||
if (appPlatform.isDesktop) {
|
|
||||||
apiSetRemoteHostsFolder(remoteHostsDir.absolutePath)
|
|
||||||
}
|
|
||||||
apiSetXFTPConfig(getXFTPCfg())
|
|
||||||
apiSetEncryptLocalFiles(appPrefs.privacyEncryptLocalFiles.get())
|
|
||||||
chatModel.users.clear()
|
chatModel.users.clear()
|
||||||
chatModel.currentUser.value = null
|
chatModel.currentUser.value = null
|
||||||
chatModel.localUserCreated.value = false
|
chatModel.localUserCreated.value = false
|
||||||
@ -596,19 +588,19 @@ object ChatController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun apiSetTempFolder(tempFolder: String) {
|
suspend fun apiSetTempFolder(tempFolder: String) {
|
||||||
val r = sendCmd(null, CC.SetTempFolder(tempFolder))
|
val r = sendCmd(null, CC.SetTempFolder(tempFolder))
|
||||||
if (r is CR.CmdOk) return
|
if (r is CR.CmdOk) return
|
||||||
throw Error("failed to set temp folder: ${r.responseType} ${r.details}")
|
throw Error("failed to set temp folder: ${r.responseType} ${r.details}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun apiSetFilesFolder(filesFolder: String) {
|
suspend fun apiSetFilesFolder(filesFolder: String) {
|
||||||
val r = sendCmd(null, CC.SetFilesFolder(filesFolder))
|
val r = sendCmd(null, CC.SetFilesFolder(filesFolder))
|
||||||
if (r is CR.CmdOk) return
|
if (r is CR.CmdOk) return
|
||||||
throw Error("failed to set files folder: ${r.responseType} ${r.details}")
|
throw Error("failed to set files folder: ${r.responseType} ${r.details}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun apiSetRemoteHostsFolder(remoteHostsFolder: String) {
|
suspend fun apiSetRemoteHostsFolder(remoteHostsFolder: String) {
|
||||||
val r = sendCmd(null, CC.SetRemoteHostsFolder(remoteHostsFolder))
|
val r = sendCmd(null, CC.SetRemoteHostsFolder(remoteHostsFolder))
|
||||||
if (r is CR.CmdOk) return
|
if (r is CR.CmdOk) return
|
||||||
throw Error("failed to set remote hosts folder: ${r.responseType} ${r.details}")
|
throw Error("failed to set remote hosts folder: ${r.responseType} ${r.details}")
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
package chat.simplex.common.platform
|
package chat.simplex.common.platform
|
||||||
|
|
||||||
import chat.simplex.common.model.*
|
import chat.simplex.common.model.*
|
||||||
|
import chat.simplex.common.model.ChatModel.controller
|
||||||
|
import chat.simplex.common.model.ChatModel.currentUser
|
||||||
import chat.simplex.common.views.helpers.*
|
import chat.simplex.common.views.helpers.*
|
||||||
|
import chat.simplex.common.views.helpers.DatabaseUtils.ksDatabasePassword
|
||||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||||
|
import chat.simplex.res.MR
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
@ -39,13 +44,17 @@ val chatController: ChatController = ChatController
|
|||||||
fun initChatControllerAndRunMigrations(ignoreSelfDestruct: Boolean) {
|
fun initChatControllerAndRunMigrations(ignoreSelfDestruct: Boolean) {
|
||||||
if (ignoreSelfDestruct || DatabaseUtils.ksSelfDestructPassword.get() == null) {
|
if (ignoreSelfDestruct || DatabaseUtils.ksSelfDestructPassword.get() == null) {
|
||||||
withBGApi {
|
withBGApi {
|
||||||
initChatController()
|
if (appPreferences.chatStopped.get() && appPreferences.storeDBPassphrase.get() && ksDatabasePassword.get() != null) {
|
||||||
|
initChatController(startChat = ::showStartChatAfterRestartAlert)
|
||||||
|
} else {
|
||||||
|
initChatController()
|
||||||
|
}
|
||||||
runMigrations()
|
runMigrations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: Boolean = true) {
|
suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred<Boolean> = { CompletableDeferred(true) }) {
|
||||||
try {
|
try {
|
||||||
chatModel.ctrlInitInProgress.value = true
|
chatModel.ctrlInitInProgress.value = true
|
||||||
val dbKey = useKey ?: DatabaseUtils.useDatabaseKey()
|
val dbKey = useKey ?: DatabaseUtils.useDatabaseKey()
|
||||||
@ -62,45 +71,66 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
|
|||||||
chatModel.chatDbStatus.value = res
|
chatModel.chatDbStatus.value = res
|
||||||
if (res != DBMigrationResult.OK) {
|
if (res != DBMigrationResult.OK) {
|
||||||
Log.d(TAG, "Unable to migrate successfully: $res")
|
Log.d(TAG, "Unable to migrate successfully: $res")
|
||||||
} else if (startChat) {
|
return
|
||||||
// If we migrated successfully means previous re-encryption process on database level finished successfully too
|
}
|
||||||
if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null)
|
controller.apiSetTempFolder(coreTmpDir.absolutePath)
|
||||||
val user = chatController.apiGetActiveUser(null)
|
controller.apiSetFilesFolder(appFilesDir.absolutePath)
|
||||||
if (user == null) {
|
if (appPlatform.isDesktop) {
|
||||||
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
controller.apiSetRemoteHostsFolder(remoteHostsDir.absolutePath)
|
||||||
chatModel.currentUser.value = null
|
}
|
||||||
chatModel.users.clear()
|
controller.apiSetXFTPConfig(controller.getXFTPCfg())
|
||||||
if (appPlatform.isDesktop) {
|
controller.apiSetEncryptLocalFiles(controller.appPrefs.privacyEncryptLocalFiles.get())
|
||||||
/**
|
// If we migrated successfully means previous re-encryption process on database level finished successfully too
|
||||||
* Setting it here to null because otherwise the screen will flash in [MainScreen] after the first start
|
if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null)
|
||||||
* because of default value of [OnboardingStage.OnboardingComplete]
|
val user = chatController.apiGetActiveUser(null)
|
||||||
* */
|
chatModel.currentUser.value = user
|
||||||
chatModel.localUserCreated.value = null
|
if (user == null) {
|
||||||
if (chatController.listRemoteHosts()?.isEmpty() == true) {
|
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
|
||||||
chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
|
chatModel.currentUser.value = null
|
||||||
}
|
chatModel.users.clear()
|
||||||
chatController.startChatWithoutUser()
|
if (appPlatform.isDesktop) {
|
||||||
} else {
|
/**
|
||||||
|
* Setting it here to null because otherwise the screen will flash in [MainScreen] after the first start
|
||||||
|
* because of default value of [OnboardingStage.OnboardingComplete]
|
||||||
|
* */
|
||||||
|
chatModel.localUserCreated.value = null
|
||||||
|
if (chatController.listRemoteHosts()?.isEmpty() == true) {
|
||||||
chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
|
chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
|
||||||
}
|
}
|
||||||
|
chatController.startChatWithoutUser()
|
||||||
} else {
|
} else {
|
||||||
val savedOnboardingStage = appPreferences.onboardingStage.get()
|
chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
|
||||||
val newStage = if (listOf(OnboardingStage.Step1_SimpleXInfo, OnboardingStage.Step2_CreateProfile).contains(savedOnboardingStage) && chatModel.users.size == 1) {
|
|
||||||
OnboardingStage.Step3_CreateSimpleXAddress
|
|
||||||
} else {
|
|
||||||
savedOnboardingStage
|
|
||||||
}
|
|
||||||
if (appPreferences.onboardingStage.get() != newStage) {
|
|
||||||
appPreferences.onboardingStage.set(newStage)
|
|
||||||
}
|
|
||||||
if (appPreferences.onboardingStage.get() == OnboardingStage.OnboardingComplete && !chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.get()) {
|
|
||||||
chatModel.setDeliveryReceipts.value = true
|
|
||||||
}
|
|
||||||
chatController.startChat(user)
|
|
||||||
platform.androidChatInitializedAndStarted()
|
|
||||||
}
|
}
|
||||||
|
} else if (startChat().await()) {
|
||||||
|
val savedOnboardingStage = appPreferences.onboardingStage.get()
|
||||||
|
val newStage = if (listOf(OnboardingStage.Step1_SimpleXInfo, OnboardingStage.Step2_CreateProfile).contains(savedOnboardingStage) && chatModel.users.size == 1) {
|
||||||
|
OnboardingStage.Step3_CreateSimpleXAddress
|
||||||
|
} else {
|
||||||
|
savedOnboardingStage
|
||||||
|
}
|
||||||
|
if (appPreferences.onboardingStage.get() != newStage) {
|
||||||
|
appPreferences.onboardingStage.set(newStage)
|
||||||
|
}
|
||||||
|
chatController.startChat(user)
|
||||||
|
platform.androidChatInitializedAndStarted()
|
||||||
|
} else {
|
||||||
|
chatController.getUserChatData(null)
|
||||||
|
chatModel.localUserCreated.value = currentUser.value != null
|
||||||
|
chatModel.chatRunning.value = false
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
chatModel.ctrlInitInProgress.value = false
|
chatModel.ctrlInitInProgress.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showStartChatAfterRestartAlert(): CompletableDeferred<Boolean> {
|
||||||
|
val deferred = CompletableDeferred<Boolean>()
|
||||||
|
AlertManager.shared.showAlertDialog(
|
||||||
|
title = generalGetString(MR.strings.start_chat_question),
|
||||||
|
text = generalGetString(MR.strings.chat_is_stopped_you_should_transfer_database),
|
||||||
|
onConfirm = { deferred.complete(true) },
|
||||||
|
onDismiss = { deferred.complete(false) },
|
||||||
|
onDismissRequest = { deferred.complete(false) }
|
||||||
|
)
|
||||||
|
return deferred
|
||||||
|
}
|
||||||
|
@ -44,7 +44,7 @@ fun DatabaseErrorView(
|
|||||||
|
|
||||||
fun callRunChat(confirmMigrations: MigrationConfirmation? = null) {
|
fun callRunChat(confirmMigrations: MigrationConfirmation? = null) {
|
||||||
val useKey = if (useKeychain) null else dbKey.value
|
val useKey = if (useKeychain) null else dbKey.value
|
||||||
runChat(useKey, confirmMigrations, chatDbStatus, progressIndicator, appPreferences)
|
runChat(useKey, confirmMigrations, chatDbStatus, progressIndicator)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveAndRunChatOnClick() {
|
fun saveAndRunChatOnClick() {
|
||||||
@ -190,13 +190,14 @@ private fun runChat(
|
|||||||
confirmMigrations: MigrationConfirmation? = null,
|
confirmMigrations: MigrationConfirmation? = null,
|
||||||
chatDbStatus: State<DBMigrationResult?>,
|
chatDbStatus: State<DBMigrationResult?>,
|
||||||
progressIndicator: MutableState<Boolean>,
|
progressIndicator: MutableState<Boolean>,
|
||||||
prefs: AppPreferences
|
|
||||||
) = CoroutineScope(Dispatchers.Default).launch {
|
) = CoroutineScope(Dispatchers.Default).launch {
|
||||||
// Don't do things concurrently. Shouldn't be here concurrently, just in case
|
// Don't do things concurrently. Shouldn't be here concurrently, just in case
|
||||||
if (progressIndicator.value) return@launch
|
if (progressIndicator.value) return@launch
|
||||||
progressIndicator.value = true
|
progressIndicator.value = true
|
||||||
try {
|
try {
|
||||||
initChatController(dbKey, confirmMigrations)
|
initChatController(dbKey, confirmMigrations,
|
||||||
|
startChat = if (appPreferences.chatStopped.get()) ::showStartChatAfterRestartAlert else { { CompletableDeferred(true) } }
|
||||||
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d(TAG, "initializeChat ${e.stackTraceToString()}")
|
Log.d(TAG, "initializeChat ${e.stackTraceToString()}")
|
||||||
}
|
}
|
||||||
|
@ -378,12 +378,12 @@ private fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatD
|
|||||||
ModalManager.closeAllModalsEverywhere()
|
ModalManager.closeAllModalsEverywhere()
|
||||||
return@withApi
|
return@withApi
|
||||||
}
|
}
|
||||||
if (m.currentUser.value == null) {
|
val user = m.currentUser.value
|
||||||
|
if (user == null) {
|
||||||
ModalManager.closeAllModalsEverywhere()
|
ModalManager.closeAllModalsEverywhere()
|
||||||
return@withApi
|
return@withApi
|
||||||
} else {
|
} else {
|
||||||
m.controller.apiStartChat()
|
m.controller.startChat(user)
|
||||||
m.chatRunning.value = true
|
|
||||||
}
|
}
|
||||||
val ts = Clock.System.now()
|
val ts = Clock.System.now()
|
||||||
m.controller.appPrefs.chatLastStart.set(ts)
|
m.controller.appPrefs.chatLastStart.set(ts)
|
||||||
@ -453,6 +453,7 @@ private fun stopChat(m: ChatModel) {
|
|||||||
suspend fun stopChatAsync(m: ChatModel) {
|
suspend fun stopChatAsync(m: ChatModel) {
|
||||||
m.controller.apiStopChat()
|
m.controller.apiStopChat()
|
||||||
m.chatRunning.value = false
|
m.chatRunning.value = false
|
||||||
|
controller.appPrefs.chatStopped.set(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteChatAsync(m: ChatModel) {
|
suspend fun deleteChatAsync(m: ChatModel) {
|
||||||
|
@ -82,7 +82,6 @@ sealed class DBMigrationResult {
|
|||||||
@Serializable @SerialName("unknown") data class Unknown(val json: String): DBMigrationResult()
|
@Serializable @SerialName("unknown") data class Unknown(val json: String): DBMigrationResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum class MigrationConfirmation(val value: String) {
|
enum class MigrationConfirmation(val value: String) {
|
||||||
YesUp("yesUp"),
|
YesUp("yesUp"),
|
||||||
YesUpDown ("yesUpDown"),
|
YesUpDown ("yesUpDown"),
|
||||||
|
@ -84,7 +84,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (
|
|||||||
m.chatDbChanged.value = true
|
m.chatDbChanged.value = true
|
||||||
m.chatDbStatus.value = null
|
m.chatDbStatus.value = null
|
||||||
try {
|
try {
|
||||||
initChatController(startChat = true)
|
initChatController()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d(TAG, "initializeChat ${e.stackTraceToString()}")
|
Log.d(TAG, "initializeChat ${e.stackTraceToString()}")
|
||||||
}
|
}
|
||||||
|
@ -1112,6 +1112,8 @@
|
|||||||
<!-- ChatModel.chatRunning interactions -->
|
<!-- ChatModel.chatRunning interactions -->
|
||||||
<string name="chat_is_stopped_indication">Chat is stopped</string>
|
<string name="chat_is_stopped_indication">Chat is stopped</string>
|
||||||
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">You can start chat via app Settings / Database or by restarting the app.</string>
|
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">You can start chat via app Settings / Database or by restarting the app.</string>
|
||||||
|
<string name="start_chat_question">Start chat?</string>
|
||||||
|
<string name="chat_is_stopped_you_should_transfer_database">Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</string>
|
||||||
|
|
||||||
<!-- ChatArchiveView.kt -->
|
<!-- ChatArchiveView.kt -->
|
||||||
<string name="chat_archive_header">Chat archive</string>
|
<string name="chat_archive_header">Chat archive</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user