Fix coroutine scope (#4820)

* Inject the session scope instead of the application scope where it's possible.

* Create AppCoroutineScope annotation to let developers explicitly choose the appropriate CoroutineScope when injecting one.
This commit is contained in:
Benoit Marty
2025-06-04 17:33:51 +02:00
committed by GitHub
parent 88124cd770
commit 03e23477b7
58 changed files with 172 additions and 72 deletions

View File

@@ -27,6 +27,7 @@ import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.x.BuildConfig import io.element.android.x.BuildConfig
import io.element.android.x.R import io.element.android.x.R
import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.CoroutineName
@@ -56,6 +57,7 @@ object AppModule {
} }
@Provides @Provides
@AppCoroutineScope
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
fun providesAppCoroutineScope(): CoroutineScope { fun providesAppCoroutineScope(): CoroutineScope {
return MainScope() + CoroutineName("ElementX Scope") return MainScope() + CoroutineName("ElementX Scope")

View File

@@ -65,6 +65,7 @@ import io.element.android.libraries.architecture.waitForNavTargetAttached
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.MAIN_SPACE import io.element.android.libraries.matrix.api.core.MAIN_SPACE
@@ -104,7 +105,8 @@ class LoggedInFlowNode @AssistedInject constructor(
private val secureBackupEntryPoint: SecureBackupEntryPoint, private val secureBackupEntryPoint: SecureBackupEntryPoint,
private val userProfileEntryPoint: UserProfileEntryPoint, private val userProfileEntryPoint: UserProfileEntryPoint,
private val ftueEntryPoint: FtueEntryPoint, private val ftueEntryPoint: FtueEntryPoint,
private val coroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val ftueService: FtueService, private val ftueService: FtueService,
private val roomDirectoryEntryPoint: RoomDirectoryEntryPoint, private val roomDirectoryEntryPoint: RoomDirectoryEntryPoint,
private val shareEntryPoint: ShareEntryPoint, private val shareEntryPoint: ShareEntryPoint,
@@ -175,7 +177,7 @@ class LoggedInFlowNode @AssistedInject constructor(
appNavigationStateService.onNavigateToSession(id, matrixClient.sessionId) appNavigationStateService.onNavigateToSession(id, matrixClient.sessionId)
// TODO We do not support Space yet, so directly navigate to main space // TODO We do not support Space yet, so directly navigate to main space
appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE) appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE)
loggedInFlowProcessor.observeEvents(coroutineScope) loggedInFlowProcessor.observeEvents(sessionCoroutineScope)
matrixClient.sessionVerificationService().setListener(verificationListener) matrixClient.sessionVerificationService().setListener(verificationListener)
ftueService.state ftueService.state
@@ -313,7 +315,7 @@ class LoggedInFlowNode @AssistedInject constructor(
} }
override fun onForwardedToSingleRoom(roomId: RoomId) { override fun onForwardedToSingleRoom(roomId: RoomId) {
coroutineScope.launch { attachRoom(roomId.toRoomIdOrAlias(), clearBackstack = false) } sessionCoroutineScope.launch { attachRoom(roomId.toRoomIdOrAlias(), clearBackstack = false) }
} }
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) { override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {

View File

@@ -30,6 +30,7 @@ import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -50,7 +51,8 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
private val messagesEntryPoint: MessagesEntryPoint, private val messagesEntryPoint: MessagesEntryPoint,
private val roomDetailsEntryPoint: RoomDetailsEntryPoint, private val roomDetailsEntryPoint: RoomDetailsEntryPoint,
private val appNavigationStateService: AppNavigationStateService, private val appNavigationStateService: AppNavigationStateService,
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val matrixClient: MatrixClient, private val matrixClient: MatrixClient,
private val activeRoomsHolder: ActiveRoomsHolder, private val activeRoomsHolder: ActiveRoomsHolder,
roomComponentFactory: RoomComponentFactory, roomComponentFactory: RoomComponentFactory,
@@ -92,7 +94,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
trackVisitedRoom() trackVisitedRoom()
}, },
onResume = { onResume = {
appCoroutineScope.launch { sessionCoroutineScope.launch {
inputs.room.subscribeToSync() inputs.room.subscribeToSync()
} }
}, },

View File

@@ -109,7 +109,7 @@ class JoinedRoomLoadedFlowNodeTest {
messagesEntryPoint = messagesEntryPoint, messagesEntryPoint = messagesEntryPoint,
roomDetailsEntryPoint = roomDetailsEntryPoint, roomDetailsEntryPoint = roomDetailsEntryPoint,
appNavigationStateService = FakeAppNavigationStateService(), appNavigationStateService = FakeAppNavigationStateService(),
appCoroutineScope = this, sessionCoroutineScope = this,
roomComponentFactory = FakeRoomComponentFactory(), roomComponentFactory = FakeRoomComponentFactory(),
matrixClient = FakeMatrixClient(), matrixClient = FakeMatrixClient(),
activeRoomsHolder = activeRoomsHolder, activeRoomsHolder = activeRoomsHolder,

View File

@@ -13,6 +13,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.annotations.AppCoroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@@ -24,7 +25,8 @@ import javax.inject.Inject
*/ */
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultCacheCleaner @Inject constructor( class DefaultCacheCleaner @Inject constructor(
private val scope: CoroutineScope, @AppCoroutineScope
private val coroutineScope: CoroutineScope,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
@CacheDirectory private val cacheDir: File, @CacheDirectory private val cacheDir: File,
) : CacheCleaner { ) : CacheCleaner {
@@ -33,7 +35,7 @@ class DefaultCacheCleaner @Inject constructor(
} }
override fun clearCache() { override fun clearCache() {
scope.launch(dispatchers.io) { coroutineScope.launch(dispatchers.io) {
runCatchingExceptions { runCatchingExceptions {
SUBDIRS_TO_CLEANUP.forEach { SUBDIRS_TO_CLEANUP.forEach {
File(cacheDir.path, it).apply { File(cacheDir.path, it).apply {

View File

@@ -55,7 +55,7 @@ class DefaultCacheCleanerTest {
} }
private fun TestScope.aCacheCleaner() = DefaultCacheCleaner( private fun TestScope.aCacheCleaner() = DefaultCacheCleaner(
scope = this, coroutineScope = this,
dispatchers = this.testCoroutineDispatchers(true), dispatchers = this.testCoroutineDispatchers(true),
cacheDir = temporaryFolder.root, cacheDir = temporaryFolder.root,
) )

View File

@@ -16,6 +16,7 @@ import io.element.android.features.call.impl.di.CallBindings
import io.element.android.features.call.impl.notifications.CallNotificationData import io.element.android.features.call.impl.notifications.CallNotificationData
import io.element.android.features.call.impl.utils.ActiveCallManager import io.element.android.features.call.impl.utils.ActiveCallManager
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.di.annotations.AppCoroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -30,8 +31,8 @@ class DeclineCallBroadcastReceiver : BroadcastReceiver() {
@Inject @Inject
lateinit var activeCallManager: ActiveCallManager lateinit var activeCallManager: ActiveCallManager
@Inject @AppCoroutineScope
lateinit var appCoroutineScope: CoroutineScope @Inject lateinit var appCoroutineScope: CoroutineScope
override fun onReceive(context: Context, intent: Intent?) { override fun onReceive(context: Context, intent: Intent?) {
val notificationData = intent?.let { IntentCompat.getParcelableExtra(it, EXTRA_NOTIFICATION_DATA, CallNotificationData::class.java) } val notificationData = intent?.let { IntentCompat.getParcelableExtra(it, EXTRA_NOTIFICATION_DATA, CallNotificationData::class.java) }

View File

@@ -32,6 +32,7 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -64,6 +65,7 @@ class CallScreenPresenter @AssistedInject constructor(
private val languageTagProvider: LanguageTagProvider, private val languageTagProvider: LanguageTagProvider,
private val appForegroundStateService: AppForegroundStateService, private val appForegroundStateService: AppForegroundStateService,
private val activeRoomsHolder: ActiveRoomsHolder, private val activeRoomsHolder: ActiveRoomsHolder,
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
) : Presenter<CallScreenState> { ) : Presenter<CallScreenState> {
@AssistedFactory @AssistedFactory

View File

@@ -23,6 +23,7 @@ import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.preferences.api.store.AppPreferencesStore import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@@ -57,8 +58,8 @@ class IncomingCallActivity : AppCompatActivity() {
@Inject @Inject
lateinit var buildMeta: BuildMeta lateinit var buildMeta: BuildMeta
@Inject @AppCoroutineScope
lateinit var appCoroutineScope: CoroutineScope @Inject lateinit var appCoroutineScope: CoroutineScope
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View File

@@ -25,6 +25,7 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
@@ -87,6 +88,7 @@ interface ActiveCallManager {
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultActiveCallManager @Inject constructor( class DefaultActiveCallManager @Inject constructor(
@ApplicationContext context: Context, @ApplicationContext context: Context,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val onMissedCallNotificationHandler: OnMissedCallNotificationHandler, private val onMissedCallNotificationHandler: OnMissedCallNotificationHandler,
private val ringingCallNotificationCreator: RingingCallNotificationCreator, private val ringingCallNotificationCreator: RingingCallNotificationCreator,

View File

@@ -16,6 +16,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.permissions.api.PermissionStateProvider import io.element.android.libraries.permissions.api.PermissionStateProvider
import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsEvents
import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.api.PermissionsPresenter
@@ -27,6 +28,7 @@ import kotlinx.coroutines.launch
class NotificationsOptInPresenter @AssistedInject constructor( class NotificationsOptInPresenter @AssistedInject constructor(
permissionsPresenterFactory: PermissionsPresenter.Factory, permissionsPresenterFactory: PermissionsPresenter.Factory,
@Assisted private val callback: NotificationsOptInNode.Callback, @Assisted private val callback: NotificationsOptInNode.Callback,
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
private val permissionStateProvider: PermissionStateProvider, private val permissionStateProvider: PermissionStateProvider,
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider, private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,

View File

@@ -18,6 +18,7 @@ import io.element.android.features.knockrequests.impl.data.KnockRequestPresentab
import io.element.android.features.knockrequests.impl.data.KnockRequestsService import io.element.android.features.knockrequests.impl.data.KnockRequestsService
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.core.coroutine.mapState
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -28,7 +29,8 @@ private const val ACCEPT_ERROR_DISPLAY_DURATION = 1500L
class KnockRequestsBannerPresenter @Inject constructor( class KnockRequestsBannerPresenter @Inject constructor(
private val knockRequestsService: KnockRequestsService, private val knockRequestsService: KnockRequestsService,
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
) : Presenter<KnockRequestsBannerState> { ) : Presenter<KnockRequestsBannerState> {
@Composable @Composable
override fun present(): KnockRequestsBannerState { override fun present(): KnockRequestsBannerState {
@@ -52,13 +54,13 @@ class KnockRequestsBannerPresenter @Inject constructor(
fun handleEvents(event: KnockRequestsBannerEvents) { fun handleEvents(event: KnockRequestsBannerEvents) {
when (event) { when (event) {
is KnockRequestsBannerEvents.AcceptSingleRequest -> { is KnockRequestsBannerEvents.AcceptSingleRequest -> {
appCoroutineScope.acceptSingleKnockRequest( sessionCoroutineScope.acceptSingleKnockRequest(
knockRequests = knockRequests, knockRequests = knockRequests,
displayAcceptError = showAcceptError, displayAcceptError = showAcceptError,
) )
} }
is KnockRequestsBannerEvents.Dismiss -> { is KnockRequestsBannerEvents.Dismiss -> {
appCoroutineScope.launch { sessionCoroutineScope.launch {
knockRequestsService.markAllKnockRequestsAsSeen() knockRequestsService.markAllKnockRequestsAsSeen()
} }
} }

View File

@@ -238,6 +238,6 @@ private fun TestScope.createKnockRequestsBannerPresenter(
) )
return KnockRequestsBannerPresenter( return KnockRequestsBannerPresenter(
knockRequestsService = knockRequestsService, knockRequestsService = knockRequestsService,
appCoroutineScope = this, sessionCoroutineScope = this,
) )
} }

View File

@@ -17,6 +17,7 @@ import io.element.android.features.lockscreen.impl.pin.PinCodeManager
import io.element.android.features.lockscreen.impl.storage.LockScreenStore import io.element.android.features.lockscreen.impl.storage.LockScreenStore
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionListener
@@ -42,6 +43,7 @@ class DefaultLockScreenService @Inject constructor(
private val featureFlagService: FeatureFlagService, private val featureFlagService: FeatureFlagService,
private val lockScreenStore: LockScreenStore, private val lockScreenStore: LockScreenStore,
private val pinCodeManager: PinCodeManager, private val pinCodeManager: PinCodeManager,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val sessionObserver: SessionObserver, private val sessionObserver: SessionObserver,
private val appForegroundStateService: AppForegroundStateService, private val appForegroundStateService: AppForegroundStateService,

View File

@@ -31,6 +31,7 @@ import io.element.android.libraries.cryptography.api.SecretKeyRepository
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -47,6 +48,7 @@ class DefaultBiometricAuthenticatorManager @Inject constructor(
private val lockScreenConfig: LockScreenConfig, private val lockScreenConfig: LockScreenConfig,
private val encryptionDecryptionService: EncryptionDecryptionService, private val encryptionDecryptionService: EncryptionDecryptionService,
private val secretKeyRepository: SecretKeyRepository, private val secretKeyRepository: SecretKeyRepository,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
) : BiometricAuthenticatorManager { ) : BiometricAuthenticatorManager {
private val callbacks = CopyOnWriteArrayList<BiometricAuthenticator.Callback>() private val callbacks = CopyOnWriteArrayList<BiometricAuthenticator.Callback>()

View File

@@ -20,6 +20,7 @@ import io.element.android.features.lockscreen.impl.biometric.BiometricAuthentica
import io.element.android.features.lockscreen.impl.pin.PinCodeManager import io.element.android.features.lockscreen.impl.pin.PinCodeManager
import io.element.android.features.lockscreen.impl.storage.LockScreenStore import io.element.android.features.lockscreen.impl.storage.LockScreenStore
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.annotations.AppCoroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -29,6 +30,7 @@ class LockScreenSettingsPresenter @Inject constructor(
private val pinCodeManager: PinCodeManager, private val pinCodeManager: PinCodeManager,
private val lockScreenStore: LockScreenStore, private val lockScreenStore: LockScreenStore,
private val biometricAuthenticatorManager: BiometricAuthenticatorManager, private val biometricAuthenticatorManager: BiometricAuthenticatorManager,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
) : Presenter<LockScreenSettingsState> { ) : Presenter<LockScreenSettingsState> {
@Composable @Composable

View File

@@ -26,6 +26,7 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.di.annotations.AppCoroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -34,6 +35,7 @@ class PinUnlockPresenter @Inject constructor(
private val pinCodeManager: PinCodeManager, private val pinCodeManager: PinCodeManager,
private val biometricAuthenticatorManager: BiometricAuthenticatorManager, private val biometricAuthenticatorManager: BiometricAuthenticatorManager,
private val logoutUseCase: LogoutUseCase, private val logoutUseCase: LogoutUseCase,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val pinUnlockHelper: PinUnlockHelper, private val pinUnlockHelper: PinUnlockHelper,
) : Presenter<PinUnlockState> { ) : Presenter<PinUnlockState> {

View File

@@ -50,6 +50,7 @@ import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.analytics.toAnalyticsViewRoom import io.element.android.libraries.matrix.api.analytics.toAnalyticsViewRoom
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -69,7 +70,8 @@ import kotlinx.coroutines.launch
class MessagesNode @AssistedInject constructor( class MessagesNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val coroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val room: BaseRoom, private val room: BaseRoom,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
messageComposerPresenterFactory: MessageComposerPresenter.Factory, messageComposerPresenterFactory: MessageComposerPresenter.Factory,
@@ -115,7 +117,7 @@ class MessagesNode @AssistedInject constructor(
super.onBuilt() super.onBuilt()
lifecycle.subscribe( lifecycle.subscribe(
onCreate = { onCreate = {
coroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) } sessionCoroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) }
}, },
onDestroy = { onDestroy = {
mediaPlayer.close() mediaPlayer.close()

View File

@@ -16,6 +16,7 @@ import dagger.assisted.AssistedInject
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.timeline.TimelineProvider import io.element.android.libraries.matrix.api.timeline.TimelineProvider
@@ -28,7 +29,8 @@ import kotlinx.coroutines.launch
class ForwardMessagesPresenter @AssistedInject constructor( class ForwardMessagesPresenter @AssistedInject constructor(
@Assisted eventId: String, @Assisted eventId: String,
@Assisted private val timelineProvider: TimelineProvider, @Assisted private val timelineProvider: TimelineProvider,
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
) : Presenter<ForwardMessagesState> { ) : Presenter<ForwardMessagesState> {
private val eventId: EventId = EventId(eventId) private val eventId: EventId = EventId(eventId)
@@ -40,7 +42,7 @@ class ForwardMessagesPresenter @AssistedInject constructor(
private val forwardingActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized) private val forwardingActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized)
fun onRoomSelected(roomIds: List<RoomId>) { fun onRoomSelected(roomIds: List<RoomId>) {
appCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState) sessionCoroutineScope.forwardEvent(eventId, roomIds.toPersistentList(), forwardingActionState)
} }
@Composable @Composable

View File

@@ -44,6 +44,7 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@@ -98,7 +99,8 @@ import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes
class MessageComposerPresenter @AssistedInject constructor( class MessageComposerPresenter @AssistedInject constructor(
@Assisted private val navigator: MessagesNavigator, @Assisted private val navigator: MessagesNavigator,
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val room: JoinedRoom, private val room: JoinedRoom,
private val mediaPickerProvider: PickerProvider, private val mediaPickerProvider: PickerProvider,
private val featureFlagService: FeatureFlagService, private val featureFlagService: FeatureFlagService,
@@ -200,7 +202,7 @@ class MessageComposerPresenter @AssistedInject constructor(
DisposableEffect(Unit) { DisposableEffect(Unit) {
// Declare that the user is not typing anymore when the composer is disposed // Declare that the user is not typing anymore when the composer is disposed
onDispose { onDispose {
appCoroutineScope.launch { sessionCoroutineScope.launch {
if (sendTypingNotifications) { if (sendTypingNotifications) {
room.typingNotice(false) room.typingNotice(false)
} }
@@ -236,12 +238,12 @@ class MessageComposerPresenter @AssistedInject constructor(
} }
} }
is MessageComposerEvents.SendMessage -> { is MessageComposerEvents.SendMessage -> {
appCoroutineScope.sendMessage( sessionCoroutineScope.sendMessage(
markdownTextEditorState = markdownTextEditorState, markdownTextEditorState = markdownTextEditorState,
richTextEditorState = richTextEditorState, richTextEditorState = richTextEditorState,
) )
} }
is MessageComposerEvents.SendUri -> appCoroutineScope.sendAttachment( is MessageComposerEvents.SendUri -> sessionCoroutineScope.sendAttachment(
attachment = Attachment.Media( attachment = Attachment.Media(
localMedia = localMediaFactory.createFromUri( localMedia = localMediaFactory.createFromUri(
uri = event.uri, uri = event.uri,
@@ -338,7 +340,7 @@ class MessageComposerPresenter @AssistedInject constructor(
} }
MessageComposerEvents.SaveDraft -> { MessageComposerEvents.SaveDraft -> {
val draft = createDraftFromState(markdownTextEditorState, richTextEditorState) val draft = createDraftFromState(markdownTextEditorState, richTextEditorState)
appCoroutineScope.updateDraft(draft, isVolatile = false) sessionCoroutineScope.updateDraft(draft, isVolatile = false)
} }
} }
} }

View File

@@ -38,6 +38,7 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin import io.element.android.libraries.matrix.api.room.powerlevels.canPinUnpin
import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther
@@ -67,7 +68,8 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
private val linkPresenter: Presenter<LinkState>, private val linkPresenter: Presenter<LinkState>,
private val snackbarDispatcher: SnackbarDispatcher, private val snackbarDispatcher: SnackbarDispatcher,
@Assisted private val actionListPresenter: Presenter<ActionListState>, @Assisted private val actionListPresenter: Presenter<ActionListState>,
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
) : Presenter<PinnedMessagesListState> { ) : Presenter<PinnedMessagesListState> {
@AssistedFactory @AssistedFactory
@@ -123,7 +125,7 @@ class PinnedMessagesListPresenter @AssistedInject constructor(
fun handleEvents(event: PinnedMessagesListEvents) { fun handleEvents(event: PinnedMessagesListEvents) {
when (event) { when (event) {
is PinnedMessagesListEvents.HandleAction -> appCoroutineScope.handleTimelineAction(event.action, event.event) is PinnedMessagesListEvents.HandleAction -> sessionCoroutineScope.handleTimelineAction(event.action, event.event)
} }
} }

View File

@@ -38,6 +38,7 @@ import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.UniqueId import io.element.android.libraries.matrix.api.core.UniqueId
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -66,7 +67,8 @@ class TimelinePresenter @AssistedInject constructor(
timelineItemsFactoryCreator: TimelineItemsFactory.Creator, timelineItemsFactoryCreator: TimelineItemsFactory.Creator,
private val room: JoinedRoom, private val room: JoinedRoom,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
private val appScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
@Assisted private val navigator: MessagesNavigator, @Assisted private val navigator: MessagesNavigator,
private val redactedVoiceMessageManager: RedactedVoiceMessageManager, private val redactedVoiceMessageManager: RedactedVoiceMessageManager,
private val sendPollResponseAction: SendPollResponseAction, private val sendPollResponseAction: SendPollResponseAction,
@@ -135,7 +137,7 @@ class TimelinePresenter @AssistedInject constructor(
newEventState.value = NewEventState.None newEventState.value = NewEventState.None
} }
Timber.d("## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex}") Timber.d("## sendReadReceiptIfNeeded firstVisibleIndex: ${event.firstIndex}")
appScope.sendReadReceiptIfNeeded( sessionCoroutineScope.sendReadReceiptIfNeeded(
firstVisibleIndex = event.firstIndex, firstVisibleIndex = event.firstIndex,
timelineItems = timelineItems, timelineItems = timelineItems,
lastReadReceiptId = lastReadReceiptId, lastReadReceiptId = lastReadReceiptId,
@@ -145,13 +147,13 @@ class TimelinePresenter @AssistedInject constructor(
newEventState.value = NewEventState.None newEventState.value = NewEventState.None
} }
} }
is TimelineEvents.SelectPollAnswer -> appScope.launch { is TimelineEvents.SelectPollAnswer -> sessionCoroutineScope.launch {
sendPollResponseAction.execute( sendPollResponseAction.execute(
pollStartId = event.pollStartId, pollStartId = event.pollStartId,
answerId = event.answerId answerId = event.answerId
) )
} }
is TimelineEvents.EndPoll -> appScope.launch { is TimelineEvents.EndPoll -> sessionCoroutineScope.launch {
endPollAction.execute( endPollAction.execute(
pollStartId = event.pollStartId, pollStartId = event.pollStartId,
) )

View File

@@ -8,6 +8,7 @@
package io.element.android.features.messages.impl.voicemessages.composer package io.element.android.features.messages.impl.voicemessages.composer
import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.mediaplayer.api.MediaPlayer
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -26,11 +27,12 @@ import javax.inject.Inject
* A media player for the voice message composer. * A media player for the voice message composer.
* *
* @param mediaPlayer The [MediaPlayer] to use. * @param mediaPlayer The [MediaPlayer] to use.
* @param coroutineScope * @param sessionCoroutineScope
*/ */
class VoiceMessageComposerPlayer @Inject constructor( class VoiceMessageComposerPlayer @Inject constructor(
private val mediaPlayer: MediaPlayer, private val mediaPlayer: MediaPlayer,
private val coroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
) { ) {
companion object { companion object {
const val MIME_TYPE = MimeTypes.Ogg const val MIME_TYPE = MimeTypes.Ogg
@@ -116,7 +118,7 @@ class VoiceMessageComposerPlayer @Inject constructor(
seekJob?.cancelAndJoin() seekJob?.cancelAndJoin()
seekingTo.value = position seekingTo.value = position
seekJob = coroutineScope.launch { seekJob = sessionCoroutineScope.launch {
val mediaState = mediaPlayer.ensureMediaReady(mediaPath) val mediaState = mediaPlayer.ensureMediaReady(mediaPath)
val duration = mediaState.duration ?: return@launch val duration = mediaState.duration ?: return@launch
val positionMs = (duration * position).toLong() val positionMs = (duration * position).toLong()

View File

@@ -22,6 +22,7 @@ import androidx.lifecycle.Lifecycle
import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.messages.api.MessageComposerContext import io.element.android.features.messages.api.MessageComposerContext
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.api.MediaSender
import io.element.android.libraries.permissions.api.PermissionsEvents import io.element.android.libraries.permissions.api.PermissionsEvents
import io.element.android.libraries.permissions.api.PermissionsPresenter import io.element.android.libraries.permissions.api.PermissionsPresenter
@@ -44,7 +45,8 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
class VoiceMessageComposerPresenter @Inject constructor( class VoiceMessageComposerPresenter @Inject constructor(
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val voiceRecorder: VoiceRecorder, private val voiceRecorder: VoiceRecorder,
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val mediaSender: MediaSender, private val mediaSender: MediaSender,
@@ -74,11 +76,11 @@ class VoiceMessageComposerPresenter @Inject constructor(
val onLifecycleEvent = { event: Lifecycle.Event -> val onLifecycleEvent = { event: Lifecycle.Event ->
when (event) { when (event) {
Lifecycle.Event.ON_PAUSE -> { Lifecycle.Event.ON_PAUSE -> {
appCoroutineScope.finishRecording() sessionCoroutineScope.finishRecording()
player.pause() player.pause()
} }
Lifecycle.Event.ON_DESTROY -> { Lifecycle.Event.ON_DESTROY -> {
appCoroutineScope.cancelRecording() sessionCoroutineScope.cancelRecording()
} }
else -> {} else -> {}
} }
@@ -145,7 +147,7 @@ class VoiceMessageComposerPresenter @Inject constructor(
isSending = true isSending = true
player.pause() player.pause()
analyticsService.captureComposerEvent() analyticsService.captureComposerEvent()
appCoroutineScope.launch { sessionCoroutineScope.launch {
val result = sendMessage( val result = sendMessage(
file = finishedState.file, file = finishedState.file,
mimeType = finishedState.mimeType, mimeType = finishedState.mimeType,

View File

@@ -97,6 +97,6 @@ class ForwardMessagesPresenterTest {
) = ForwardMessagesPresenter( ) = ForwardMessagesPresenter(
eventId = eventId.value, eventId = eventId.value,
timelineProvider = LiveTimelineProvider(fakeRoom), timelineProvider = LiveTimelineProvider(fakeRoom),
appCoroutineScope = this, sessionCoroutineScope = this,
) )
} }

View File

@@ -1539,7 +1539,7 @@ class MessageComposerPresenterTest {
draftService: ComposerDraftService = FakeComposerDraftService(), draftService: ComposerDraftService = FakeComposerDraftService(),
) = MessageComposerPresenter( ) = MessageComposerPresenter(
navigator = navigator, navigator = navigator,
appCoroutineScope = this, sessionCoroutineScope = this,
room = room, room = room,
mediaPickerProvider = pickerProvider, mediaPickerProvider = pickerProvider,
featureFlagService = featureFlagService, featureFlagService = featureFlagService,

View File

@@ -337,7 +337,7 @@ class PinnedMessagesListPresenterTest {
actionListPresenter = { anActionListState() }, actionListPresenter = { anActionListState() },
linkPresenter = { aLinkState() }, linkPresenter = { aLinkState() },
analyticsService = analyticsService, analyticsService = analyticsService,
appCoroutineScope = this, sessionCoroutineScope = this,
) )
} }
} }

View File

@@ -727,7 +727,7 @@ class TimelinePresenterTest {
timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(), timelineItemsFactoryCreator = aTimelineItemsFactoryCreator(),
room = room, room = room,
dispatchers = testCoroutineDispatchers(), dispatchers = testCoroutineDispatchers(),
appScope = this, sessionCoroutineScope = this,
navigator = messagesNavigator, navigator = messagesNavigator,
redactedVoiceMessageManager = redactedVoiceMessageManager, redactedVoiceMessageManager = redactedVoiceMessageManager,
endPollAction = endPollAction, endPollAction = endPollAction,

View File

@@ -19,6 +19,7 @@ import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
@@ -38,6 +39,7 @@ import javax.inject.Inject
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
class DefaultNetworkMonitor @Inject constructor( class DefaultNetworkMonitor @Inject constructor(
@ApplicationContext context: Context, @ApplicationContext context: Context,
@AppCoroutineScope
appCoroutineScope: CoroutineScope, appCoroutineScope: CoroutineScope,
) : NetworkMonitor { ) : NetworkMonitor {
private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java) private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java)

View File

@@ -23,6 +23,7 @@ import io.element.android.features.poll.impl.history.model.PollHistoryFilter
import io.element.android.features.poll.impl.history.model.PollHistoryItems import io.element.android.features.poll.impl.history.model.PollHistoryItems
import io.element.android.features.poll.impl.history.model.PollHistoryItemsFactory import io.element.android.features.poll.impl.history.model.PollHistoryItemsFactory
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -31,7 +32,8 @@ import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class PollHistoryPresenter @Inject constructor( class PollHistoryPresenter @Inject constructor(
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val sendPollResponseAction: SendPollResponseAction, private val sendPollResponseAction: SendPollResponseAction,
private val endPollAction: EndPollAction, private val endPollAction: EndPollAction,
private val pollHistoryItemFactory: PollHistoryItemsFactory, private val pollHistoryItemFactory: PollHistoryItemsFactory,
@@ -64,10 +66,10 @@ class PollHistoryPresenter @Inject constructor(
is PollHistoryEvents.LoadMore -> { is PollHistoryEvents.LoadMore -> {
coroutineScope.loadMore(timeline) coroutineScope.loadMore(timeline)
} }
is PollHistoryEvents.SelectPollAnswer -> appCoroutineScope.launch { is PollHistoryEvents.SelectPollAnswer -> sessionCoroutineScope.launch {
sendPollResponseAction.execute(pollStartId = event.pollStartId, answerId = event.answerId) sendPollResponseAction.execute(pollStartId = event.pollStartId, answerId = event.answerId)
} }
is PollHistoryEvents.EndPoll -> appCoroutineScope.launch { is PollHistoryEvents.EndPoll -> sessionCoroutineScope.launch {
endPollAction.execute(pollStartId = event.pollStartId) endPollAction.execute(pollStartId = event.pollStartId)
} }
is PollHistoryEvents.SelectFilter -> { is PollHistoryEvents.SelectFilter -> {

View File

@@ -163,7 +163,7 @@ class PollHistoryPresenterTest {
), ),
): PollHistoryPresenter { ): PollHistoryPresenter {
return PollHistoryPresenter( return PollHistoryPresenter(
appCoroutineScope = this, sessionCoroutineScope = this,
sendPollResponseAction = sendPollResponseAction, sendPollResponseAction = sendPollResponseAction,
endPollAction = endPollAction, endPollAction = endPollAction,
pollHistoryItemFactory = pollHistoryItemFactory, pollHistoryItemFactory = pollHistoryItemFactory,

View File

@@ -22,6 +22,7 @@ import io.element.android.features.rageshake.impl.crash.CrashDataStore
import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder
import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.annotations.AppCoroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -30,6 +31,7 @@ class BugReportPresenter @Inject constructor(
private val bugReporter: BugReporter, private val bugReporter: BugReporter,
private val crashDataStore: CrashDataStore, private val crashDataStore: CrashDataStore,
private val screenshotHolder: ScreenshotHolder, private val screenshotHolder: ScreenshotHolder,
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
) : Presenter<BugReportState> { ) : Presenter<BugReportState> {
private class BugReporterUploadListener( private class BugReporterUploadListener(

View File

@@ -12,6 +12,7 @@ import io.element.android.libraries.androidutils.diff.DiffCacheUpdater
import io.element.android.libraries.androidutils.diff.MutableListDiffCache import io.element.android.libraries.androidutils.diff.MutableListDiffCache
import io.element.android.libraries.androidutils.system.DateTimeObserver import io.element.android.libraries.androidutils.system.DateTimeObserver
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomListService
@@ -36,7 +37,8 @@ class RoomListDataSource @Inject constructor(
private val roomListRoomSummaryFactory: RoomListRoomSummaryFactory, private val roomListRoomSummaryFactory: RoomListRoomSummaryFactory,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
private val notificationSettingsService: NotificationSettingsService, private val notificationSettingsService: NotificationSettingsService,
private val appScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val dateTimeObserver: DateTimeObserver, private val dateTimeObserver: DateTimeObserver,
) { ) {
init { init {
@@ -77,7 +79,7 @@ class RoomListDataSource @Inject constructor(
.onEach { .onEach {
roomListService.allRooms.rebuildSummaries() roomListService.allRooms.rebuildSummaries()
} }
.launchIn(appScope) .launchIn(sessionCoroutineScope)
} }
private fun observeDateTimeChanges() { private fun observeDateTimeChanges() {
@@ -88,7 +90,7 @@ class RoomListDataSource @Inject constructor(
is DateTimeObserver.Event.DateChanged -> rebuildAllRoomSummaries() is DateTimeObserver.Event.DateChanged -> rebuildAllRoomSummaries()
} }
} }
.launchIn(appScope) .launchIn(sessionCoroutineScope)
} }
private suspend fun replaceWith(roomSummaries: List<RoomSummary>) = withContext(coroutineDispatchers.computation) { private suspend fun replaceWith(roomSummaries: List<RoomSummary>) = withContext(coroutineDispatchers.computation) {

View File

@@ -701,7 +701,7 @@ class RoomListPresenterTest {
), ),
coroutineDispatchers = testCoroutineDispatchers(), coroutineDispatchers = testCoroutineDispatchers(),
notificationSettingsService = client.notificationSettingsService(), notificationSettingsService = client.notificationSettingsService(),
appScope = backgroundScope, sessionCoroutineScope = backgroundScope,
dateTimeObserver = FakeDateTimeObserver(), dateTimeObserver = FakeDateTimeObserver(),
), ),
featureFlagService = featureFlagService, featureFlagService = featureFlagService,

View File

@@ -100,7 +100,7 @@ class RoomListDataSourceTest {
roomListRoomSummaryFactory = roomListRoomSummaryFactory, roomListRoomSummaryFactory = roomListRoomSummaryFactory,
coroutineDispatchers = testCoroutineDispatchers(), coroutineDispatchers = testCoroutineDispatchers(),
notificationSettingsService = notificationSettingsService, notificationSettingsService = notificationSettingsService,
appScope = backgroundScope, sessionCoroutineScope = backgroundScope,
dateTimeObserver = dateTimeObserver, dateTimeObserver = dateTimeObserver,
) )
} }

View File

@@ -36,6 +36,7 @@ import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityOidcResetHandle
import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle import io.element.android.libraries.matrix.api.encryption.IdentityPasswordResetHandle
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -50,7 +51,8 @@ class ResetIdentityFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val resetIdentityFlowManager: ResetIdentityFlowManager, private val resetIdentityFlowManager: ResetIdentityFlowManager,
private val coroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
) : BaseFlowNode<ResetIdentityFlowNode.NavTarget>( ) : BaseFlowNode<ResetIdentityFlowNode.NavTarget>(
backstack = BackStack(initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap), backstack = BackStack(initialElement = NavTarget.Root, savedStateMap = buildContext.savedStateMap),
buildContext = buildContext, buildContext = buildContext,
@@ -79,7 +81,7 @@ class ResetIdentityFlowNode @AssistedInject constructor(
override fun onStart(owner: LifecycleOwner) { override fun onStart(owner: LifecycleOwner) {
// If the custom tab / Web browser was opened, we need to cancel the reset job // If the custom tab / Web browser was opened, we need to cancel the reset job
// when we come back to the node if the reset wasn't successful // when we come back to the node if the reset wasn't successful
coroutineScope.launch { sessionCoroutineScope.launch {
cancelResetJob() cancelResetJob()
resetIdentityFlowManager.whenResetIsDone { resetIdentityFlowManager.whenResetIsDone {
@@ -90,7 +92,7 @@ class ResetIdentityFlowNode @AssistedInject constructor(
override fun onDestroy(owner: LifecycleOwner) { override fun onDestroy(owner: LifecycleOwner) {
// Make sure we cancel the reset job when the node is destroyed, just in case // Make sure we cancel the reset job when the node is destroyed, just in case
coroutineScope.launch { cancelResetJob() } sessionCoroutineScope.launch { cancelResetJob() }
} }
}) })
} }
@@ -100,7 +102,7 @@ class ResetIdentityFlowNode @AssistedInject constructor(
is NavTarget.Root -> { is NavTarget.Root -> {
val callback = object : ResetIdentityRootNode.Callback { val callback = object : ResetIdentityRootNode.Callback {
override fun onContinue() { override fun onContinue() {
coroutineScope.startReset() sessionCoroutineScope.startReset()
} }
} }
createNode<ResetIdentityRootNode>(buildContext, listOf(callback)) createNode<ResetIdentityRootNode>(buildContext, listOf(callback))
@@ -157,7 +159,7 @@ class ResetIdentityFlowNode @AssistedInject constructor(
if (startResetState.isLoading()) { if (startResetState.isLoading()) {
ProgressDialog( ProgressDialog(
properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true), properties = DialogProperties(dismissOnBackPress = true, dismissOnClickOutside = true),
onDismissRequest = { coroutineScope.launch { cancelResetJob() } } onDismissRequest = { sessionCoroutineScope.launch { cancelResetJob() } }
) )
} }

View File

@@ -18,6 +18,7 @@ import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
@@ -31,7 +32,8 @@ import kotlin.coroutines.cancellation.CancellationException
class SharePresenter @AssistedInject constructor( class SharePresenter @AssistedInject constructor(
@Assisted private val intent: Intent, @Assisted private val intent: Intent,
private val appCoroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val shareIntentHandler: ShareIntentHandler, private val shareIntentHandler: ShareIntentHandler,
private val matrixClient: MatrixClient, private val matrixClient: MatrixClient,
private val mediaPreProcessor: MediaPreProcessor, private val mediaPreProcessor: MediaPreProcessor,
@@ -46,7 +48,7 @@ class SharePresenter @AssistedInject constructor(
private val shareActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized) private val shareActionState: MutableState<AsyncAction<List<RoomId>>> = mutableStateOf(AsyncAction.Uninitialized)
fun onRoomSelected(roomIds: List<RoomId>) { fun onRoomSelected(roomIds: List<RoomId>) {
appCoroutineScope.share(intent, roomIds) sessionCoroutineScope.share(intent, roomIds)
} }
@Composable @Composable

View File

@@ -169,7 +169,7 @@ class SharePresenterTest {
): SharePresenter { ): SharePresenter {
return SharePresenter( return SharePresenter(
intent = intent, intent = intent,
appCoroutineScope = this, sessionCoroutineScope = this,
shareIntentHandler = shareIntentHandler, shareIntentHandler = shareIntentHandler,
matrixClient = matrixClient, matrixClient = matrixClient,
mediaPreProcessor = mediaPreProcessor, mediaPreProcessor = mediaPreProcessor,

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.di.annotations
import javax.inject.Qualifier
/**
* Qualifies a [CoroutineScope] object which represents the base coroutine scope to use for the application.
*/
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Qualifier
annotation class AppCoroutineScope

View File

@@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.impl.analytics.UtdTracker import io.element.android.libraries.matrix.impl.analytics.UtdTracker
@@ -40,6 +41,7 @@ import javax.inject.Inject
class RustMatrixClientFactory @Inject constructor( class RustMatrixClientFactory @Inject constructor(
private val baseDirectory: File, private val baseDirectory: File,
@CacheDirectory private val cacheDirectory: File, @CacheDirectory private val cacheDirectory: File,
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
private val sessionStore: SessionStore, private val sessionStore: SessionStore,

View File

@@ -15,6 +15,7 @@ import io.element.android.libraries.audio.api.AudioFocus
import io.element.android.libraries.audio.api.AudioFocusRequester import io.element.android.libraries.audio.api.AudioFocusRequester
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.mediaplayer.api.MediaPlayer
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
@@ -37,7 +38,8 @@ import kotlin.time.Duration.Companion.seconds
@SingleIn(RoomScope::class) @SingleIn(RoomScope::class)
class DefaultMediaPlayer @Inject constructor( class DefaultMediaPlayer @Inject constructor(
private val player: SimplePlayer, private val player: SimplePlayer,
private val coroutineScope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val audioFocus: AudioFocus, private val audioFocus: AudioFocus,
) : MediaPlayer { ) : MediaPlayer {
private val listener = object : SimplePlayer.Listener { private val listener = object : SimplePlayer.Listener {
@@ -50,7 +52,7 @@ class DefaultMediaPlayer @Inject constructor(
) )
} }
if (isPlaying) { if (isPlaying) {
job = coroutineScope.launch { updateCurrentPosition() } job = sessionCoroutineScope.launch { updateCurrentPosition() }
} else { } else {
audioFocus.releaseAudioFocus() audioFocus.releaseAudioFocus()
job?.cancel() job?.cancel()

View File

@@ -423,7 +423,7 @@ class DefaultMediaPlayerTest {
audioFocus: AudioFocus = FakeAudioFocus(), audioFocus: AudioFocus = FakeAudioFocus(),
): DefaultMediaPlayer = DefaultMediaPlayer( ): DefaultMediaPlayer = DefaultMediaPlayer(
player = simplePlayer, player = simplePlayer,
coroutineScope = backgroundScope, sessionCoroutineScope = backgroundScope,
audioFocus = audioFocus, audioFocus = audioFocus,
) )
} }

View File

@@ -14,6 +14,7 @@ import io.element.android.libraries.core.data.tryOrNull
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
@@ -48,6 +49,7 @@ class DefaultNotificationDrawerManager @Inject constructor(
private val notificationManager: NotificationManagerCompat, private val notificationManager: NotificationManagerCompat,
private val notificationRenderer: NotificationRenderer, private val notificationRenderer: NotificationRenderer,
private val appNavigationStateService: AppNavigationStateService, private val appNavigationStateService: AppNavigationStateService,
@AppCoroutineScope
coroutineScope: CoroutineScope, coroutineScope: CoroutineScope,
private val matrixClientProvider: MatrixClientProvider, private val matrixClientProvider: MatrixClientProvider,
private val imageLoaderHolder: ImageLoaderHolder, private val imageLoaderHolder: ImageLoaderHolder,

View File

@@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications
import android.content.Intent import android.content.Intent
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
@@ -36,6 +37,7 @@ import javax.inject.Inject
private val loggerTag = LoggerTag("NotificationBroadcastReceiverHandler", LoggerTag.NotificationLoggerTag) private val loggerTag = LoggerTag("NotificationBroadcastReceiverHandler", LoggerTag.NotificationLoggerTag)
class NotificationBroadcastReceiverHandler @Inject constructor( class NotificationBroadcastReceiverHandler @Inject constructor(
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
private val matrixClientProvider: MatrixClientProvider, private val matrixClientProvider: MatrixClientProvider,
private val sessionPreferencesStore: SessionPreferencesStoreFactory, private val sessionPreferencesStore: SessionPreferencesStoreFactory,

View File

@@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.notifications
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
@@ -34,6 +35,7 @@ import kotlin.time.Duration.Companion.milliseconds
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
class NotificationResolverQueue @Inject constructor( class NotificationResolverQueue @Inject constructor(
private val notifiableEventResolver: NotifiableEventResolver, private val notifiableEventResolver: NotifiableEventResolver,
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
) { ) {
companion object { companion object {

View File

@@ -14,6 +14,7 @@ import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.push.impl.history.PushHistoryService import io.element.android.libraries.push.impl.history.PushHistoryService
import io.element.android.libraries.push.impl.history.onDiagnosticPush import io.element.android.libraries.push.impl.history.onDiagnosticPush
@@ -58,6 +59,7 @@ class DefaultPushHandler @Inject constructor(
private val notificationChannels: NotificationChannels, private val notificationChannels: NotificationChannels,
private val pushHistoryService: PushHistoryService, private val pushHistoryService: PushHistoryService,
private val resolverQueue: NotificationResolverQueue, private val resolverQueue: NotificationResolverQueue,
@AppCoroutineScope
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
) : PushHandler { ) : PushHandler {
init { init {

View File

@@ -9,6 +9,7 @@ package io.element.android.libraries.push.impl.push
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager import io.element.android.libraries.push.impl.notifications.DefaultNotificationDrawerManager
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
@@ -23,6 +24,7 @@ interface OnNotifiableEventReceived {
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultOnNotifiableEventReceived @Inject constructor( class DefaultOnNotifiableEventReceived @Inject constructor(
private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager, private val defaultNotificationDrawerManager: DefaultNotificationDrawerManager,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val syncOnNotifiableEvent: SyncOnNotifiableEvent, private val syncOnNotifiableEvent: SyncOnNotifiableEvent,
) : OnNotifiableEventReceived { ) : OnNotifiableEventReceived {

View File

@@ -17,6 +17,7 @@ import androidx.core.text.inSpans
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.push.impl.notifications.ActiveNotificationsProvider import io.element.android.libraries.push.impl.notifications.ActiveNotificationsProvider
import io.element.android.libraries.push.impl.notifications.NotificationDisplayer import io.element.android.libraries.push.impl.notifications.NotificationDisplayer
import io.element.android.libraries.push.impl.notifications.factories.DefaultNotificationCreator import io.element.android.libraries.push.impl.notifications.factories.DefaultNotificationCreator
@@ -36,6 +37,7 @@ interface OnRedactedEventReceived {
class DefaultOnRedactedEventReceived @Inject constructor( class DefaultOnRedactedEventReceived @Inject constructor(
private val activeNotificationsProvider: ActiveNotificationsProvider, private val activeNotificationsProvider: ActiveNotificationsProvider,
private val notificationDisplayer: NotificationDisplayer, private val notificationDisplayer: NotificationDisplayer,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,

View File

@@ -11,6 +11,7 @@ import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage import com.google.firebase.messaging.RemoteMessage
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.pushproviders.api.PushHandler import io.element.android.libraries.pushproviders.api.PushHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -23,6 +24,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
@Inject lateinit var firebaseNewTokenHandler: FirebaseNewTokenHandler @Inject lateinit var firebaseNewTokenHandler: FirebaseNewTokenHandler
@Inject lateinit var pushParser: FirebasePushParser @Inject lateinit var pushParser: FirebasePushParser
@Inject lateinit var pushHandler: PushHandler @Inject lateinit var pushHandler: PushHandler
@AppCoroutineScope
@Inject lateinit var coroutineScope: CoroutineScope @Inject lateinit var coroutineScope: CoroutineScope
override fun onCreate() { override fun onCreate() {

View File

@@ -11,6 +11,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.pushproviders.api.PushHandler import io.element.android.libraries.pushproviders.api.PushHandler
import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler
import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult
@@ -34,6 +35,7 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
@Inject lateinit var unifiedPushGatewayUrlResolver: UnifiedPushGatewayUrlResolver @Inject lateinit var unifiedPushGatewayUrlResolver: UnifiedPushGatewayUrlResolver
@Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler @Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler
@Inject lateinit var endpointRegistrationHandler: EndpointRegistrationHandler @Inject lateinit var endpointRegistrationHandler: EndpointRegistrationHandler
@AppCoroutineScope
@Inject lateinit var coroutineScope: CoroutineScope @Inject lateinit var coroutineScope: CoroutineScope
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {

View File

@@ -11,6 +11,7 @@ import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionListener
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
@@ -28,6 +29,7 @@ import javax.inject.Inject
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultSessionObserver @Inject constructor( class DefaultSessionObserver @Inject constructor(
private val sessionStore: SessionStore, private val sessionStore: SessionStore,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
) : SessionObserver { ) : SessionObserver {

View File

@@ -10,6 +10,7 @@ package io.element.android.libraries.voiceplayer.impl
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.media.MediaSource import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.voiceplayer.api.VoiceMessagePresenterFactory import io.element.android.libraries.voiceplayer.api.VoiceMessagePresenterFactory
@@ -22,7 +23,8 @@ import kotlin.time.Duration
@ContributesBinding(RoomScope::class) @ContributesBinding(RoomScope::class)
class DefaultVoiceMessagePresenterFactory @Inject constructor( class DefaultVoiceMessagePresenterFactory @Inject constructor(
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val scope: CoroutineScope, @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val voiceMessagePlayerFactory: VoiceMessagePlayer.Factory, private val voiceMessagePlayerFactory: VoiceMessagePlayer.Factory,
) : VoiceMessagePresenterFactory { ) : VoiceMessagePresenterFactory {
override fun createVoiceMessagePresenter( override fun createVoiceMessagePresenter(
@@ -41,7 +43,7 @@ class DefaultVoiceMessagePresenterFactory @Inject constructor(
return VoiceMessagePresenter( return VoiceMessagePresenter(
analyticsService = analyticsService, analyticsService = analyticsService,
scope = scope, sessionCoroutineScope = sessionCoroutineScope,
player = player, player = player,
eventId = eventId, eventId = eventId,
duration = duration, duration = duration,

View File

@@ -31,7 +31,7 @@ import kotlin.time.Duration.Companion.milliseconds
class VoiceMessagePresenter( class VoiceMessagePresenter(
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val scope: CoroutineScope, private val sessionCoroutineScope: CoroutineScope,
private val player: VoiceMessagePlayer, private val player: VoiceMessagePlayer,
private val eventId: EventId?, private val eventId: EventId?,
private val duration: Duration, private val duration: Duration,
@@ -92,7 +92,7 @@ class VoiceMessagePresenter(
} else if (playerState.isReady) { } else if (playerState.isReady) {
player.play() player.play()
} else { } else {
scope.launch { sessionCoroutineScope.launch {
play.runUpdatingState( play.runUpdatingState(
errorTransform = { errorTransform = {
analyticsService.trackError( analyticsService.trackError(

View File

@@ -236,7 +236,7 @@ fun TestScope.createVoiceMessagePresenter(
mediaSource: MediaSource = MediaSource(contentUri), mediaSource: MediaSource = MediaSource(contentUri),
) = VoiceMessagePresenter( ) = VoiceMessagePresenter(
analyticsService = analyticsService, analyticsService = analyticsService,
scope = this, sessionCoroutineScope = this,
player = DefaultVoiceMessagePlayer( player = DefaultVoiceMessagePlayer(
mediaPlayer = mediaPlayer, mediaPlayer = mediaPlayer,
voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo }, voiceMessageMediaRepoFactory = { _, _, _ -> voiceMessageMediaRepo },

View File

@@ -15,6 +15,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.voicerecorder.api.VoiceRecorder import io.element.android.libraries.voicerecorder.api.VoiceRecorder
import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import io.element.android.libraries.voicerecorder.api.VoiceRecorderState
import io.element.android.libraries.voicerecorder.impl.audio.Audio import io.element.android.libraries.voicerecorder.impl.audio.Audio
@@ -51,10 +52,11 @@ class DefaultVoiceRecorder @Inject constructor(
private val config: AudioConfig, private val config: AudioConfig,
private val fileConfig: VoiceFileConfig, private val fileConfig: VoiceFileConfig,
private val audioLevelCalculator: AudioLevelCalculator, private val audioLevelCalculator: AudioLevelCalculator,
appCoroutineScope: CoroutineScope, @SessionCoroutineScope
sessionCoroutineScope: CoroutineScope,
) : VoiceRecorder { ) : VoiceRecorder {
private val voiceCoroutineScope by lazy { private val voiceCoroutineScope by lazy {
appCoroutineScope.childScope(dispatchers.io, "VoiceRecorder-${UUID.randomUUID()}") sessionCoroutineScope.childScope(dispatchers.io, "VoiceRecorder-${UUID.randomUUID()}")
} }
private var outputFile: File? = null private var outputFile: File? = null

View File

@@ -141,7 +141,7 @@ class DefaultVoiceRecorderTest {
fileConfig = fileConfig, fileConfig = fileConfig,
fileManager = FakeVoiceFileManager(fakeFileSystem, fileConfig, FILE_ID), fileManager = FakeVoiceFileManager(fakeFileSystem, fileConfig, FILE_ID),
audioLevelCalculator = FakeAudioLevelCalculator(), audioLevelCalculator = FakeAudioLevelCalculator(),
appCoroutineScope = backgroundScope, sessionCoroutineScope = backgroundScope,
) )
} }

View File

@@ -14,6 +14,7 @@ import im.vector.app.features.analytics.plan.SuperProperties
import im.vector.app.features.analytics.plan.UserProperties import im.vector.app.features.analytics.plan.UserProperties
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.sessionstorage.api.observer.SessionListener import io.element.android.libraries.sessionstorage.api.observer.SessionListener
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@@ -34,6 +35,7 @@ class DefaultAnalyticsService @Inject constructor(
private val analyticsProviders: Set<@JvmSuppressWildcards AnalyticsProvider>, private val analyticsProviders: Set<@JvmSuppressWildcards AnalyticsProvider>,
private val analyticsStore: AnalyticsStore, private val analyticsStore: AnalyticsStore,
// private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory, // private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory,
@AppCoroutineScope
private val coroutineScope: CoroutineScope, private val coroutineScope: CoroutineScope,
private val sessionObserver: SessionObserver, private val sessionObserver: SessionObserver,
) : AnalyticsService, SessionListener { ) : AnalyticsService, SessionListener {

View File

@@ -11,6 +11,7 @@ import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.SingleIn import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.SpaceId import io.element.android.libraries.matrix.api.core.SpaceId
@@ -36,7 +37,8 @@ private val loggerTag = LoggerTag("Navigation")
@SingleIn(AppScope::class) @SingleIn(AppScope::class)
class DefaultAppNavigationStateService @Inject constructor( class DefaultAppNavigationStateService @Inject constructor(
private val appForegroundStateService: AppForegroundStateService, private val appForegroundStateService: AppForegroundStateService,
private val coroutineScope: CoroutineScope, @AppCoroutineScope
coroutineScope: CoroutineScope,
) : AppNavigationStateService { ) : AppNavigationStateService {
private val state = MutableStateFlow( private val state = MutableStateFlow(
AppNavigationState( AppNavigationState(