Add hidden developer settings in release builds too (#3020)

* Add hidden developer settings to release builds

* Add changelog
This commit is contained in:
Jorge Martin Espinosa 2024-06-13 13:08:56 +02:00 committed by GitHub
parent c9e0db26d8
commit cd045027dc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 185 additions and 26 deletions

View file

@ -36,6 +36,8 @@ import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
import io.element.android.libraries.featureflag.api.Feature
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
@ -52,6 +54,7 @@ class DeveloperSettingsPresenter @Inject constructor(
private val clearCacheUseCase: ClearCacheUseCase,
private val rageshakePresenter: RageshakePreferencesPresenter,
private val appPreferencesStore: AppPreferencesStore,
private val buildMeta: BuildMeta,
) : Presenter<DeveloperSettingsState> {
@Composable
override fun present(): DeveloperSettingsState {
@ -76,6 +79,14 @@ class DeveloperSettingsPresenter @Inject constructor(
LaunchedEffect(Unit) {
FeatureFlags.entries
.filter { it.isFinished.not() }
.run {
// Never display room directory search in release builds for Play Store
if (buildMeta.flavorDescription == "GooglePlay" && buildMeta.buildType == BuildType.RELEASE) {
filterNot { it.key == FeatureFlags.RoomDirectorySearch.key }
} else {
this
}
}
.forEach { feature ->
features[feature.key] = feature
enabledFeatures[feature.key] = featureFlagService.isFeatureEnabled(feature)

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.preferences.impl.root
sealed interface PreferencesRootEvents {
data object OnVersionInfoClick : PreferencesRootEvents
}

View file

@ -25,8 +25,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import io.element.android.features.logout.api.direct.DirectLogoutPresenter
import io.element.android.features.preferences.impl.utils.ShowDeveloperSettingsProvider
import io.element.android.libraries.architecture.Presenter
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.collectSnackbarMessageAsState
import io.element.android.libraries.featureflag.api.FeatureFlagService
@ -46,12 +46,12 @@ class PreferencesRootPresenter @Inject constructor(
private val matrixClient: MatrixClient,
private val sessionVerificationService: SessionVerificationService,
private val analyticsService: AnalyticsService,
private val buildType: BuildType,
private val versionFormatter: VersionFormatter,
private val snackbarDispatcher: SnackbarDispatcher,
private val featureFlagService: FeatureFlagService,
private val indicatorService: IndicatorService,
private val directLogoutPresenter: DirectLogoutPresenter,
private val showDeveloperSettingsProvider: ShowDeveloperSettingsProvider,
) : Presenter<PreferencesRootState> {
@Composable
override fun present(): PreferencesRootState {
@ -97,7 +97,16 @@ class PreferencesRootPresenter @Inject constructor(
initAccountManagementUrl(accountManagementUrl, devicesManagementUrl)
}
val showDeveloperSettings = buildType != BuildType.RELEASE
val showDeveloperSettings by showDeveloperSettingsProvider.showDeveloperSettings.collectAsState()
fun handleEvent(event: PreferencesRootEvents) {
when (event) {
is PreferencesRootEvents.OnVersionInfoClick -> {
showDeveloperSettingsProvider.unlockDeveloperSettings()
}
}
}
return PreferencesRootState(
myUser = matrixUser.value,
version = versionFormatter.get(),
@ -113,6 +122,7 @@ class PreferencesRootPresenter @Inject constructor(
showBlockedUsersItem = showBlockedUsersItem,
directLogoutState = directLogoutState,
snackbarMessage = snackbarMessage,
eventSink = ::handleEvent,
)
}

View file

@ -35,4 +35,5 @@ data class PreferencesRootState(
val showBlockedUsersItem: Boolean,
val directLogoutState: DirectLogoutState,
val snackbarMessage: SnackbarMessage?,
val eventSink: (PreferencesRootEvents) -> Unit,
)

View file

@ -23,6 +23,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
fun aPreferencesRootState(
myUser: MatrixUser,
eventSink: (PreferencesRootEvents) -> Unit = { _ -> },
) = PreferencesRootState(
myUser = myUser,
version = "Version 1.1 (1)",
@ -38,4 +39,5 @@ fun aPreferencesRootState(
showBlockedUsersItem = true,
snackbarMessage = SnackbarMessage(CommonStrings.common_verification_complete),
directLogoutState = aDirectLogoutState(),
eventSink = eventSink,
)

View file

@ -18,10 +18,10 @@ package io.element.android.features.preferences.impl.root
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@ -112,6 +112,11 @@ fun PreferencesRootView(
Footer(
version = state.version,
deviceId = state.deviceId,
onClick = if (!state.showDeveloperSettings) {
{ state.eventSink(PreferencesRootEvents.OnVersionInfoClick) }
} else {
null
}
)
}
}
@ -231,9 +236,10 @@ private fun ColumnScope.GeneralSection(
}
@Composable
private fun Footer(
private fun ColumnScope.Footer(
version: String,
deviceId: String?
deviceId: String?,
onClick: (() -> Unit)?,
) {
val text = remember(version, deviceId) {
buildString {
@ -246,8 +252,10 @@ private fun Footer(
}
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 40.dp, bottom = 24.dp),
.align(Alignment.CenterHorizontally)
.padding(top = 16.dp)
.clickable(enabled = onClick != null, onClick = onClick ?: {})
.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 24.dp),
textAlign = TextAlign.Center,
text = text,
style = ElementTheme.typography.fontBodySmRegular,

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.preferences.impl.utils
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.core.meta.BuildType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
class ShowDeveloperSettingsProvider @Inject constructor(
buildMeta: BuildMeta,
) {
companion object {
const val DEVELOPER_SETTINGS_COUNTER = 7
}
private var counter = DEVELOPER_SETTINGS_COUNTER
private val isDeveloperBuild = buildMeta.buildType != BuildType.RELEASE
private val _showDeveloperSettings = MutableStateFlow(isDeveloperBuild)
val showDeveloperSettings: StateFlow<Boolean> = _showDeveloperSettings
fun unlockDeveloperSettings() {
if (counter == 0) {
return
}
counter--
if (counter == 0) {
_showDeveloperSettings.value = true
}
}
}