Refact coroutine session scope

This commit is contained in:
ganfra 2021-04-22 19:28:17 +02:00 committed by Benoit Marty
parent bd0fcce572
commit 80b155e042
23 changed files with 142 additions and 79 deletions

View File

@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.session
import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import kotlinx.coroutines.Job
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.failure.GlobalError
@ -57,8 +56,6 @@ import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService
import org.matrix.android.sdk.api.session.typing.TypingUsersTracker
import org.matrix.android.sdk.api.session.user.UserService
import org.matrix.android.sdk.api.session.widgets.WidgetService
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
/**
* This interface defines interactions with a session.
@ -258,13 +255,13 @@ interface Session :
/**
* A global session listener to get notified for some events.
*/
interface Listener {
interface Listener : SessionLifecycleObserver {
/**
* Possible cases:
* - The access token is not valid anymore,
* - a M_CONSENT_NOT_GIVEN error has been received from the homeserver
*/
fun onGlobalError(globalError: GlobalError)
fun onGlobalError(session: Session, globalError: GlobalError)
}
val sharedSecretStorageService: SharedSecretStorageService
@ -275,10 +272,4 @@ interface Session :
* Maintenance API, allows to print outs info on DB size to logcat
*/
fun logDbUsageInfo()
/**
* Launch a coroutine using the session scope
*/
fun launch(context: CoroutineContext = EmptyCoroutineContext,
block: suspend () -> Unit): Job
}

View File

@ -14,20 +14,19 @@
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session
package org.matrix.android.sdk.api.session
import androidx.annotation.MainThread
/**
* This defines methods associated with some lifecycle events of a session.
* A list of SessionLifecycle will be injected into [DefaultSession]
*/
internal interface SessionLifecycleObserver {
interface SessionLifecycleObserver {
/*
Called when the session is opened
*/
@MainThread
fun onSessionStarted() {
fun onSessionStarted(session: Session) {
// noop
}
@ -35,7 +34,7 @@ internal interface SessionLifecycleObserver {
Called when the session is cleared
*/
@MainThread
fun onClearCache() {
fun onClearCache(session: Session) {
// noop
}
@ -43,7 +42,7 @@ internal interface SessionLifecycleObserver {
Called when the session is closed
*/
@MainThread
fun onSessionStopped() {
fun onSessionStopped(session: Session) {
// noop
}
}

View File

@ -20,6 +20,7 @@ import io.realm.Realm
import io.realm.RealmConfiguration
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.database.helper.nextDisplayIndex
import org.matrix.android.sdk.internal.database.model.ChunkEntity
import org.matrix.android.sdk.internal.database.model.ChunkEntityFields
@ -29,7 +30,7 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
import org.matrix.android.sdk.internal.database.model.deleteOnCascade
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
import org.matrix.android.sdk.internal.task.TaskExecutor
import timber.log.Timber
@ -47,7 +48,7 @@ private const val MIN_NUMBER_OF_EVENTS_BY_CHUNK = 300
internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val realmConfiguration: RealmConfiguration,
private val taskExecutor: TaskExecutor) : SessionLifecycleObserver {
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
taskExecutor.executorScope.launch(Dispatchers.Default) {
awaitTransaction(realmConfiguration) { realm ->
val allRooms = realm.where(RoomEntity::class.java).findAll()

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.database
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.util.createBackgroundHandler
import io.realm.Realm
import io.realm.RealmChangeListener
@ -28,6 +28,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.cancelChildren
import org.matrix.android.sdk.api.session.Session
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
@ -46,7 +47,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
private val backgroundRealm = AtomicReference<Realm>()
private lateinit var results: AtomicReference<RealmResults<T>>
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
if (isStarted.compareAndSet(false, true)) {
BACKGROUND_HANDLER.post {
val realm = Realm.getInstance(realmConfiguration)
@ -58,7 +59,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
}
}
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
if (isStarted.compareAndSet(true, false)) {
BACKGROUND_HANDLER.post {
results.getAndSet(null).removeAllChangeListeners()
@ -70,7 +71,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
}
}
override fun onClearCache() {
override fun onClearCache(session: Session) {
observerScope.coroutineContext.cancelChildren()
}
}

View File

@ -20,8 +20,9 @@ import android.os.Looper
import androidx.annotation.MainThread
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import javax.inject.Inject
import kotlin.concurrent.getOrSet
@ -44,14 +45,14 @@ internal class RealmSessionProvider @Inject constructor(@SessionDatabase private
}
@MainThread
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
realmThreadLocal.getOrSet {
Realm.getInstance(monarchy.realmConfiguration)
}
}
@MainThread
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
realmThreadLocal.get()?.close()
realmThreadLocal.remove()
}

View File

@ -19,14 +19,15 @@ package org.matrix.android.sdk.internal.session
import androidx.annotation.MainThread
import dagger.Lazy
import io.realm.RealmConfiguration
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.federation.FederationService
import org.matrix.android.sdk.api.pushrules.PushRuleService
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.account.AccountService
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
import org.matrix.android.sdk.api.session.cache.CacheService
@ -76,7 +77,6 @@ import org.matrix.android.sdk.internal.util.createUIHandler
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Provider
import kotlin.coroutines.CoroutineContext
@SessionScope
internal class DefaultSession @Inject constructor(
@ -163,10 +163,12 @@ internal class DefaultSession @Inject constructor(
override fun open() {
assert(!isOpen)
isOpen = true
sessionCoroutineScopeHolder.start()
cryptoService.get().ensureDevice()
uiHandler.post {
lifecycleObservers.forEach { it.onSessionStarted() }
lifecycleObservers.forEach { it.onSessionStarted(this) }
sessionListeners.dispatch {
it.onSessionStarted(this)
}
}
globalErrorHandler.listener = this
}
@ -204,11 +206,13 @@ internal class DefaultSession @Inject constructor(
override fun close() {
assert(isOpen)
sessionCoroutineScopeHolder.stop()
stopSync()
// timelineEventDecryptor.destroy()
uiHandler.post {
lifecycleObservers.forEach { it.onSessionStopped() }
lifecycleObservers.forEach { it.onSessionStopped(this) }
sessionListeners.dispatch {
it.onSessionStopped(this)
}
}
cryptoService.get().close()
isOpen = false
@ -233,14 +237,21 @@ internal class DefaultSession @Inject constructor(
stopSync()
stopAnyBackgroundSync()
uiHandler.post {
lifecycleObservers.forEach { it.onClearCache() }
lifecycleObservers.forEach { it.onClearCache(this) }
sessionListeners.dispatch {
it.onClearCache(this)
}
}
withContext(NonCancellable) {
cacheService.get().clearCache()
}
cacheService.get().clearCache()
workManagerProvider.cancelAllWorks()
}
override fun onGlobalError(globalError: GlobalError) {
sessionListeners.dispatchGlobalError(globalError)
sessionListeners.dispatch {
it.onGlobalError(this, globalError)
}
}
override fun contentUrlResolver() = contentUrlResolver
@ -307,11 +318,4 @@ internal class DefaultSession @Inject constructor(
override fun logDbUsageInfo() {
RealmDebugTools(realmConfiguration).logInfo("Session")
}
override fun launch(context: CoroutineContext,
block: suspend () -> Unit): Job {
return sessionCoroutineScopeHolder.scope?.launch(context) {
block()
} ?: throw IllegalStateException("Session is closed")
}
}

View File

@ -21,19 +21,24 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import javax.inject.Inject
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
@SessionScope
internal class SessionCoroutineScopeHolder @Inject constructor() {
internal class SessionCoroutineScopeHolder @Inject constructor(): SessionLifecycleObserver {
var scope: CoroutineScope? = null
private set
val scope: CoroutineScope = CoroutineScope(SupervisorJob())
fun start() {
scope = CoroutineScope(SupervisorJob())
override fun onSessionStopped(session: Session) {
scope.cancelChildren()
}
fun stop() {
scope?.coroutineContext?.cancelChildren(CancellationException("Closing session"))
scope = null
override fun onClearCache(session: Session) {
scope.cancelChildren()
}
private fun CoroutineScope.cancelChildren(){
coroutineContext.cancelChildren(CancellationException("Closing session"))
}
}

View File

@ -16,7 +16,6 @@
package org.matrix.android.sdk.internal.session
import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
@ -36,10 +35,10 @@ internal class SessionListeners @Inject constructor() {
}
}
fun dispatchGlobalError(globalError: GlobalError) {
fun dispatch(block: (Session.Listener) -> Unit) {
synchronized(listeners) {
listeners.forEach {
it.onGlobalError(globalError)
block(it)
}
}
}

View File

@ -33,6 +33,7 @@ import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.auth.data.sessionId
import org.matrix.android.sdk.api.crypto.MXCryptoConfig
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.accountdata.AccountDataService
import org.matrix.android.sdk.api.session.events.EventService
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService
@ -343,6 +344,10 @@ internal abstract class SessionModule {
@IntoSet
abstract fun bindRealmSessionProvider(provider: RealmSessionProvider): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindSessionCoroutineScopeHolder(holder: SessionCoroutineScopeHolder): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindEventSenderProcessorAsSessionLifecycleObserver(processor: EventSenderProcessorCoroutine): SessionLifecycleObserver

View File

@ -36,7 +36,7 @@ import org.matrix.android.sdk.internal.di.AuthenticatedIdentity
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
import org.matrix.android.sdk.internal.extensions.observeNotNull
import org.matrix.android.sdk.internal.network.RetrofitFactory
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.identity.data.IdentityStore
import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask
@ -51,6 +51,7 @@ import org.matrix.android.sdk.internal.util.ensureProtocol
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
import timber.log.Timber
import javax.inject.Inject
import javax.net.ssl.HttpsURLConnection
@ -86,7 +87,7 @@ internal class DefaultIdentityService @Inject constructor(
private val listeners = mutableSetOf<IdentityServiceListener>()
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
lifecycleRegistry.currentState = Lifecycle.State.STARTED
// Observe the account data change
accountDataDataSource
@ -111,7 +112,7 @@ internal class DefaultIdentityService @Inject constructor(
}
}
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
}

View File

@ -21,6 +21,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
@ -29,7 +30,7 @@ import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.extensions.observeNotNull
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
@ -77,7 +78,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
currentConfigs.add(defaultConfig)
}
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
lifecycleRegistry.currentState = Lifecycle.State.STARTED
observeWellknownConfig()
accountDataDataSource
@ -105,7 +106,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
}
}
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
}

View File

@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.room.send.queue
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
internal interface EventSenderProcessor: SessionLifecycleObserver {

View File

@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.auth.data.SessionParams
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.failure.getRetryDelay
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.util.Cancelable
@ -72,7 +73,7 @@ internal class EventSenderProcessorCoroutine @Inject constructor(
*/
private val cancelableBag = ConcurrentHashMap<String, Cancelable>()
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
// We should check for sending events not handled because app was killed
// But we should be careful of only took those that was submitted to us, because if it's
// for example it's a media event it is handled by some worker and he will handle it

View File

@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.failure.isTokenError
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.sync.SyncState
@ -64,11 +65,11 @@ internal class EventSenderProcessorThread @Inject constructor(
memento.unTrack(task)
}
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
start()
}
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
interrupt()
}

View File

@ -17,12 +17,13 @@
package org.matrix.android.sdk.internal.session.widgets
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
import org.matrix.android.sdk.api.session.widgets.WidgetURLFormatter
import org.matrix.android.sdk.api.util.appendParamToUrl
import org.matrix.android.sdk.api.util.appendParamsToUrl
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
import org.matrix.android.sdk.internal.session.widgets.token.GetScalarTokenTask
@ -37,12 +38,12 @@ internal class DefaultWidgetURLFormatter @Inject constructor(private val integra
private lateinit var currentConfig: IntegrationManagerConfig
private var whiteListedUrls: List<String> = emptyList()
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
setupWithConfiguration()
integrationManager.addListener(this)
}
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
integrationManager.removeListener(this)
}

View File

@ -22,6 +22,7 @@ import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.api.session.events.model.Content
@ -34,7 +35,7 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure
import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
import org.matrix.android.sdk.api.session.SessionLifecycleObserver
import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
@ -57,12 +58,12 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry }
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner)
override fun onSessionStarted() {
override fun onSessionStarted(session: Session) {
lifecycleRegistry.currentState = Lifecycle.State.STARTED
integrationManager.addListener(this)
}
override fun onSessionStopped() {
override fun onSessionStopped(session: Session) {
integrationManager.removeListener(this)
lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
}

View File

@ -28,6 +28,7 @@ import com.bumptech.glide.signature.ObjectKey
import im.vector.app.core.extensions.vectorComponent
import im.vector.app.core.files.LocalFilesHelper
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -114,7 +115,7 @@ class VectorGlideDataFetcher(context: Context,
callback.onLoadFailed(IllegalArgumentException("No File service"))
}
// Use the file vector service, will avoid flickering and redownload after upload
activeSessionHolder.getSafeActiveSession()?.launch {
activeSessionHolder.getSafeActiveSession()?.coroutineScope?.launch {
val result = runCatching {
fileService.downloadFile(
fileName = data.filename,

View File

@ -161,6 +161,7 @@ import im.vector.app.features.permalink.NavigationInterceptor
import im.vector.app.features.permalink.PermalinkHandler
import im.vector.app.features.reactions.EmojiReactionPickerActivity
import im.vector.app.features.roomprofile.RoomProfileActivity
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.share.SharedData
@ -1744,12 +1745,13 @@ class RoomDetailFragment @Inject constructor(
}
private fun onSaveActionClicked(action: EventSharedAction.Save) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q
&& !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) {
sharedActionViewModel.pendingAction = action
return
}
session.launch {
session.coroutineScope.launch {
val result = runCatching { session.fileService().downloadFile(messageContent = action.messageContent) }
if (!isAdded) return@launch
result.fold(

View File

@ -53,6 +53,7 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.typing.TypingHelper
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import im.vector.app.features.raw.wellknown.getElementWellknown
import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences
import io.reactivex.Observable
@ -570,7 +571,7 @@ class RoomDetailViewModel @AssistedInject constructor(
* Convert a send mode to a draft and save the draft
*/
private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) = withState {
session.launch {
session.coroutineScope.launch {
when {
it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> {
setState { copy(sendMode = it.sendMode.copy(action.draft)) }
@ -1317,7 +1318,7 @@ class RoomDetailViewModel @AssistedInject constructor(
}
}
bufferedMostRecentDisplayedEvent.root.eventId?.let { eventId ->
session.launch {
session.coroutineScope.launch {
tryOrNull { room.setReadReceipt(eventId) }
}
}

View File

@ -23,6 +23,8 @@ import androidx.core.app.RemoteInput
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.vectorComponent
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.Room
@ -76,7 +78,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
activeSessionHolder.getSafeActiveSession()?.let { session ->
val room = session.getRoom(roomId)
if (room != null) {
session.launch {
session.coroutineScope.launch {
tryOrNull { room.join() }
}
}
@ -87,7 +89,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
activeSessionHolder.getSafeActiveSession()?.let { session ->
val room = session.getRoom(roomId)
if (room != null) {
session.launch {
session.coroutineScope.launch {
tryOrNull { room.leave() }
}
}
@ -98,7 +100,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
activeSessionHolder.getActiveSession().let { session ->
val room = session.getRoom(roomId)
if (room != null) {
session.launch {
session.coroutineScope.launch {
tryOrNull { room.markAsRead(ReadService.MarkAsReadParams.READ_RECEIPT) }
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.session
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import org.matrix.android.sdk.api.session.Session
private val sessionCoroutineScopes = HashMap<String, CoroutineScope>(1)
val Session.coroutineScope: CoroutineScope
get() {
return synchronized(sessionCoroutineScopes) {
sessionCoroutineScopes.getOrPut(sessionId) {
CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
}
}
}

View File

@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import im.vector.app.core.extensions.postLiveEvent
import im.vector.app.core.utils.LiveEvent
import kotlinx.coroutines.cancelChildren
import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject
@ -32,7 +33,16 @@ class SessionListener @Inject constructor() : Session.Listener {
val globalErrorLiveData: LiveData<LiveEvent<GlobalError>>
get() = _globalErrorLiveData
override fun onGlobalError(globalError: GlobalError) {
override fun onGlobalError(session: Session, globalError: GlobalError) {
_globalErrorLiveData.postLiveEvent(globalError)
}
override fun onSessionStopped(session: Session) {
session.coroutineScope.coroutineContext.cancelChildren()
}
override fun onClearCache(session: Session) {
session.coroutineScope.coroutineContext.cancelChildren()
}
}

View File

@ -20,7 +20,9 @@ import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.pushrules.RuleIds
@ -50,7 +52,7 @@ class TestAccountSettings @Inject constructor(private val stringProvider: String
override fun doFix() {
if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished
session.launch {
session.coroutineScope.launch {
tryOrNull {
session.updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
}