Browse Source

Secure backup: create a feature flag (disabled)

pull/1716/head
Benoit Marty 11 months ago committed by Benoit Marty
parent
commit
5013ff061e
  1. 2
      features/logout/impl/build.gradle.kts
  2. 16
      features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt
  3. 3
      features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt
  4. 6
      features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt
  5. 1
      features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt
  6. 2
      features/roomlist/impl/build.gradle.kts
  7. 10
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
  8. 4
      features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt
  9. 6
      libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt
  10. 1
      libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt
  11. 2
      libraries/indicator/impl/build.gradle.kts
  12. 8
      libraries/indicator/impl/src/main/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorService.kt
  13. 9
      samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt

2
features/logout/impl/build.gradle.kts

@ -34,6 +34,7 @@ dependencies { @@ -34,6 +34,7 @@ dependencies {
implementation(projects.libraries.androidutils)
implementation(projects.libraries.core)
implementation(projects.libraries.architecture)
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.testtags)
@ -48,5 +49,6 @@ dependencies { @@ -48,5 +49,6 @@ dependencies {
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.tests.testutils)
}

16
features/logout/impl/src/main/kotlin/io/element/android/features/logout/impl/LogoutPresenter.kt

@ -28,16 +28,21 @@ import androidx.compose.runtime.setValue @@ -28,16 +28,21 @@ import androidx.compose.runtime.setValue
import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
class LogoutPresenter @Inject constructor(
private val matrixClient: MatrixClient,
private val encryptionService: EncryptionService,
private val featureFlagService: FeatureFlagService,
) : Presenter<LogoutState> {
@Composable
@ -47,8 +52,15 @@ class LogoutPresenter @Inject constructor( @@ -47,8 +52,15 @@ class LogoutPresenter @Inject constructor(
mutableStateOf(Async.Uninitialized)
}
val backupUploadState: BackupUploadState by remember {
encryptionService.waitForBackupUploadSteadyState()
val secureStorageFlag by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SecureStorage)
.collectAsState(initial = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.SecureStorage) })
val backupUploadState: BackupUploadState by remember(secureStorageFlag) {
if (secureStorageFlag) {
encryptionService.waitForBackupUploadSteadyState()
} else {
flowOf(BackupUploadState.Done)
}
}
.collectAsState(initial = BackupUploadState.Unknown)

3
features/logout/impl/src/test/kotlin/io/element/android/features/logout/impl/LogoutPresenterTest.kt

@ -21,6 +21,8 @@ import app.cash.molecule.moleculeFlow @@ -21,6 +21,8 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.architecture.Async
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.encryption.BackupUploadState
import io.element.android.libraries.matrix.api.encryption.EncryptionService
@ -205,6 +207,7 @@ class LogoutPresenterTest { @@ -205,6 +207,7 @@ class LogoutPresenterTest {
): LogoutPresenter = LogoutPresenter(
matrixClient = matrixClient,
encryptionService = encryptionService,
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)),
)
}

6
features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenter.kt

@ -39,6 +39,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS @@ -39,6 +39,7 @@ import io.element.android.libraries.matrix.api.verification.SessionVerificationS
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
class PreferencesRootPresenter @Inject constructor(
@ -78,6 +79,9 @@ class PreferencesRootPresenter @Inject constructor( @@ -78,6 +79,9 @@ class PreferencesRootPresenter @Inject constructor(
val showSecureBackupIndicator by indicatorService.showSettingChatBackupIndicator()
val secureStorageFlag by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SecureStorage)
.collectAsState(initial = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.SecureStorage) })
val accountManagementUrl: MutableState<String?> = remember {
mutableStateOf(null)
}
@ -94,7 +98,7 @@ class PreferencesRootPresenter @Inject constructor( @@ -94,7 +98,7 @@ class PreferencesRootPresenter @Inject constructor(
myUser = matrixUser.value,
version = versionFormatter.get(),
showCompleteVerification = showCompleteVerification,
showSecureBackup = !showCompleteVerification,
showSecureBackup = !showCompleteVerification && secureStorageFlag,
showSecureBackupBadge = showSecureBackupIndicator,
accountManagementUrl = accountManagementUrl.value,
devicesManagementUrl = devicesManagementUrl.value,

1
features/preferences/impl/src/test/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootPresenterTest.kt

@ -56,6 +56,7 @@ class PreferencesRootPresenterTest { @@ -56,6 +56,7 @@ class PreferencesRootPresenterTest {
DefaultIndicatorService(
sessionVerificationService = sessionVerificationService,
encryptionService = FakeEncryptionService(),
featureFlagService = FakeFeatureFlagService(),
),
)
moleculeFlow(RecompositionMode.Immediate) {

2
features/roomlist/impl/build.gradle.kts

@ -41,6 +41,7 @@ dependencies { @@ -41,6 +41,7 @@ dependencies {
implementation(projects.libraries.core)
implementation(projects.libraries.androidutils)
implementation(projects.libraries.architecture)
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.matrix.api)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.designsystem)
@ -64,6 +65,7 @@ dependencies { @@ -64,6 +65,7 @@ dependencies {
testImplementation(libs.test.turbine)
testImplementation(libs.test.robolectric)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.dateformatter.test)
testImplementation(projects.libraries.eventformatter.test)
testImplementation(projects.libraries.indicator.impl)

10
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt

@ -35,6 +35,8 @@ import io.element.android.features.roomlist.impl.datasource.RoomListDataSource @@ -35,6 +35,8 @@ import io.element.android.features.roomlist.impl.datasource.RoomListDataSource
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.designsystem.utils.snackbar.collectSnackbarMessageAsState
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.indicator.api.IndicatorService
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.EncryptionService
@ -44,6 +46,7 @@ import io.element.android.libraries.matrix.api.user.getCurrentUser @@ -44,6 +46,7 @@ import io.element.android.libraries.matrix.api.user.getCurrentUser
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
private const val EXTENDED_RANGE_SIZE = 40
@ -57,6 +60,7 @@ class RoomListPresenter @Inject constructor( @@ -57,6 +60,7 @@ class RoomListPresenter @Inject constructor(
private val leaveRoomPresenter: LeaveRoomPresenter,
private val roomListDataSource: RoomListDataSource,
private val encryptionService: EncryptionService,
private val featureFlagService: FeatureFlagService,
private val indicatorService: IndicatorService,
) : Presenter<RoomListState> {
@ -84,10 +88,14 @@ class RoomListPresenter @Inject constructor( @@ -84,10 +88,14 @@ class RoomListPresenter @Inject constructor(
derivedStateOf { canVerifySession && !verificationPromptDismissed }
}
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
val secureStorageFlag by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SecureStorage)
.collectAsState(initial = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.SecureStorage) })
var recoveryKeyPromptDismissed by rememberSaveable { mutableStateOf(false) }
val displayRecoveryKeyPrompt by remember {
derivedStateOf {
recoveryState == RecoveryState.INCOMPLETE && !recoveryKeyPromptDismissed
secureStorageFlag &&
recoveryState == RecoveryState.INCOMPLETE &&
!recoveryKeyPromptDismissed
}
}

4
features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt

@ -37,6 +37,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize @@ -37,6 +37,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.eventformatter.test.FakeRoomLastMessageFormatter
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.indicator.impl.DefaultIndicatorService
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.BackupState
@ -417,9 +419,11 @@ class RoomListPresenterTests { @@ -417,9 +419,11 @@ class RoomListPresenterTests {
appScope = coroutineScope
),
encryptionService = encryptionService,
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)),
indicatorService = DefaultIndicatorService(
sessionVerificationService = sessionVerificationService,
encryptionService = encryptionService,
featureFlagService = FakeFeatureFlagService(mapOf(FeatureFlags.SecureStorage.key to true)),
),
)
}

6
libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt

@ -66,5 +66,11 @@ enum class FeatureFlags( @@ -66,5 +66,11 @@ enum class FeatureFlags(
title = "Mentions",
description = "Type `@` to get mention suggestions and insert them",
defaultValue = false,
),
SecureStorage(
key = "feature.securestorage",
title = "Chat backup",
description = "Allow access to backup and restore chat history settings",
defaultValue = false,
)
}

1
libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt

@ -41,6 +41,7 @@ class StaticFeatureFlagProvider @Inject constructor() : @@ -41,6 +41,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
FeatureFlags.PinUnlock -> true
FeatureFlags.InRoomCalls -> true
FeatureFlags.Mentions -> false
FeatureFlags.SecureStorage -> false
}
} else {
false

2
libraries/indicator/impl/build.gradle.kts

@ -31,6 +31,7 @@ dependencies { @@ -31,6 +31,7 @@ dependencies {
anvil(projects.anvilcodegen)
implementation(libs.dagger)
implementation(projects.libraries.di)
implementation(projects.libraries.featureflag.api)
implementation(projects.libraries.matrix.api)
implementation(projects.anvilannotations)
@ -38,6 +39,7 @@ dependencies { @@ -38,6 +39,7 @@ dependencies {
api(projects.libraries.indicator.api)
testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.matrix.test)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)

8
libraries/indicator/impl/src/main/kotlin/io/element/android/libraries/indicator/impl/DefaultIndicatorService.kt

@ -24,17 +24,21 @@ import androidx.compose.runtime.getValue @@ -24,17 +24,21 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.indicator.api.IndicatorService
import io.element.android.libraries.matrix.api.encryption.BackupState
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
@ContributesBinding(SessionScope::class)
class DefaultIndicatorService @Inject constructor(
private val sessionVerificationService: SessionVerificationService,
private val encryptionService: EncryptionService,
private val featureFlagService: FeatureFlagService,
) : IndicatorService {
@Composable
@ -51,6 +55,8 @@ class DefaultIndicatorService @Inject constructor( @@ -51,6 +55,8 @@ class DefaultIndicatorService @Inject constructor(
@Composable
override fun showSettingChatBackupIndicator(): State<Boolean> {
val secureStorageFlag by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SecureStorage)
.collectAsState(initial = runBlocking { featureFlagService.isFeatureEnabled(FeatureFlags.SecureStorage) })
val backupState by encryptionService.backupStateStateFlow.collectAsState()
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
@ -63,7 +69,7 @@ class DefaultIndicatorService @Inject constructor( @@ -63,7 +69,7 @@ class DefaultIndicatorService @Inject constructor(
RecoveryState.DISABLED,
RecoveryState.INCOMPLETE,
)
showForBackup || showForRecovery
secureStorageFlag && (showForBackup || showForRecovery)
}
}
}

9
samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt

@ -36,6 +36,8 @@ import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFo @@ -36,6 +36,8 @@ import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFo
import io.element.android.libraries.eventformatter.impl.ProfileChangeContentFormatter
import io.element.android.libraries.eventformatter.impl.RoomMembershipContentFormatter
import io.element.android.libraries.eventformatter.impl.StateContentFormatter
import io.element.android.libraries.featureflag.impl.DefaultFeatureFlagService
import io.element.android.libraries.featureflag.impl.StaticFeatureFlagProvider
import io.element.android.libraries.indicator.impl.DefaultIndicatorService
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
@ -62,6 +64,9 @@ class RoomListScreen( @@ -62,6 +64,9 @@ class RoomListScreen(
private val sessionVerificationService = matrixClient.sessionVerificationService()
private val encryptionService = matrixClient.encryptionService()
private val stringProvider = AndroidStringProvider(context.resources)
private val featureFlagService = DefaultFeatureFlagService(
providers = setOf(StaticFeatureFlagProvider())
)
private val presenter = RoomListPresenter(
client = matrixClient,
sessionVerificationService = sessionVerificationService,
@ -87,7 +92,9 @@ class RoomListScreen( @@ -87,7 +92,9 @@ class RoomListScreen(
indicatorService = DefaultIndicatorService(
sessionVerificationService = sessionVerificationService,
encryptionService = encryptionService,
)
featureFlagService = featureFlagService,
),
featureFlagService = featureFlagService,
)
@Composable

Loading…
Cancel
Save