diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index af464ebc7b..9d34c03a93 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -34,6 +34,13 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), + SyncOnPush( + key = "feature.syncOnPush", + title = "Sync on push", + description = "Subscribe to room sync when a push is received", + defaultValue = { true }, + isFinished = false, + ), OnlySignedDeviceIsolationMode( key = "feature.onlySignedDeviceIsolationMode", title = "Exclude insecure devices when sending/receiving messages", diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEvent.kt index bb4e03e4d0..57c004ce23 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEvent.kt @@ -8,6 +8,8 @@ package io.element.android.libraries.push.impl.push import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.featureflag.api.FeatureFlagService +import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClientProvider import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.services.appnavstate.api.AppForegroundStateService @@ -19,10 +21,15 @@ import kotlin.time.Duration.Companion.seconds class SyncOnNotifiableEvent @Inject constructor( private val matrixClientProvider: MatrixClientProvider, + private val featureFlagService: FeatureFlagService, private val appForegroundStateService: AppForegroundStateService, private val dispatchers: CoroutineDispatchers, ) { suspend operator fun invoke(notifiableEvents: List) = withContext(dispatchers.io) { + if (!featureFlagService.isFeatureEnabled(FeatureFlags.SyncOnPush)) { + return@withContext + } + try { val eventsBySession = notifiableEvents.groupBy { it.sessionId } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt index f7d4fc0fda..e1c4b33acf 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/push/SyncOnNotifiableEventTest.kt @@ -9,6 +9,8 @@ package io.element.android.libraries.push.impl.push import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -21,6 +23,7 @@ import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableCallEvent import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.services.appnavstate.test.FakeAppForegroundStateService +import io.element.android.tests.testutils.lambda.assert import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -57,13 +60,24 @@ class SyncOnNotifiableEventTest { private val notifiableEvent = aNotifiableMessageEvent() private val incomingCallNotifiableEvent = aNotifiableCallEvent() + @Test + fun `when feature flag is disabled, nothing happens`() = runTest { + val sut = createSyncOnNotifiableEvent(client = client, isSyncOnPushEnabled = false) + + sut(listOf(notifiableEvent)) + + assert(startSyncLambda).isNeverCalled() + assert(stopSyncLambda).isNeverCalled() + assert(subscribeToSyncLambda).isNeverCalled() + } + @OptIn(ExperimentalCoroutinesApi::class) @Test fun `when feature flag is enabled, a ringing call waits until the room is in 'in-call' state`() = runTest { val appForegroundStateService = FakeAppForegroundStateService( initialForegroundValue = false, ) - val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService) + val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService, isSyncOnPushEnabled = true) val unlocked = AtomicBoolean(false) launch { @@ -83,7 +97,7 @@ class SyncOnNotifiableEventTest { val appForegroundStateService = FakeAppForegroundStateService( initialForegroundValue = false, ) - val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService) + val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService, isSyncOnPushEnabled = true) val unlocked = AtomicBoolean(false) launch { @@ -102,7 +116,7 @@ class SyncOnNotifiableEventTest { val appForegroundStateService = FakeAppForegroundStateService( initialForegroundValue = false, ) - val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService) + val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService, isSyncOnPushEnabled = true) appForegroundStateService.isSyncingNotificationEvent.test { syncService.emitSyncState(SyncState.Running) @@ -124,7 +138,7 @@ class SyncOnNotifiableEventTest { val appForegroundStateService = FakeAppForegroundStateService( initialForegroundValue = false, ) - val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService) + val sut = createSyncOnNotifiableEvent(client = client, appForegroundStateService = appForegroundStateService, isSyncOnPushEnabled = true) appForegroundStateService.isSyncingNotificationEvent.test { launch { sut(listOf(notifiableEvent)) } @@ -143,13 +157,20 @@ class SyncOnNotifiableEventTest { private fun TestScope.createSyncOnNotifiableEvent( client: MatrixClient = FakeMatrixClient(), + isSyncOnPushEnabled: Boolean = true, appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService( initialForegroundValue = true, ), ): SyncOnNotifiableEvent { + val featureFlagService = FakeFeatureFlagService( + initialState = mapOf( + FeatureFlags.SyncOnPush.key to isSyncOnPushEnabled + ) + ) val matrixClientProvider = FakeMatrixClientProvider { Result.success(client) } return SyncOnNotifiableEvent( matrixClientProvider = matrixClientProvider, + featureFlagService = featureFlagService, appForegroundStateService = appForegroundStateService, dispatchers = testCoroutineDispatchers(), )