Convert InternalValues to kotlin.

This commit is contained in:
Greyson Parrelli
2024-11-18 12:18:29 -05:00
parent 5f67bd9725
commit ae37001949
25 changed files with 270 additions and 289 deletions

View File

@@ -1,228 +0,0 @@
package org.thoughtcrime.securesms.keyvalue;
import androidx.annotation.NonNull;
import org.signal.ringrtc.CallManager;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.util.Environment;
import org.thoughtcrime.securesms.util.RemoteConfig;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public final class InternalValues extends SignalStoreValues {
public static final String GV2_FORCE_INVITES = "internal.gv2.force_invites";
public static final String GV2_IGNORE_P2P_CHANGES = "internal.gv2.ignore_p2p_changes";
public static final String RECIPIENT_DETAILS = "internal.recipient_details";
public static final String ALLOW_CENSORSHIP_SETTING = "internal.force_censorship";
public static final String FORCE_BUILT_IN_EMOJI = "internal.force_built_in_emoji";
public static final String REMOVE_SENDER_KEY_MINIMUM = "internal.remove_sender_key_minimum";
public static final String DELAY_RESENDS = "internal.delay_resends";
public static final String CALLING_SERVER = "internal.calling_server";
public static final String CALLING_AUDIO_PROCESSING_METHOD = "internal.calling_audio_processing_method";
public static final String CALLING_DATA_MODE = "internal.calling_bandwidth_mode";
public static final String CALLING_DISABLE_TELECOM = "internal.calling_disable_telecom";
public static final String CALLING_ENABLE_OBOE_ADM = "internal.calling_enable_oboe_adm";
public static final String SHAKE_TO_REPORT = "internal.shake_to_report";
public static final String DISABLE_STORAGE_SERVICE = "internal.disable_storage_service";
public static final String FORCE_WEBSOCKET_MODE = "internal.force_websocket_mode";
public static final String LAST_SCROLL_POSITION = "internal.last_scroll_position";
public static final String CONVERSATION_ITEM_V2_MEDIA = "internal.conversation_item_v2_media";
public static final String FORCE_ENTER_RESTORE_V2_FLOW = "internal.force_enter_restore_v2_flow";
public static final String WEB_SOCKET_SHADOWING_STATS = "internal.web_socket_shadowing_stats";
public static final String ENCODE_HEVC = "internal.hevc_encoding";
public static final String NEW_CALL_UI = "internal.new.call.ui";
InternalValues(KeyValueStore store) {
super(store);
}
@Override
void onFirstEverAppLaunch() {
}
@Override
@NonNull List<String> getKeysToIncludeInBackup() {
return Collections.emptyList();
}
/**
* Members will not be added directly to a GV2 even if they could be.
*/
public synchronized boolean gv2ForceInvites() {
return RemoteConfig.internalUser() && getBoolean(GV2_FORCE_INVITES, false);
}
/**
* Signed group changes are sent P2P, if the client ignores them, it will then ask the server
* directly which allows testing of certain testing scenarios.
*/
public synchronized boolean gv2IgnoreP2PChanges() {
return RemoteConfig.internalUser() && getBoolean(GV2_IGNORE_P2P_CHANGES, false);
}
/**
* Show detailed recipient info in the {@link org.thoughtcrime.securesms.components.settings.conversation.InternalConversationSettingsFragment}.
*/
public synchronized boolean recipientDetails() {
return RemoteConfig.internalUser() && getBoolean(RECIPIENT_DETAILS, true);
}
/**
* Allow changing the censorship circumvention setting regardless of network status.
*/
public synchronized boolean allowChangingCensorshipSetting() {
return RemoteConfig.internalUser() && getBoolean(ALLOW_CENSORSHIP_SETTING, false);
}
/**
* Force the app to use the emoji that ship with the app, as opposed to the ones that were downloaded.
*/
public synchronized boolean forceBuiltInEmoji() {
return RemoteConfig.internalUser() && getBoolean(FORCE_BUILT_IN_EMOJI, false);
}
/**
* Remove the requirement that there must be two sender-key-capable recipients to use sender key
*/
public synchronized boolean removeSenderKeyMinimum() {
return RemoteConfig.internalUser() && getBoolean(REMOVE_SENDER_KEY_MINIMUM, false);
}
/**
* Delay resending messages in response to retry receipts by 10 seconds.
*/
public synchronized boolean delayResends() {
return RemoteConfig.internalUser() && getBoolean(DELAY_RESENDS, false);
}
/**
* Whether or not "shake to report" is enabled.
*/
public synchronized boolean shakeToReport() {
return RemoteConfig.internalUser() && getBoolean(SHAKE_TO_REPORT, true);
}
/**
* Whether or not storage service is manually disabled.
*/
public synchronized boolean storageServiceDisabled() {
return RemoteConfig.internalUser() && getBoolean(DISABLE_STORAGE_SERVICE, false);
}
/**
* The selected group calling server to use.
* <p>
* The user must be an internal user and the setting must be one of the current set of internal servers otherwise
* the default SFU will be returned. This ensures that if the {@link BuildConfig#SIGNAL_SFU_INTERNAL_URLS} list changes,
* internal users cannot be left on old servers.
*/
public synchronized @NonNull String groupCallingServer() {
String internalServer = RemoteConfig.internalUser() ? getString(CALLING_SERVER, Environment.Calling.defaultSfuUrl()) : null;
if (internalServer != null && !Arrays.asList(BuildConfig.SIGNAL_SFU_INTERNAL_URLS).contains(internalServer)) {
internalServer = null;
}
return internalServer != null ? internalServer : BuildConfig.SIGNAL_SFU_URL;
}
/**
* Setting to override the default handling of hardware/software AEC.
*/
public synchronized CallManager.AudioProcessingMethod callingAudioProcessingMethod() {
if (RemoteConfig.internalUser()) {
return CallManager.AudioProcessingMethod.values()[getInteger(CALLING_AUDIO_PROCESSING_METHOD, CallManager.AudioProcessingMethod.Default.ordinal())];
} else {
return CallManager.AudioProcessingMethod.Default;
}
}
/**
* Setting to override the default calling bandwidth mode.
*/
public synchronized CallManager.DataMode callingDataMode() {
if (RemoteConfig.internalUser()) {
int index = getInteger(CALLING_DATA_MODE, CallManager.DataMode.NORMAL.ordinal());
CallManager.DataMode[] modes = CallManager.DataMode.values();
return index < modes.length ? modes[index] : CallManager.DataMode.NORMAL;
} else {
return CallManager.DataMode.NORMAL;
}
}
/**
* Whether or not Telecom integration is manually disabled.
*/
public synchronized boolean callingDisableTelecom() {
if (RemoteConfig.internalUser()) {
return getBoolean(CALLING_DISABLE_TELECOM, true);
} else {
return false;
}
}
/**
* Whether or not the Oboe ADM is used.
*/
public synchronized boolean callingEnableOboeAdm() {
if (RemoteConfig.internalUser()) {
return getBoolean(CALLING_ENABLE_OBOE_ADM, true);
} else {
return false;
}
}
/**
* Whether or not the system is forced to be in 'websocket mode', where FCM is ignored and we use a foreground service to keep the app alive.
*/
public boolean isWebsocketModeForced() {
if (RemoteConfig.internalUser()) {
return getBoolean(FORCE_WEBSOCKET_MODE, false);
} else {
return false;
}
}
public void setHevcEncoding(boolean enabled) {
putBoolean(ENCODE_HEVC, enabled);
}
public boolean getHevcEncoding() {
return getBoolean(ENCODE_HEVC, false);
}
public void setNewCallingUi(boolean enabled) {
putBoolean(NEW_CALL_UI, enabled);
}
public boolean getNewCallingUi() {
return getBoolean(NEW_CALL_UI, false);
}
public void setLastScrollPosition(int position) {
putInteger(LAST_SCROLL_POSITION, position);
}
public int getLastScrollPosition() {
return getInteger(LAST_SCROLL_POSITION, 0);
}
public void setUseConversationItemV2Media(boolean useConversationFragmentV2Media) {
putBoolean(CONVERSATION_ITEM_V2_MEDIA, useConversationFragmentV2Media);
}
public boolean useConversationItemV2Media() {
return RemoteConfig.internalUser() && getBoolean(CONVERSATION_ITEM_V2_MEDIA, false);
}
public synchronized void setWebSocketShadowingStats(byte[] bytes) {
putBlob(WEB_SOCKET_SHADOWING_STATS, bytes);
}
public synchronized byte[] getWebSocketShadowingStats(byte[] defaultValue) {
return getBlob(WEB_SOCKET_SHADOWING_STATS, defaultValue);
}
}

View File

@@ -0,0 +1,161 @@
package org.thoughtcrime.securesms.keyvalue
import org.signal.ringrtc.CallManager.AudioProcessingMethod
import org.signal.ringrtc.CallManager.DataMode
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.util.Environment.Calling.defaultSfuUrl
import org.thoughtcrime.securesms.util.RemoteConfig
class InternalValues internal constructor(store: KeyValueStore) : SignalStoreValues(store) {
companion object {
const val GV2_FORCE_INVITES: String = "internal.gv2.force_invites"
const val GV2_IGNORE_P2P_CHANGES: String = "internal.gv2.ignore_p2p_changes"
const val RECIPIENT_DETAILS: String = "internal.recipient_details"
const val ALLOW_CENSORSHIP_SETTING: String = "internal.force_censorship"
const val FORCE_BUILT_IN_EMOJI: String = "internal.force_built_in_emoji"
const val REMOVE_SENDER_KEY_MINIMUM: String = "internal.remove_sender_key_minimum"
const val DELAY_RESENDS: String = "internal.delay_resends"
const val CALLING_SERVER: String = "internal.calling_server"
const val CALLING_AUDIO_PROCESSING_METHOD: String = "internal.calling_audio_processing_method"
const val CALLING_DATA_MODE: String = "internal.calling_bandwidth_mode"
const val CALLING_DISABLE_TELECOM: String = "internal.calling_disable_telecom"
const val CALLING_ENABLE_OBOE_ADM: String = "internal.calling_enable_oboe_adm"
const val SHAKE_TO_REPORT: String = "internal.shake_to_report"
const val DISABLE_STORAGE_SERVICE: String = "internal.disable_storage_service"
const val FORCE_WEBSOCKET_MODE: String = "internal.force_websocket_mode"
const val LAST_SCROLL_POSITION: String = "internal.last_scroll_position"
const val CONVERSATION_ITEM_V2_MEDIA: String = "internal.conversation_item_v2_media"
const val WEB_SOCKET_SHADOWING_STATS: String = "internal.web_socket_shadowing_stats"
const val ENCODE_HEVC: String = "internal.hevc_encoding"
const val NEW_CALL_UI: String = "internal.new.call.ui"
}
public override fun onFirstEverAppLaunch() = Unit
public override fun getKeysToIncludeInBackup(): List<String> = emptyList()
/**
* Members will not be added directly to a GV2 even if they could be.
*/
var gv2ForceInvites by booleanValue(GV2_FORCE_INVITES, false).defaultForExternalUsers()
/**
* Signed group changes are sent P2P, if the client ignores them, it will then ask the server
* directly which allows testing of certain testing scenarios.
*/
var gv2IgnoreP2PChanges by booleanValue(GV2_IGNORE_P2P_CHANGES, false).defaultForExternalUsers()
/**
* Show detailed recipient info in the [org.thoughtcrime.securesms.components.settings.conversation.InternalConversationSettingsFragment].
*/
var recipientDetails by booleanValue(RECIPIENT_DETAILS, true).falseForExternalUsers()
/**
* Allow changing the censorship circumvention setting regardless of network status.
*/
var allowChangingCensorshipSetting by booleanValue(ALLOW_CENSORSHIP_SETTING, false).defaultForExternalUsers()
/**
* Force the app to use the emoji that ship with the app, as opposed to the ones that were downloaded.
*/
var forceBuiltInEmoji by booleanValue(FORCE_BUILT_IN_EMOJI, false).defaultForExternalUsers()
/**
* Remove the requirement that there must be two sender-key-capable recipients to use sender key
*/
var removeSenderKeyMinimum by booleanValue(REMOVE_SENDER_KEY_MINIMUM, false).defaultForExternalUsers()
/**
* Delay resending messages in response to retry receipts by 10 seconds.
*/
var delayResends by booleanValue(DELAY_RESENDS, false).defaultForExternalUsers()
/**
* Whether or not "shake to report" is enabled.
*/
var shakeToReport: Boolean by booleanValue(SHAKE_TO_REPORT, true).falseForExternalUsers()
/**
* Whether or not storage service is manually disabled.
*/
var storageServiceDisabled by booleanValue(DISABLE_STORAGE_SERVICE, false).defaultForExternalUsers()
/**
* The selected group calling server to use.
*
*
* The user must be an internal user and the setting must be one of the current set of internal servers otherwise
* the default SFU will be returned. This ensures that if the [BuildConfig.SIGNAL_SFU_INTERNAL_URLS] list changes,
* internal users cannot be left on old servers.
*/
var groupCallingServer: String
get() {
var internalServer = if (RemoteConfig.internalUser) getString(CALLING_SERVER, defaultSfuUrl()) else null
if (internalServer != null && !listOf(*BuildConfig.SIGNAL_SFU_INTERNAL_URLS).contains(internalServer)) {
internalServer = null
}
return internalServer ?: BuildConfig.SIGNAL_SFU_URL
}
set(value) = putString(CALLING_SERVER, value)
/**
* Setting to override the default handling of hardware/software AEC.
*/
val callingAudioProcessingMethod: AudioProcessingMethod
get() {
return if (RemoteConfig.internalUser) {
val entryIndex = getInteger(CALLING_AUDIO_PROCESSING_METHOD, AudioProcessingMethod.Default.ordinal)
AudioProcessingMethod.entries[entryIndex]
} else {
AudioProcessingMethod.Default
}
}
/**
* Setting to override the default calling bandwidth mode.
*/
val callingDataMode: DataMode
get() {
return if (RemoteConfig.internalUser) {
val index = getInteger(CALLING_DATA_MODE, DataMode.NORMAL.ordinal)
val modes: Array<DataMode> = DataMode.entries.toTypedArray()
if (index < modes.size) modes[index] else DataMode.NORMAL
} else {
DataMode.NORMAL
}
}
/**
* Whether or not Telecom integration is manually disabled.
*/
var callingDisableTelecom by booleanValue(CALLING_DISABLE_TELECOM, true).falseForExternalUsers()
/**
* Whether or not the Oboe ADM is used.
*/
var callingEnableOboeAdm by booleanValue(CALLING_ENABLE_OBOE_ADM, true).falseForExternalUsers()
/**
* Whether or not the system is forced to be in 'websocket mode', where FCM is ignored and we use a foreground service to keep the app alive.
*/
var isWebsocketModeForced: Boolean by booleanValue(FORCE_WEBSOCKET_MODE, false).defaultForExternalUsers()
var hevcEncoding by booleanValue(ENCODE_HEVC, false).defaultForExternalUsers()
var newCallingUi: Boolean by booleanValue(NEW_CALL_UI, false).defaultForExternalUsers()
var lastScrollPosition: Int by integerValue(LAST_SCROLL_POSITION, 0).defaultForExternalUsers()
var useConversationItemV2Media by booleanValue(CONVERSATION_ITEM_V2_MEDIA, false).defaultForExternalUsers()
var webSocketShadowingStats by nullableBlobValue(WEB_SOCKET_SHADOWING_STATS, null).defaultForExternalUsers()
private fun <T> SignalStoreValueDelegate<T>.defaultForExternalUsers(): SignalStoreValueDelegate<T> {
return this.withPrecondition { RemoteConfig.internalUser }
}
private fun SignalStoreValueDelegate<Boolean>.falseForExternalUsers(): SignalStoreValueDelegate<Boolean> {
return this.map { actualValue -> RemoteConfig.internalUser && actualValue }
}
}

View File

@@ -42,11 +42,25 @@ internal fun <M> SignalStoreValues.protoValue(key: String, adapter: ProtoAdapter
return KeyValueProtoValue(key, adapter, this.store)
}
internal fun <T> SignalStoreValueDelegate<T>.withPrecondition(precondition: () -> Boolean): SignalStoreValueDelegate<T> {
return PreconditionDelegate(
delegate = this,
precondition = precondition
)
}
internal fun <T> SignalStoreValueDelegate<T>.map(transform: (T) -> T): SignalStoreValueDelegate<T> {
return MappingDelegate(
delegate = this,
transform = transform
)
}
/**
* Kotlin delegate that serves as a base for all other value types. This allows us to only expose this sealed
* class to callers and protect the individual implementations as private behind the various extension functions.
*/
sealed class SignalStoreValueDelegate<T>(private val store: KeyValueStore) {
sealed class SignalStoreValueDelegate<T>(val store: KeyValueStore, open val default: T) {
private var flow: Lazy<MutableStateFlow<T>> = lazy { MutableStateFlow(getValue(store)) }
@@ -69,7 +83,7 @@ sealed class SignalStoreValueDelegate<T>(private val store: KeyValueStore) {
internal abstract fun setValue(values: KeyValueStore, value: T)
}
private class LongValue(private val key: String, private val default: Long, store: KeyValueStore) : SignalStoreValueDelegate<Long>(store) {
private class LongValue(private val key: String, default: Long, store: KeyValueStore) : SignalStoreValueDelegate<Long>(store, default) {
override fun getValue(values: KeyValueStore): Long {
return values.getLong(key, default)
}
@@ -79,7 +93,7 @@ private class LongValue(private val key: String, private val default: Long, stor
}
}
private class BooleanValue(private val key: String, private val default: Boolean, store: KeyValueStore) : SignalStoreValueDelegate<Boolean>(store) {
private class BooleanValue(private val key: String, default: Boolean, store: KeyValueStore) : SignalStoreValueDelegate<Boolean>(store, default) {
override fun getValue(values: KeyValueStore): Boolean {
return values.getBoolean(key, default)
}
@@ -89,7 +103,7 @@ private class BooleanValue(private val key: String, private val default: Boolean
}
}
private class StringValue<T : String?>(private val key: String, private val default: T, store: KeyValueStore) : SignalStoreValueDelegate<T>(store) {
private class StringValue<T : String?>(private val key: String, default: T, store: KeyValueStore) : SignalStoreValueDelegate<T>(store, default) {
override fun getValue(values: KeyValueStore): T {
@Suppress("UNCHECKED_CAST")
return values.getString(key, default) as T
@@ -100,7 +114,7 @@ private class StringValue<T : String?>(private val key: String, private val defa
}
}
private class IntValue(private val key: String, private val default: Int, store: KeyValueStore) : SignalStoreValueDelegate<Int>(store) {
private class IntValue(private val key: String, default: Int, store: KeyValueStore) : SignalStoreValueDelegate<Int>(store, default) {
override fun getValue(values: KeyValueStore): Int {
return values.getInteger(key, default)
}
@@ -110,7 +124,7 @@ private class IntValue(private val key: String, private val default: Int, store:
}
}
private class FloatValue(private val key: String, private val default: Float, store: KeyValueStore) : SignalStoreValueDelegate<Float>(store) {
private class FloatValue(private val key: String, default: Float, store: KeyValueStore) : SignalStoreValueDelegate<Float>(store, default) {
override fun getValue(values: KeyValueStore): Float {
return values.getFloat(key, default)
}
@@ -120,7 +134,7 @@ private class FloatValue(private val key: String, private val default: Float, st
}
}
private class BlobValue(private val key: String, private val default: ByteArray, store: KeyValueStore) : SignalStoreValueDelegate<ByteArray>(store) {
private class BlobValue(private val key: String, default: ByteArray, store: KeyValueStore) : SignalStoreValueDelegate<ByteArray>(store, default) {
override fun getValue(values: KeyValueStore): ByteArray {
return values.getBlob(key, default)
}
@@ -130,7 +144,7 @@ private class BlobValue(private val key: String, private val default: ByteArray,
}
}
private class NullableBlobValue(private val key: String, private val default: ByteArray?, store: KeyValueStore) : SignalStoreValueDelegate<ByteArray?>(store) {
private class NullableBlobValue(private val key: String, default: ByteArray?, store: KeyValueStore) : SignalStoreValueDelegate<ByteArray?>(store, default) {
override fun getValue(values: KeyValueStore): ByteArray? {
return values.getBlob(key, default)
}
@@ -144,7 +158,7 @@ private class KeyValueProtoValue<M>(
private val key: String,
private val adapter: ProtoAdapter<M>,
store: KeyValueStore
) : SignalStoreValueDelegate<M?>(store) {
) : SignalStoreValueDelegate<M?>(store, null) {
override fun getValue(values: KeyValueStore): M? {
return if (values.containsKey(key)) {
adapter.decode(values.getBlob(key, null))
@@ -162,7 +176,7 @@ private class KeyValueProtoValue<M>(
}
}
private class KeyValueEnumValue<T>(private val key: String, private val default: T, private val serializer: LongSerializer<T>, store: KeyValueStore) : SignalStoreValueDelegate<T>(store) {
private class KeyValueEnumValue<T>(private val key: String, default: T, private val serializer: LongSerializer<T>, store: KeyValueStore) : SignalStoreValueDelegate<T>(store, default) {
override fun getValue(values: KeyValueStore): T {
return if (values.containsKey(key)) {
serializer.deserialize(values.getLong(key, 0))
@@ -175,3 +189,37 @@ private class KeyValueEnumValue<T>(private val key: String, private val default:
values.beginWrite().putLong(key, serializer.serialize(value)).apply()
}
}
private class PreconditionDelegate<T>(
private val delegate: SignalStoreValueDelegate<T>,
private val precondition: () -> Boolean
) : SignalStoreValueDelegate<T>(delegate.store, delegate.default) {
override fun getValue(values: KeyValueStore): T {
return if (precondition()) {
delegate.getValue(values)
} else {
delegate.default
}
}
override fun setValue(values: KeyValueStore, value: T) {
if (precondition()) {
delegate.setValue(values, value)
}
}
}
private class MappingDelegate<T>(
private val delegate: SignalStoreValueDelegate<T>,
private val transform: (T) -> T
) : SignalStoreValueDelegate<T>(delegate.store, delegate.default) {
override fun getValue(values: KeyValueStore): T {
return transform(delegate.getValue(values))
}
override fun setValue(values: KeyValueStore, value: T) {
delegate.setValue(values, value)
}
}