Remove FeatureFlag.NotificationSettings and FeatureFlag.PinUnlock

This commit is contained in:
Benoit Marty 2025-08-12 14:30:30 +02:00 committed by Benoit Marty
parent 92990b27f3
commit 0eadf275e1
12 changed files with 16 additions and 77 deletions

View file

@ -18,8 +18,6 @@ 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.di.annotations.AppCoroutineScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
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
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
import io.element.android.services.appnavstate.api.AppForegroundStateService import io.element.android.services.appnavstate.api.AppForegroundStateService
@ -29,7 +27,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -40,7 +37,6 @@ import kotlin.time.Duration
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultLockScreenService @Inject constructor( class DefaultLockScreenService @Inject constructor(
private val lockScreenConfig: LockScreenConfig, private val lockScreenConfig: LockScreenConfig,
private val featureFlagService: FeatureFlagService,
private val lockScreenStore: LockScreenStore, private val lockScreenStore: LockScreenStore,
private val pinCodeManager: PinCodeManager, private val pinCodeManager: PinCodeManager,
@AppCoroutineScope @AppCoroutineScope
@ -108,12 +104,7 @@ class DefaultLockScreenService @Inject constructor(
} }
override fun isPinSetup(): Flow<Boolean> { override fun isPinSetup(): Flow<Boolean> {
return combine( return pinCodeManager.hasPinCode()
featureFlagService.isFeatureEnabledFlow(FeatureFlags.PinUnlock),
pinCodeManager.hasPinCode()
) { isEnabled, hasPinCode ->
isEnabled && hasPinCode
}
} }
override fun isSetupRequired(): Flow<Boolean> { override fun isSetupRequired(): Flow<Boolean> {

View file

@ -23,8 +23,6 @@ import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
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.collectSnackbarMessageAsState 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.indicator.api.IndicatorService
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
@ -42,7 +40,6 @@ class PreferencesRootPresenter @Inject constructor(
private val analyticsService: AnalyticsService, private val analyticsService: AnalyticsService,
private val versionFormatter: VersionFormatter, private val versionFormatter: VersionFormatter,
private val snackbarDispatcher: SnackbarDispatcher, private val snackbarDispatcher: SnackbarDispatcher,
private val featureFlagService: FeatureFlagService,
private val indicatorService: IndicatorService, private val indicatorService: IndicatorService,
private val directLogoutPresenter: Presenter<DirectLogoutState>, private val directLogoutPresenter: Presenter<DirectLogoutState>,
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider, private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
@ -60,15 +57,6 @@ class PreferencesRootPresenter @Inject constructor(
val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState()
val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() } val hasAnalyticsProviders = remember { analyticsService.getAvailableAnalyticsProviders().isNotEmpty() }
val showNotificationSettings = remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
showNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings)
}
val showLockScreenSettings = remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
showLockScreenSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.PinUnlock)
}
// We should display the 'complete verification' option if the current session can be verified // We should display the 'complete verification' option if the current session can be verified
val canVerifyUserSession by sessionVerificationService.needsSessionVerification.collectAsState(false) val canVerifyUserSession by sessionVerificationService.needsSessionVerification.collectAsState(false)
@ -122,8 +110,6 @@ class PreferencesRootPresenter @Inject constructor(
canReportBug = canReportBug, canReportBug = canReportBug,
showDeveloperSettings = showDeveloperSettings, showDeveloperSettings = showDeveloperSettings,
canDeactivateAccount = canDeactivateAccount, canDeactivateAccount = canDeactivateAccount,
showNotificationSettings = showNotificationSettings.value,
showLockScreenSettings = showLockScreenSettings.value,
showBlockedUsersItem = showBlockedUsersItem, showBlockedUsersItem = showBlockedUsersItem,
directLogoutState = directLogoutState, directLogoutState = directLogoutState,
snackbarMessage = snackbarMessage, snackbarMessage = snackbarMessage,

View file

@ -24,8 +24,6 @@ data class PreferencesRootState(
val showAnalyticsSettings: Boolean, val showAnalyticsSettings: Boolean,
val showDeveloperSettings: Boolean, val showDeveloperSettings: Boolean,
val canDeactivateAccount: Boolean, val canDeactivateAccount: Boolean,
val showLockScreenSettings: Boolean,
val showNotificationSettings: Boolean,
val showBlockedUsersItem: Boolean, val showBlockedUsersItem: Boolean,
val directLogoutState: DirectLogoutState, val directLogoutState: DirectLogoutState,
val snackbarMessage: SnackbarMessage?, val snackbarMessage: SnackbarMessage?,

View file

@ -27,8 +27,6 @@ fun aPreferencesRootState(
showAnalyticsSettings = true, showAnalyticsSettings = true,
canReportBug = true, canReportBug = true,
showDeveloperSettings = true, showDeveloperSettings = true,
showNotificationSettings = true,
showLockScreenSettings = true,
showBlockedUsersItem = true, showBlockedUsersItem = true,
canDeactivateAccount = true, canDeactivateAccount = true,
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete), snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),

View file

@ -121,20 +121,16 @@ private fun ColumnScope.ManageAppSection(
onOpenLockScreenSettings: () -> Unit, onOpenLockScreenSettings: () -> Unit,
onSecureBackupClick: () -> Unit, onSecureBackupClick: () -> Unit,
) { ) {
if (state.showNotificationSettings) { ListItem(
ListItem( headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_title)) },
headlineContent = { Text(stringResource(id = R.string.screen_notification_settings_title)) }, leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())),
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Notifications())), onClick = onOpenNotificationSettings,
onClick = onOpenNotificationSettings, )
) ListItem(
} headlineContent = { Text(stringResource(id = CommonStrings.common_screen_lock)) },
if (state.showLockScreenSettings) { leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
ListItem( onClick = onOpenLockScreenSettings,
headlineContent = { Text(stringResource(id = CommonStrings.common_screen_lock)) }, )
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Lock())),
onClick = onOpenLockScreenSettings,
)
}
if (state.showSecureBackup) { if (state.showSecureBackup) {
ListItem( ListItem(
headlineContent = { Text(stringResource(id = CommonStrings.common_encryption)) }, headlineContent = { Text(stringResource(id = CommonStrings.common_encryption)) },
@ -143,9 +139,7 @@ private fun ColumnScope.ManageAppSection(
onClick = onSecureBackupClick, onClick = onSecureBackupClick,
) )
} }
if (state.showNotificationSettings || state.showLockScreenSettings || state.showSecureBackup) { HorizontalDivider()
HorizontalDivider()
}
} }
@Composable @Composable

View file

@ -16,7 +16,6 @@ import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsP
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
import io.element.android.libraries.core.meta.BuildType import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
import io.element.android.libraries.indicator.api.IndicatorService import io.element.android.libraries.indicator.api.IndicatorService
import io.element.android.libraries.indicator.test.FakeIndicatorService import io.element.android.libraries.indicator.test.FakeIndicatorService
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
@ -77,8 +76,6 @@ class PreferencesRootPresenterTest {
assertThat(loadedState.devicesManagementUrl).isNull() assertThat(loadedState.devicesManagementUrl).isNull()
assertThat(loadedState.showAnalyticsSettings).isFalse() assertThat(loadedState.showAnalyticsSettings).isFalse()
assertThat(loadedState.showDeveloperSettings).isTrue() assertThat(loadedState.showDeveloperSettings).isTrue()
assertThat(loadedState.showLockScreenSettings).isTrue()
assertThat(loadedState.showNotificationSettings).isTrue()
assertThat(loadedState.canDeactivateAccount).isTrue() assertThat(loadedState.canDeactivateAccount).isTrue()
assertThat(loadedState.canReportBug).isTrue() assertThat(loadedState.canReportBug).isTrue()
assertThat(loadedState.directLogoutState).isEqualTo(aDirectLogoutState()) assertThat(loadedState.directLogoutState).isEqualTo(aDirectLogoutState())
@ -194,7 +191,6 @@ class PreferencesRootPresenterTest {
analyticsService = FakeAnalyticsService(), analyticsService = FakeAnalyticsService(),
versionFormatter = FakeVersionFormatter(), versionFormatter = FakeVersionFormatter(),
snackbarDispatcher = SnackbarDispatcher(), snackbarDispatcher = SnackbarDispatcher(),
featureFlagService = FakeFeatureFlagService(),
indicatorService = indicatorService, indicatorService = indicatorService,
directLogoutPresenter = { aDirectLogoutState() }, directLogoutPresenter = { aDirectLogoutState() },
showDeveloperSettingsProvider = showDeveloperSettingsProvider, showDeveloperSettingsProvider = showDeveloperSettingsProvider,

View file

@ -12,7 +12,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -77,7 +76,6 @@ class RoomDetailsPresenter @Inject constructor(
override fun present(): RoomDetailsState { override fun present(): RoomDetailsState {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val leaveRoomState = leaveRoomPresenter.present() val leaveRoomState = leaveRoomPresenter.present()
val canShowNotificationSettings = remember { mutableStateOf(false) }
val roomInfo by room.roomInfoFlow.collectAsState() val roomInfo by room.roomInfoFlow.collectAsState()
val isUserAdmin = room.isOwnUserAdmin() val isUserAdmin = room.isOwnUserAdmin()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
@ -96,11 +94,8 @@ class RoomDetailsPresenter @Inject constructor(
}.collectAsState(false) }.collectAsState(false)
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
canShowNotificationSettings.value = featureFlagService.isFeatureEnabled(FeatureFlags.NotificationSettings) room.updateRoomNotificationSettings()
if (canShowNotificationSettings.value) { observeNotificationSettings()
room.updateRoomNotificationSettings()
observeNotificationSettings()
}
} }
val membersState by room.membersStateFlow.collectAsState() val membersState by room.membersStateFlow.collectAsState()
@ -197,7 +192,6 @@ class RoomDetailsPresenter @Inject constructor(
isEncrypted = isEncrypted, isEncrypted = isEncrypted,
canInvite = canInvite, canInvite = canInvite,
canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room, canEdit = (canEditAvatar || canEditName || canEditTopic) && roomType == RoomDetailsType.Room,
canShowNotificationSettings = canShowNotificationSettings.value,
roomCallState = roomCallState, roomCallState = roomCallState,
roomType = roomType, roomType = roomType,
roomMemberDetailsState = roomMemberDetailsState, roomMemberDetailsState = roomMemberDetailsState,

View file

@ -32,7 +32,6 @@ data class RoomDetailsState(
val roomMemberDetailsState: UserProfileState?, val roomMemberDetailsState: UserProfileState?,
val canEdit: Boolean, val canEdit: Boolean,
val canInvite: Boolean, val canInvite: Boolean,
val canShowNotificationSettings: Boolean,
val roomCallState: RoomCallState, val roomCallState: RoomCallState,
val leaveRoomState: LeaveRoomState, val leaveRoomState: LeaveRoomState,
val roomNotificationSettings: RoomNotificationSettings?, val roomNotificationSettings: RoomNotificationSettings?,

View file

@ -100,7 +100,6 @@ fun aRoomDetailsState(
isEncrypted: Boolean = true, isEncrypted: Boolean = true,
canInvite: Boolean = false, canInvite: Boolean = false,
canEdit: Boolean = false, canEdit: Boolean = false,
canShowNotificationSettings: Boolean = true,
roomCallState: RoomCallState = aStandByCallState(), roomCallState: RoomCallState = aStandByCallState(),
roomType: RoomDetailsType = RoomDetailsType.Room, roomType: RoomDetailsType = RoomDetailsType.Room,
roomMemberDetailsState: UserProfileState? = null, roomMemberDetailsState: UserProfileState? = null,
@ -132,7 +131,6 @@ fun aRoomDetailsState(
isEncrypted = isEncrypted, isEncrypted = isEncrypted,
canInvite = canInvite, canInvite = canInvite,
canEdit = canEdit, canEdit = canEdit,
canShowNotificationSettings = canShowNotificationSettings,
roomCallState = roomCallState, roomCallState = roomCallState,
roomType = roomType, roomType = roomType,
roomMemberDetailsState = roomMemberDetailsState, roomMemberDetailsState = roomMemberDetailsState,

View file

@ -185,7 +185,7 @@ fun RoomDetailsView(
} }
PreferenceCategory { PreferenceCategory {
if (state.canShowNotificationSettings && state.roomNotificationSettings != null) { if (state.roomNotificationSettings != null) {
NotificationItem( NotificationItem(
isDefaultMode = state.roomNotificationSettings.isDefault, isDefaultMode = state.roomNotificationSettings.isDefault,
openRoomNotificationSettings = openRoomNotificationSettings openRoomNotificationSettings = openRoomNotificationSettings
@ -337,8 +337,7 @@ private fun MainActionsSection(
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.SpaceEvenly, horizontalArrangement = Arrangement.SpaceEvenly,
) { ) {
val roomNotificationSettings = state.roomNotificationSettings state.roomNotificationSettings?.let { roomNotificationSettings ->
if (state.canShowNotificationSettings && roomNotificationSettings != null) {
if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) { if (roomNotificationSettings.mode == RoomNotificationMode.MUTE) {
MainActionButton( MainActionButton(
title = stringResource(CommonStrings.common_unmute), title = stringResource(CommonStrings.common_unmute),

View file

@ -79,7 +79,6 @@ class RoomDetailsPresenterTest {
analyticsService: AnalyticsService = FakeAnalyticsService(), analyticsService: AnalyticsService = FakeAnalyticsService(),
featureFlagService: FeatureFlagService = FakeFeatureFlagService( featureFlagService: FeatureFlagService = FakeFeatureFlagService(
mapOf( mapOf(
FeatureFlags.NotificationSettings.key to true,
FeatureFlags.Knock.key to false, FeatureFlags.Knock.key to false,
) )
), ),

View file

@ -21,12 +21,6 @@ enum class FeatureFlags(
override val defaultValue: (BuildMeta) -> Boolean, override val defaultValue: (BuildMeta) -> Boolean,
override val isFinished: Boolean, override val isFinished: Boolean,
) : Feature { ) : Feature {
NotificationSettings(
key = "feature.notificationsettings",
title = "Show notification settings",
defaultValue = { true },
isFinished = true,
),
VoiceMessages( VoiceMessages(
key = "feature.voicemessages", key = "feature.voicemessages",
title = "Voice messages", title = "Voice messages",
@ -34,13 +28,6 @@ enum class FeatureFlags(
defaultValue = { true }, defaultValue = { true },
isFinished = true, isFinished = true,
), ),
PinUnlock(
key = "feature.pinunlock",
title = "Pin unlock",
description = "Allow user to lock/unlock the app with a pin code or biometrics",
defaultValue = { true },
isFinished = true,
),
MarkAsUnread( MarkAsUnread(
key = "feature.markAsUnread", key = "feature.markAsUnread",
title = "Mark as unread", title = "Mark as unread",