From f8d3336a1e9dd9f01b822a0735de03ceb2a6fc6f Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 4 Mar 2026 13:29:02 -0500 Subject: [PATCH] Add internal setting to disable internal user. --- .../app/internal/InternalSettingsFragment.kt | 10 ++++++++++ .../app/internal/InternalSettingsState.kt | 3 ++- .../app/internal/InternalSettingsViewModel.kt | 9 ++++++++- .../thoughtcrime/securesms/util/RemoteConfig.kt | 15 ++++++++++++++- .../util/RemoteConfig_StaticValuesTest.kt | 11 +++++++---- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt index 1ab3769e77..d9a3858987 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt @@ -190,6 +190,16 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter promptUserForSentTimestamp() } ) + + switchPref( + title = DSLSettingsText.from("Disable internal user flag"), + summary = DSLSettingsText.from("Experience life as a non-internal user. Force-stop the app to be an internal user again."), + isChecked = state.disableInternalUser, + onClick = { + viewModel.setDisableInternalUser(!state.disableInternalUser) + } + ) + dividerPref() sectionHeaderPref(DSLSettingsText.from("App UI")) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt index 1e3e990d52..ebc8990966 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsState.kt @@ -31,5 +31,6 @@ data class InternalSettingsState( val hasPendingOneTimeDonation: Boolean, val hevcEncoding: Boolean, val forceSplitPane: Boolean, - val useNewMediaActivity: Boolean + val useNewMediaActivity: Boolean, + val disableInternalUser: Boolean ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt index a4860d5bfd..67027b8b84 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsViewModel.kt @@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.keyvalue.InternalValues import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.stories.Stories +import org.thoughtcrime.securesms.util.RemoteConfig import org.thoughtcrime.securesms.util.livedata.Store class InternalSettingsViewModel(private val repository: InternalSettingsRepository) : ViewModel() { @@ -202,7 +203,8 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito hasPendingOneTimeDonation = SignalStore.inAppPayments.getPendingOneTimeDonation() != null, hevcEncoding = SignalStore.internal.hevcEncoding, forceSplitPane = SignalStore.internal.forceSplitPane, - useNewMediaActivity = SignalStore.internal.useNewMediaActivity + useNewMediaActivity = SignalStore.internal.useNewMediaActivity, + disableInternalUser = RemoteConfig.internalUserDisabled ) fun onClearOnboardingState() { @@ -213,6 +215,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito StoryOnboardingDownloadJob.enqueueIfNeeded() } + fun setDisableInternalUser(disabled: Boolean) { + RemoteConfig.internalUserDisabled = disabled + refresh() + } + fun setForceSplitPane(forceSplitPane: Boolean) { SignalStore.internal.forceSplitPane = forceSplitPane refresh() diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt index f8713543dc..7b222bb306 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt @@ -82,6 +82,14 @@ object RemoteConfig { var initialized: Boolean = false private val initLock: ReentrantLock = ReentrantLock() + /** Solely for fixing an issue with the internalUser flag */ + @VisibleForTesting + var underTest: Boolean = false + + @JvmStatic + @Volatile + var internalUserDisabled: Boolean = false + @JvmStatic fun init() { initLock.withLock { @@ -590,7 +598,12 @@ object RemoteConfig { key = "android.internalUser", hotSwappable = true ) { value -> - value.asBoolean(false) || Environment.isInternal() + when { + internalUserDisabled -> false + underTest -> value.asBoolean(false) + Environment.isInternal() -> true + else -> value.asBoolean(false) + } } /** The raw client expiration JSON string. */ diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/RemoteConfig_StaticValuesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/util/RemoteConfig_StaticValuesTest.kt index b39fa0374b..7b6ce143b5 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/util/RemoteConfig_StaticValuesTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/util/RemoteConfig_StaticValuesTest.kt @@ -14,6 +14,7 @@ class RemoteConfig_StaticValuesTest { @Before fun setup() { RemoteConfig.initialized = true + RemoteConfig.underTest = true } /** @@ -40,17 +41,19 @@ class RemoteConfig_StaticValuesTest { val configKeys = RemoteConfig.configsByKey.keys val ignoreList = setOf( - "initialized", - "REMOTE_VALUES", "configsByKey", - "debugMemoryValues", "debugDiskValues", + "debugMemoryValues", "debugPendingDiskValues", + "initialized", + "internalUserDisabled", "libsignalConfigs", + "underTest", "CRASH_PROMPT_CONFIG", + "DEVICE_SPECIFIC_NOTIFICATION_CONFIG", "PROMPT_BATTERY_SAVER", "PROMPT_FOR_NOTIFICATION_LOGS", - "DEVICE_SPECIFIC_NOTIFICATION_CONFIG" + "REMOTE_VALUES" ) val publicVals: List> = RemoteConfig::class.memberProperties