Convert SettingsValues to kotlin.

This commit is contained in:
Greyson Parrelli
2026-09-09 16:37:45 -04:00
committed by Cody Henthorne
parent c4f9b0e416
commit 4f3f50e131
14 changed files with 575 additions and 801 deletions
@@ -120,7 +120,7 @@ object AccountDataArchiveProcessor {
storiesDisabled = signalStore.storyValues.isFeatureDisabled,
hasViewedOnboardingStory = signalStore.storyValues.userHasViewedOnboardingStory,
hasSetMyStoriesPrivacy = signalStore.storyValues.userHasBeenNotifiedAboutStories,
keepMutedChatsArchived = signalStore.settingsValues.shouldKeepMutedChatsArchived(),
keepMutedChatsArchived = signalStore.settingsValues.keepMutedChatsArchived,
displayBadgesOnProfile = signalStore.inAppPaymentValues.getDisplayBadgesOnProfile(),
hasSeenGroupStoryEducationSheet = signalStore.storyValues.userHasSeenGroupStoryEducationSheet,
hasCompletedUsernameOnboarding = signalStore.uiHintValues.hasCompletedUsernameOnboarding(),
@@ -277,7 +277,7 @@ object AccountDataArchiveProcessor {
SignalStore.settings.universalExpireTimer = settings.universalExpireTimerSeconds
SignalStore.emoji.reactions = settings.preferredReactionEmoji
SignalStore.inAppPayments.setDisplayBadgesOnProfile(settings.displayBadgesOnProfile)
SignalStore.settings.setKeepMutedChatsArchived(settings.keepMutedChatsArchived)
SignalStore.settings.keepMutedChatsArchived = settings.keepMutedChatsArchived
SignalStore.story.userHasBeenNotifiedAboutStories = settings.hasSetMyStoriesPrivacy
SignalStore.story.userHasViewedOnboardingStory = settings.hasViewedOnboardingStory
SignalStore.story.isFeatureDisabled = settings.storiesDisabled
@@ -286,8 +286,8 @@ object AccountDataArchiveProcessor {
SignalStore.backup.optimizeStorage = !importState.backupMode.isLinkAndSync && settings.optimizeOnDeviceStorage
SignalStore.backup.backupTier = MessageBackupTier.fromBackupLevel(settings.backupTier)
SignalStore.settings.sentMediaQuality = settings.defaultSentMediaQuality.toLocalSentMediaQuality()
SignalStore.settings.setTheme(settings.appTheme.toLocalTheme())
SignalStore.settings.setCallDataMode(settings.callsUseLessDataSetting.toLocalCallDataMode())
SignalStore.settings.theme = settings.appTheme.toLocalTheme()
SignalStore.settings.callDataMode = settings.callsUseLessDataSetting.toLocalCallDataMode()
SignalStore.settings.automaticVerificationEnabled = settings.allowAutomaticKeyVerification
SignalStore.settings.setUnreadBadgeType(settings.unreadBadgeType.value)
SignalStore.settings.setIncludeMutedInBadgeCount(settings.includeMutedChatsInBadge ?: false)
@@ -22,7 +22,6 @@ import org.thoughtcrime.securesms.help.HelpFragment
import org.thoughtcrime.securesms.keyvalue.SettingsValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.profiles.manage.UsernameEditMode
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.service.KeyCachingService
import org.thoughtcrime.securesms.util.CachedInflater
import org.thoughtcrime.securesms.util.DynamicTheme
@@ -128,7 +127,7 @@ class AppSettingsActivity : DSLSettingsActivity(), GooglePayComponent {
when (intent.getStringExtra(EXTRA_PERFORM_ACTION_ON_CREATE)) {
ACTION_CHANGE_NUMBER_SUCCESS -> {
MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.ChangeNumber__your_phone_number_has_changed_to_s, SignalE164Util.prettyPrint(Recipient.self().requireE164())))
.setMessage(getString(R.string.ChangeNumber__your_phone_number_has_changed_to_s, SignalE164Util.prettyPrint(SignalStore.account.requireE164())))
.setPositiveButton(R.string.ChangeNumber__okay, null)
.show()
}
@@ -29,7 +29,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor(
ChatsSettingsState(
generateLinkPreviews = SignalStore.settings.isLinkPreviewsEnabled,
useAddressBook = SignalStore.settings.isPreferSystemContactPhotos,
keepMutedChatsArchived = SignalStore.settings.shouldKeepMutedChatsArchived(),
keepMutedChatsArchived = SignalStore.settings.keepMutedChatsArchived,
useSystemEmoji = SignalStore.settings.isPreferSystemEmoji,
enterKeySends = SignalStore.settings.isEnterKeySends,
localBackupsEnabled = SignalStore.settings.isBackupEnabled && BackupUtil.canUserAccessBackupDirectory(AppDependencies.application),
@@ -97,7 +97,7 @@ class ChatsSettingsViewModel @JvmOverloads constructor(
fun setKeepMutedChatsArchived(enabled: Boolean) {
store.update { it.copy(keepMutedChatsArchived = enabled) }
SignalStore.settings.setKeepMutedChatsArchived(enabled)
SignalStore.settings.keepMutedChatsArchived = enabled
repository.syncKeepMutedChatsArchivedState()
}
@@ -35,7 +35,7 @@ class ManageStorageSettingsViewModel : ViewModel() {
ManageStorageState(
keepMessagesDuration = SignalStore.settings.keepMessagesDuration,
lengthLimit = if (SignalStore.settings.isTrimByLengthEnabled) SignalStore.settings.threadTrimLength else ManageStorageState.NO_LIMIT,
syncTrimDeletes = SignalStore.settings.shouldSyncThreadTrimDeletes(),
syncTrimDeletes = SignalStore.settings.syncThreadTrimDeletes,
localBackupsEnabled = SignalStore.backup.newLocalBackupsEnabled,
isPrimary = SignalStore.account.isPrimaryDevice,
initialOptimizeStorage = SignalStore.backup.optimizeStorage
@@ -73,7 +73,7 @@ class ManageStorageSettingsViewModel : ViewModel() {
}
fun setKeepMessagesDuration(newDuration: KeepMessagesDuration) {
SignalStore.settings.setKeepMessagesForDuration(newDuration)
SignalStore.settings.keepMessagesDuration = newDuration
AppDependencies.trimThreadsByDateManager.scheduleIfNecessary()
store.update { it.copy(keepMessagesDuration = newDuration) }
@@ -86,7 +86,7 @@ class ManageStorageSettingsViewModel : ViewModel() {
fun setChatLengthLimit(newLimit: Int) {
val restrictingChange = isRestrictingLengthLimitChange(newLimit)
SignalStore.settings.setThreadTrimByLengthEnabled(newLimit != ManageStorageState.NO_LIMIT)
SignalStore.settings.isTrimByLengthEnabled = newLimit != ManageStorageState.NO_LIMIT
SignalStore.settings.threadTrimLength = newLimit
store.update { it.copy(lengthLimit = newLimit) }
@@ -110,7 +110,7 @@ class ManageStorageSettingsViewModel : ViewModel() {
}
fun setSyncTrimDeletes(syncTrimDeletes: Boolean) {
SignalStore.settings.setSyncThreadTrimDeletes(syncTrimDeletes)
SignalStore.settings.syncThreadTrimDeletes = syncTrimDeletes
store.update { it.copy(syncTrimDeletes = syncTrimDeletes) }
}
@@ -295,7 +295,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
}
private fun allowedToUnarchive(threadId: Long): Boolean {
if (!SignalStore.settings.shouldKeepMutedChatsArchived()) {
if (!SignalStore.settings.keepMutedChatsArchived) {
return true
}
@@ -353,7 +353,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
return
}
val syncThreadTrimDeletes = SignalStore.settings.shouldSyncThreadTrimDeletes()
val syncThreadTrimDeletes = SignalStore.settings.syncThreadTrimDeletes
val threadTrimsToSync = mutableListOf<ThreadDeleteSyncInfo>()
readableDatabase
@@ -78,7 +78,7 @@ public class TrimThreadJob extends BaseJob {
long trimBeforeDate = keepMessagesDuration != KeepMessagesDuration.FOREVER ? System.currentTimeMillis() - keepMessagesDuration.getDuration()
: ThreadTable.NO_TRIM_BEFORE_DATE_SET;
SignalDatabase.threads().trimThread(threadId, SignalStore.settings().shouldSyncThreadTrimDeletes(), trimLength, trimBeforeDate, false);
SignalDatabase.threads().trimThread(threadId, SignalStore.settings().getSyncThreadTrimDeletes(), trimLength, trimBeforeDate, false);
}
@Override
@@ -1,779 +0,0 @@
package org.thoughtcrime.securesms.keyvalue;
import android.content.Context;
import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.signal.mediasend.SentMediaQuality;
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference;
import org.thoughtcrime.securesms.util.Environment;
import org.thoughtcrime.securesms.util.SingleLiveEvent;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.webrtc.CallDataMode;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@SuppressWarnings("deprecation")
public final class SettingsValues extends SignalStoreValues {
private static final String TAG = Log.tag(SettingsValues.class);
public static final String LINK_PREVIEWS = "settings.link_previews";
public static final String KEEP_MESSAGES_DURATION = "settings.keep_messages_duration";
public static final String PREFER_SYSTEM_CONTACT_PHOTOS = "settings.prefer.system.contact.photos";
private static final String SIGNAL_BACKUP_DIRECTORY = "settings.signal.backup.directory";
private static final String SIGNAL_LATEST_BACKUP_DIRECTORY = "settings.signal.backup.directory,latest";
private static final String CALL_DATA_MODE = "settings.signal.call.bandwidth.mode";
public static final String THREAD_TRIM_LENGTH = "pref_trim_length";
public static final String THREAD_TRIM_ENABLED = "pref_trim_threads";
public static final String THEME = "settings.theme";
public static final String MESSAGE_FONT_SIZE = "settings.message.font.size";
public static final String LANGUAGE = "settings.language";
public static final String PREFER_SYSTEM_EMOJI = "settings.use.system.emoji";
public static final String ENTER_KEY_SENDS = "settings.enter.key.sends";
public static final String BACKUPS_ENABLED = "settings.backups.enabled";
public static final String BACKUPS_SCHEDULE_HOUR = "settings.backups.schedule.hour";
public static final String BACKUPS_SCHEDULE_MINUTE = "settings.backups.schedule.minute";
public static final String SIGNAL_BACKUPS_SCHEDULE_HOUR = "settings.signal.backups.schedule.hour";
public static final String SIGNAL_BACKUPS_SCHEDULE_MINUTE = "settings.signal.backups.schedule.minute";
public static final String SMS_DELIVERY_REPORTS_ENABLED = "settings.sms.delivery.reports.enabled";
public static final String WIFI_CALLING_COMPATIBILITY_MODE_ENABLED = "settings.wifi.calling.compatibility.mode.enabled";
public static final String MESSAGE_NOTIFICATIONS_ENABLED = "settings.message.notifications.enabled";
public static final String MESSAGE_NOTIFICATION_SOUND = "settings.message.notifications.sound";
public static final String MESSAGE_VIBRATE_ENABLED = "settings.message.vibrate.enabled";
public static final String MESSAGE_LED_COLOR = "settings.message.led.color";
public static final String MESSAGE_LED_BLINK_PATTERN = "settings.message.led.blink";
public static final String MESSAGE_IN_CHAT_SOUNDS_ENABLED = "settings.message.in.chats.sounds.enabled";
public static final String MESSAGE_REPEAT_ALERTS = "settings.message.repeat.alerts";
public static final String MESSAGE_NOTIFICATION_PRIVACY = "settings.message.notification.privacy";
public static final String MESSAGE_NOTIFICATION_REACTION = "settings.message.notification.reaction";
public static final String MESSAGE_NOTIFICATION_MUTED_CALLS = "settings.message.notifications.muted.call";
public static final String MESSAGE_NOTIFICATION_MUTED_MENTIONS = "settings.message.notifications.muted.mentions";
public static final String MESSAGE_NOTIFICATION_MUTED_REPLIES = "settings.message.notifications.muted.replies";
public static final String UNREAD_REMINDER_ENABLED = "settings.message.notifications.unread.reminder";
public static final String UNREAD_REMINDER_NEXT_NOTIFY_TIME = "settings.message.notifications.unread.reminder.next.notify.time";
public static final String UNREAD_BADGE_TYPE = "settings.notifications.badge.type";
public static final String INCLUDE_MUTED_IN_BADGE_COUNT = "settings.notifications.include.muted.badge.count";
public static final String CALL_NOTIFICATIONS_ENABLED = "settings.call.notifications.enabled";
public static final String CALL_RINGTONE = "settings.call.ringtone";
public static final String CALL_VIBRATE_ENABLED = "settings.call.vibrate.enabled";
public static final String NOTIFY_WHEN_CONTACT_JOINS_SIGNAL = "settings.notify.when.contact.joins.signal";
private static final String UNIVERSAL_EXPIRE_TIMER = "settings.universal.expire.timer";
private static final String SENT_MEDIA_QUALITY = "settings.sentMediaQuality";
private static final String CENSORSHIP_CIRCUMVENTION_ENABLED = "settings.censorshipCircumventionEnabled";
private static final String KEEP_MUTED_CHATS_ARCHIVED = "settings.keepMutedChatsArchived";
private static final String USE_COMPACT_NAVIGATION_BAR = "settings.useCompactNavigationBar";
private static final String THREAD_TRIM_SYNC_TO_LINKED_DEVICES = "settings.storage.syncThreadTrimDeletes";
private static final String PASSPHRASE_DISABLED = "settings.passphrase.disabled";
private static final String PASSPHRASE_TIMEOUT_ENABLED = "settings.passphrase.timeout.enabled";
private static final String PASSPHRASE_TIMEOUT = "settings.passphrase.timeout";
private static final String SCREEN_LOCK_ENABLED = "settings.screen.lock.enabled";
private static final String SCREEN_LOCK_TIMEOUT = "settings.screen.lock.timeout";
private static final String AUTOMATIC_VERIFICATION_ENABLED = "settings.automatic.verification.enabled";
private static final String FORCE_WEBSOCKET_MODE = "settings.force.websocket.mode.2";
public static final int BACKUP_DEFAULT_HOUR = 2;
public static final int BACKUP_DEFAULT_MINUTE = 0;
private final SingleLiveEvent<String> onConfigurationSettingChanged = new SingleLiveEvent<>();
SettingsValues(@NonNull KeyValueStore store, Context context) {
super(store);
if (!store.containsKey(SCREEN_LOCK_ENABLED) && !Environment.IS_INSTRUMENTATION) {
migrateFromSharedPrefsV1(context);
}
}
private void migrateFromSharedPrefsV1(@NonNull Context context) {
Log.i(TAG, "[V1] Migrating screen lock values from shared prefs.");
putBoolean(PASSPHRASE_DISABLED, TextSecurePreferences.getBooleanPreference(context, "pref_disable_passphrase", true));
putBoolean(PASSPHRASE_TIMEOUT_ENABLED, TextSecurePreferences.getBooleanPreference(context, "pref_timeout_passphrase", false));
putInteger(PASSPHRASE_TIMEOUT, TextSecurePreferences.getIntegerPreference(context, "pref_timeout_interval", 5 * 60));
putBoolean(SCREEN_LOCK_ENABLED, TextSecurePreferences.getBooleanPreference(context, "pref_android_screen_lock", false));
putLong(SCREEN_LOCK_TIMEOUT, TextSecurePreferences.getLongPreference(context, "pref_android_screen_lock_timeout", 0));
}
@Override
void onFirstEverAppLaunch() {
final KeyValueStore store = getStore();
if (!store.containsKey(LINK_PREVIEWS)) {
store.beginWrite()
.putBoolean(LINK_PREVIEWS, true)
.apply();
}
if (!store.containsKey(BACKUPS_SCHEDULE_HOUR)) {
// Initialize backup time to a 5min interval between 1-5am
setBackupSchedule(new Random().nextInt(5) + 1, new Random().nextInt(12) * 5);
}
if (!store.containsKey(SIGNAL_BACKUPS_SCHEDULE_HOUR)) {
initSignalBackupsSchedule();
}
}
@Override
@NonNull List<String> getKeysToIncludeInBackup() {
return Arrays.asList(LINK_PREVIEWS,
KEEP_MESSAGES_DURATION,
PREFER_SYSTEM_CONTACT_PHOTOS,
CALL_DATA_MODE,
THREAD_TRIM_LENGTH,
THREAD_TRIM_ENABLED,
LANGUAGE,
THEME,
MESSAGE_FONT_SIZE,
PREFER_SYSTEM_EMOJI,
ENTER_KEY_SENDS,
BACKUPS_ENABLED,
MESSAGE_NOTIFICATIONS_ENABLED,
MESSAGE_NOTIFICATION_SOUND,
MESSAGE_VIBRATE_ENABLED,
MESSAGE_LED_COLOR,
MESSAGE_LED_BLINK_PATTERN,
MESSAGE_IN_CHAT_SOUNDS_ENABLED,
MESSAGE_REPEAT_ALERTS,
MESSAGE_NOTIFICATION_PRIVACY,
CALL_NOTIFICATIONS_ENABLED,
CALL_RINGTONE,
CALL_VIBRATE_ENABLED,
NOTIFY_WHEN_CONTACT_JOINS_SIGNAL,
UNIVERSAL_EXPIRE_TIMER,
SENT_MEDIA_QUALITY,
KEEP_MUTED_CHATS_ARCHIVED,
USE_COMPACT_NAVIGATION_BAR,
THREAD_TRIM_SYNC_TO_LINKED_DEVICES,
PASSPHRASE_DISABLED,
PASSPHRASE_TIMEOUT_ENABLED,
PASSPHRASE_TIMEOUT,
SCREEN_LOCK_ENABLED,
SCREEN_LOCK_TIMEOUT);
}
public @NonNull LiveData<String> getOnConfigurationSettingChanged() {
return onConfigurationSettingChanged;
}
public boolean isLinkPreviewsEnabled() {
return getBoolean(LINK_PREVIEWS, false);
}
public void setLinkPreviewsEnabled(boolean enabled) {
putBoolean(LINK_PREVIEWS, enabled);
}
public @NonNull KeepMessagesDuration getKeepMessagesDuration() {
return KeepMessagesDuration.fromId(getInteger(KEEP_MESSAGES_DURATION, 0));
}
public void setKeepMessagesForDuration(@NonNull KeepMessagesDuration duration) {
putInteger(KEEP_MESSAGES_DURATION, duration.getId());
}
public boolean isTrimByLengthEnabled() {
return getBoolean(THREAD_TRIM_ENABLED, false);
}
public void setThreadTrimByLengthEnabled(boolean enabled) {
putBoolean(THREAD_TRIM_ENABLED, enabled);
}
public int getThreadTrimLength() {
return getInteger(THREAD_TRIM_LENGTH, 500);
}
public void setThreadTrimLength(int length) {
putInteger(THREAD_TRIM_LENGTH, length);
}
public boolean shouldSyncThreadTrimDeletes() {
if (!getStore().containsKey(THREAD_TRIM_SYNC_TO_LINKED_DEVICES)) {
setSyncThreadTrimDeletes(!isTrimByLengthEnabled() && getKeepMessagesDuration() == KeepMessagesDuration.FOREVER);
}
return getBoolean(THREAD_TRIM_SYNC_TO_LINKED_DEVICES, true) && SignalStore.account().isPrimaryDevice();
}
public void setSyncThreadTrimDeletes(boolean syncDeletes) {
putBoolean(THREAD_TRIM_SYNC_TO_LINKED_DEVICES, syncDeletes);
}
public void setSignalBackupDirectory(@NonNull Uri uri) {
putString(SIGNAL_BACKUP_DIRECTORY, uri.toString());
putString(SIGNAL_LATEST_BACKUP_DIRECTORY, uri.toString());
}
public void setPreferSystemContactPhotos(boolean preferSystemContactPhotos) {
putBoolean(PREFER_SYSTEM_CONTACT_PHOTOS, preferSystemContactPhotos);
}
public boolean isPreferSystemContactPhotos() {
return getBoolean(PREFER_SYSTEM_CONTACT_PHOTOS, false);
}
public @Nullable Uri getSignalBackupDirectory() {
return getUri(SIGNAL_BACKUP_DIRECTORY);
}
public @Nullable Uri getLatestSignalBackupDirectory() {
return getUri(SIGNAL_LATEST_BACKUP_DIRECTORY);
}
public void clearSignalBackupDirectory() {
putString(SIGNAL_BACKUP_DIRECTORY, null);
}
public void setCallDataMode(@NonNull CallDataMode callDataMode) {
putInteger(CALL_DATA_MODE, callDataMode.getCode());
}
public @NonNull CallDataMode getCallDataMode() {
return CallDataMode.fromCode(getInteger(CALL_DATA_MODE, CallDataMode.HIGH_ALWAYS.getCode()));
}
public @NonNull Theme getTheme() {
return Theme.deserialize(getString(THEME, TextSecurePreferences.getTheme(AppDependencies.getApplication())));
}
public void setTheme(@NonNull Theme theme) {
putString(THEME, theme.serialize());
onConfigurationSettingChanged.postValue(THEME);
}
public int getMessageFontSize() {
return getInteger(MESSAGE_FONT_SIZE, TextSecurePreferences.getMessageBodyTextSize(AppDependencies.getApplication()));
}
public int getMessageQuoteFontSize(@NonNull Context context) {
int currentMessageSize = getMessageFontSize();
int[] possibleMessageSizes = context.getResources().getIntArray(R.array.pref_message_font_size_values);
int[] possibleQuoteSizes = context.getResources().getIntArray(R.array.pref_message_font_quote_size_values);
int sizeIndex = Arrays.binarySearch(possibleMessageSizes, currentMessageSize);
if (sizeIndex < 0) {
int closestSizeIndex = 0;
int closestSizeDiff = Integer.MAX_VALUE;
for (int i = 0; i < possibleMessageSizes.length; i++) {
int diff = Math.abs(possibleMessageSizes[i] - currentMessageSize);
if (diff < closestSizeDiff) {
closestSizeIndex = i;
closestSizeDiff = diff;
}
}
int newSize = possibleMessageSizes[closestSizeIndex];
Log.w(TAG, "Using non-standard font size of " + currentMessageSize + ". Closest match was " + newSize + ". Updating.");
setMessageFontSize(newSize);
sizeIndex = Arrays.binarySearch(possibleMessageSizes, newSize);
}
return possibleQuoteSizes[sizeIndex];
}
public void setMessageFontSize(int messageFontSize) {
putInteger(MESSAGE_FONT_SIZE, messageFontSize);
}
public @NonNull String getLanguage() {
return TextSecurePreferences.getLanguage(AppDependencies.getApplication());
}
public void setLanguage(@NonNull String language) {
TextSecurePreferences.setLanguage(AppDependencies.getApplication(), language);
onConfigurationSettingChanged.postValue(LANGUAGE);
}
public boolean isPreferSystemEmoji() {
return getBoolean(PREFER_SYSTEM_EMOJI, TextSecurePreferences.isSystemEmojiPreferred(AppDependencies.getApplication()));
}
public void setPreferSystemEmoji(boolean useSystemEmoji) {
putBoolean(PREFER_SYSTEM_EMOJI, useSystemEmoji);
}
public boolean isEnterKeySends() {
return getBoolean(ENTER_KEY_SENDS, TextSecurePreferences.isEnterSendsEnabled(AppDependencies.getApplication()));
}
public void setEnterKeySends(boolean enterKeySends) {
putBoolean(ENTER_KEY_SENDS, enterKeySends);
}
public boolean isBackupEnabled() {
return getBoolean(BACKUPS_ENABLED, TextSecurePreferences.isBackupEnabled(AppDependencies.getApplication()));
}
public void setBackupEnabled(boolean backupEnabled) {
putBoolean(BACKUPS_ENABLED, backupEnabled);
}
public int getBackupHour() {
return getInteger(BACKUPS_SCHEDULE_HOUR, BACKUP_DEFAULT_HOUR);
}
public int getBackupMinute() {
return getInteger(BACKUPS_SCHEDULE_MINUTE, BACKUP_DEFAULT_MINUTE);
}
public int getSignalBackupHour() {
int hour = getInteger(SIGNAL_BACKUPS_SCHEDULE_HOUR, -1);
if (hour < 0) {
initSignalBackupsSchedule();
return getInteger(SIGNAL_BACKUPS_SCHEDULE_HOUR, BACKUP_DEFAULT_HOUR);
} else {
return hour;
}
}
public int getSignalBackupMinute() {
int minute = getInteger(SIGNAL_BACKUPS_SCHEDULE_MINUTE, -1);
if (minute < 0) {
initSignalBackupsSchedule();
return getInteger(SIGNAL_BACKUPS_SCHEDULE_MINUTE, BACKUP_DEFAULT_MINUTE);
} else {
return minute;
}
}
public void setBackupSchedule(int hour, int minute) {
putInteger(BACKUPS_SCHEDULE_HOUR, hour);
putInteger(BACKUPS_SCHEDULE_MINUTE, minute);
}
private void initSignalBackupsSchedule() {
setSignalBackupSchedule(new Random().nextInt(5) + 1, new Random().nextInt(12) * 5);
}
public void setSignalBackupSchedule(int hour, int minute) {
putInteger(SIGNAL_BACKUPS_SCHEDULE_HOUR, hour);
putInteger(SIGNAL_BACKUPS_SCHEDULE_MINUTE, minute);
}
public boolean isSmsDeliveryReportsEnabled() {
return getBoolean(SMS_DELIVERY_REPORTS_ENABLED, TextSecurePreferences.isSmsDeliveryReportsEnabled(AppDependencies.getApplication()));
}
public void setSmsDeliveryReportsEnabled(boolean smsDeliveryReportsEnabled) {
putBoolean(SMS_DELIVERY_REPORTS_ENABLED, smsDeliveryReportsEnabled);
}
public boolean isWifiCallingCompatibilityModeEnabled() {
return getBoolean(WIFI_CALLING_COMPATIBILITY_MODE_ENABLED, TextSecurePreferences.isWifiSmsEnabled(AppDependencies.getApplication()));
}
public void setWifiCallingCompatibilityModeEnabled(boolean wifiCallingCompatibilityModeEnabled) {
putBoolean(WIFI_CALLING_COMPATIBILITY_MODE_ENABLED, wifiCallingCompatibilityModeEnabled);
}
public void setMessageNotificationsEnabled(boolean messageNotificationsEnabled) {
putBoolean(MESSAGE_NOTIFICATIONS_ENABLED, messageNotificationsEnabled);
}
public boolean isMessageNotificationsEnabled() {
return getBoolean(MESSAGE_NOTIFICATIONS_ENABLED, TextSecurePreferences.isNotificationsEnabled(AppDependencies.getApplication()));
}
public void setMessageNotificationSound(@NonNull Uri sound) {
putString(MESSAGE_NOTIFICATION_SOUND, sound.toString());
}
public @NonNull Uri getMessageNotificationSound() {
String result = getString(MESSAGE_NOTIFICATION_SOUND, TextSecurePreferences.getNotificationRingtone(AppDependencies.getApplication()).toString());
if (result.startsWith("file:")) {
result = Settings.System.DEFAULT_NOTIFICATION_URI.toString();
}
return Uri.parse(result);
}
public boolean isMessageVibrateEnabled() {
return getBoolean(MESSAGE_VIBRATE_ENABLED, TextSecurePreferences.isNotificationVibrateEnabled(AppDependencies.getApplication()));
}
public void setMessageVibrateEnabled(boolean messageVibrateEnabled) {
putBoolean(MESSAGE_VIBRATE_ENABLED, messageVibrateEnabled);
}
public @NonNull String getMessageLedColor() {
return getString(MESSAGE_LED_COLOR, TextSecurePreferences.getNotificationLedColor(AppDependencies.getApplication()));
}
public void setMessageLedColor(@NonNull String ledColor) {
putString(MESSAGE_LED_COLOR, ledColor);
}
public @NonNull String getMessageLedBlinkPattern() {
return getString(MESSAGE_LED_BLINK_PATTERN, TextSecurePreferences.getNotificationLedPattern(AppDependencies.getApplication()));
}
public void setMessageLedBlinkPattern(@NonNull String blinkPattern) {
putString(MESSAGE_LED_BLINK_PATTERN, blinkPattern);
}
public boolean isMessageNotificationsInChatSoundsEnabled() {
return getBoolean(MESSAGE_IN_CHAT_SOUNDS_ENABLED, TextSecurePreferences.isInThreadNotifications(AppDependencies.getApplication()));
}
public void setMessageNotificationsInChatSoundsEnabled(boolean inChatSoundsEnabled) {
putBoolean(MESSAGE_IN_CHAT_SOUNDS_ENABLED, inChatSoundsEnabled);
}
public int getMessageNotificationsRepeatAlerts() {
return getInteger(MESSAGE_REPEAT_ALERTS, TextSecurePreferences.getRepeatAlertsCount(AppDependencies.getApplication()));
}
public void setMessageNotificationsRepeatAlerts(int count) {
putInteger(MESSAGE_REPEAT_ALERTS, count);
}
public @NonNull NotificationPrivacyPreference getMessageNotificationsPrivacy() {
return new NotificationPrivacyPreference(getString(MESSAGE_NOTIFICATION_PRIVACY, TextSecurePreferences.getNotificationPrivacy(AppDependencies.getApplication()).toString()));
}
public void setMessageNotificationsPrivacy(@NonNull NotificationPrivacyPreference messageNotificationsPrivacy) {
putString(MESSAGE_NOTIFICATION_PRIVACY, messageNotificationsPrivacy.toString());
}
public boolean getReactionNotifications() {
return getBoolean(MESSAGE_NOTIFICATION_REACTION, true);
}
public void setReactionNotifications(boolean show) {
putBoolean(MESSAGE_NOTIFICATION_REACTION, show);
}
public boolean getAllowCallsWhileMuted() {
return getBoolean(MESSAGE_NOTIFICATION_MUTED_CALLS, false);
}
public void setAllowCallsWhileMuted(boolean allow) {
putBoolean(MESSAGE_NOTIFICATION_MUTED_CALLS, allow);
}
public boolean getAllowMentionsWhileMuted() {
return getBoolean(MESSAGE_NOTIFICATION_MUTED_MENTIONS, true);
}
public void setAllowMentionsWhileMuted(boolean allow) {
putBoolean(MESSAGE_NOTIFICATION_MUTED_MENTIONS, allow);
}
public boolean getAllowRepliesWhileMuted() {
return getBoolean(MESSAGE_NOTIFICATION_MUTED_REPLIES, true);
}
public void setAllowRepliesWhileMuted(boolean allow) {
putBoolean(MESSAGE_NOTIFICATION_MUTED_REPLIES, allow);
}
public boolean getUnreadReminderEnabled() {
return getBoolean(UNREAD_REMINDER_ENABLED, true);
}
public void setUnreadReminderEnabled(boolean enabled) {
putBoolean(UNREAD_REMINDER_ENABLED, enabled);
}
public long getUnreadReminderNextNotifyTime() {
return getLong(UNREAD_REMINDER_NEXT_NOTIFY_TIME, 0L);
}
public void setUnreadReminderNextNotifyTime(long time) { // TODO(michelle): Use in unread job
putLong(UNREAD_REMINDER_NEXT_NOTIFY_TIME, time);
}
// Only used to round trip ios/desktop settings
public void setUnreadBadgeType(int type) {
putInteger(UNREAD_BADGE_TYPE, type);
}
public UnreadBadgeType getUnreadBadgeType() {
return UnreadBadgeType.deserialize(getInteger(UNREAD_BADGE_TYPE, UnreadBadgeType.UNREAD_MESSAGES.serialize()));
}
// Only used to round trip ios/desktop settings
public void setIncludeMutedInBadgeCount(boolean include) {
putBoolean(INCLUDE_MUTED_IN_BADGE_COUNT, include);
}
public @Nullable Boolean getIncludeMutedInBadgeCount() {
if (!getStore().containsKey(INCLUDE_MUTED_IN_BADGE_COUNT)) {
return null;
}
return getBoolean(INCLUDE_MUTED_IN_BADGE_COUNT, false);
}
public boolean isCallNotificationsEnabled() {
return getBoolean(CALL_NOTIFICATIONS_ENABLED, TextSecurePreferences.isCallNotificationsEnabled(AppDependencies.getApplication()));
}
public void setCallNotificationsEnabled(boolean callNotificationsEnabled) {
putBoolean(CALL_NOTIFICATIONS_ENABLED, callNotificationsEnabled);
}
public @NonNull Uri getCallRingtone() {
String result = getString(CALL_RINGTONE, TextSecurePreferences.getCallNotificationRingtone(AppDependencies.getApplication()).toString());
if (result != null && result.startsWith("file:")) {
result = Settings.System.DEFAULT_RINGTONE_URI.toString();
}
return Uri.parse(result);
}
public void setCallRingtone(@NonNull Uri ringtone) {
putString(CALL_RINGTONE, ringtone.toString());
}
public boolean isCallVibrateEnabled() {
return getBoolean(CALL_VIBRATE_ENABLED, TextSecurePreferences.isCallNotificationVibrateEnabled(AppDependencies.getApplication()));
}
public void setCallVibrateEnabled(boolean callVibrateEnabled) {
putBoolean(CALL_VIBRATE_ENABLED, callVibrateEnabled);
}
public boolean isNotifyWhenContactJoinsSignal() {
return getBoolean(NOTIFY_WHEN_CONTACT_JOINS_SIGNAL, TextSecurePreferences.isNewContactsNotificationEnabled(AppDependencies.getApplication()));
}
public void setNotifyWhenContactJoinsSignal(boolean notifyWhenContactJoinsSignal) {
putBoolean(NOTIFY_WHEN_CONTACT_JOINS_SIGNAL, notifyWhenContactJoinsSignal);
}
public void setUniversalExpireTimer(int seconds) {
putInteger(UNIVERSAL_EXPIRE_TIMER, seconds);
}
public int getUniversalExpireTimer() {
return getInteger(UNIVERSAL_EXPIRE_TIMER, 0);
}
public void setSentMediaQuality(@NonNull SentMediaQuality sentMediaQuality) {
putInteger(SENT_MEDIA_QUALITY, sentMediaQuality.code);
}
public @NonNull SentMediaQuality getSentMediaQuality() {
return SentMediaQuality.fromCode(getInteger(SENT_MEDIA_QUALITY, SentMediaQuality.STANDARD.code));
}
public @NonNull CensorshipCircumventionEnabled getCensorshipCircumventionEnabled() {
return CensorshipCircumventionEnabled.deserialize(getInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, CensorshipCircumventionEnabled.DEFAULT.serialize()));
}
public void setCensorshipCircumventionEnabled(boolean enabled) {
Log.i(TAG, "Changing censorship circumvention state to: " + enabled, new Throwable());
putInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, enabled ? CensorshipCircumventionEnabled.ENABLED.serialize() : CensorshipCircumventionEnabled.DISABLED.serialize());
}
public void setKeepMutedChatsArchived(boolean enabled) {
putBoolean(KEEP_MUTED_CHATS_ARCHIVED, enabled);
}
public boolean shouldKeepMutedChatsArchived() {
return getBoolean(KEEP_MUTED_CHATS_ARCHIVED, false);
}
public void setUseCompactNavigationBar(boolean enabled) {
putBoolean(USE_COMPACT_NAVIGATION_BAR, enabled);
}
public boolean getUseCompactNavigationBar() {
return getBoolean(USE_COMPACT_NAVIGATION_BAR, false);
}
public void setPassphraseDisabled(boolean disabled) {
putBoolean(PASSPHRASE_DISABLED, disabled);
}
public boolean getPassphraseDisabled() {
return getBoolean(PASSPHRASE_DISABLED, true);
}
public void setPassphraseTimeoutEnabled(boolean enabled) {
putBoolean(PASSPHRASE_TIMEOUT_ENABLED, enabled);
}
public boolean getPassphraseTimeoutEnabled() {
return getBoolean(PASSPHRASE_TIMEOUT_ENABLED, false);
}
public void setPassphraseTimeout(int minutes) {
putLong(PASSPHRASE_TIMEOUT, minutes);
}
public int getPassphraseTimeout() {
return getInteger(PASSPHRASE_TIMEOUT, 0);
}
public void setScreenLockEnabled(boolean enabled) {
putBoolean(SCREEN_LOCK_ENABLED, enabled);
}
public boolean getScreenLockEnabled() {
return getBoolean(SCREEN_LOCK_ENABLED, false);
}
public void setScreenLockTimeout(long seconds) {
putLong(SCREEN_LOCK_TIMEOUT, seconds);
}
public long getScreenLockTimeout() {
return getLong(SCREEN_LOCK_TIMEOUT, 0);
}
public boolean getAutomaticVerificationEnabled() {
return getBoolean(AUTOMATIC_VERIFICATION_ENABLED, true);
}
public void setAutomaticVerificationEnabled(boolean enabled) {
Log.i(TAG, "Setting key transparency enabled to " + enabled);
putBoolean(AUTOMATIC_VERIFICATION_ENABLED, enabled);
}
public @NonNull ForceWebsocketMode getForceWebsocketMode() {
if (getStore().containsKey(FORCE_WEBSOCKET_MODE)) {
return ForceWebsocketMode.deserialize(getInteger(FORCE_WEBSOCKET_MODE, ForceWebsocketMode.DISABLED.serialize()));
}
return getBoolean(FORCE_WEBSOCKET_MODE, false) ? ForceWebsocketMode.ENABLED_BY_USER : ForceWebsocketMode.DISABLED;
}
public void setForceWebsocketMode(@NonNull ForceWebsocketMode mode) {
putInteger(FORCE_WEBSOCKET_MODE, mode.serialize());
}
private @Nullable Uri getUri(@NonNull String key) {
String uri = getString(key, "");
if (TextUtils.isEmpty(uri)) {
return null;
} else {
return Uri.parse(uri);
}
}
public enum CensorshipCircumventionEnabled {
DEFAULT(0), ENABLED(1), DISABLED(2);
private final int value;
CensorshipCircumventionEnabled(int value) {
this.value = value;
}
public static CensorshipCircumventionEnabled deserialize(int value) {
switch (value) {
case 0:
return DEFAULT;
case 1:
return ENABLED;
case 2:
return DISABLED;
default:
throw new IllegalArgumentException("Bad value: " + value);
}
}
public int serialize() {
return value;
}
}
public enum ForceWebsocketMode {
DISABLED(0), ENABLED_BY_USER(1), ENABLED_AUTOMATICALLY(2);
private final int value;
ForceWebsocketMode(int value) {
this.value = value;
}
public boolean isEnabled() {
return this != DISABLED;
}
public int serialize() {
return value;
}
public static ForceWebsocketMode deserialize(int value) {
switch (value) {
case 0:
return DISABLED;
case 1:
return ENABLED_BY_USER;
case 2:
return ENABLED_AUTOMATICALLY;
default:
throw new IllegalArgumentException("Bad value: " + value);
}
}
}
public enum Theme {
SYSTEM("system"), LIGHT("light"), DARK("dark");
private final String value;
Theme(String value) {
this.value = value;
}
public @NonNull String serialize() {
return value;
}
public static @NonNull Theme deserialize(@NonNull String value) {
switch (value) {
case "system":
return SYSTEM;
case "light":
return LIGHT;
case "dark":
return DARK;
default:
throw new IllegalArgumentException("Unrecognized value " + value);
}
}
}
public enum UnreadBadgeType {
UNKNOWN_BADGE_TYPE(0), UNREAD_MESSAGES(1), UNREAD_CHATS(2);
private final int value;
UnreadBadgeType(int value) {
this.value = value;
}
public static UnreadBadgeType deserialize(int value) {
return switch (value) {
case 0 -> UNKNOWN_BADGE_TYPE;
case 1 -> UNREAD_MESSAGES;
case 2 -> UNREAD_CHATS;
default -> throw new IllegalArgumentException("Bad value: " + value);
};
}
public int serialize() {
return value;
}
}
}
@@ -0,0 +1,554 @@
package org.thoughtcrime.securesms.keyvalue
import android.content.Context
import android.net.Uri
import android.provider.Settings
import androidx.lifecycle.LiveData
import org.signal.core.util.logging.Log
import org.signal.mediasend.SentMediaQuality
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference
import org.thoughtcrime.securesms.util.Environment
import org.thoughtcrime.securesms.util.SingleLiveEvent
import org.thoughtcrime.securesms.util.TextSecurePreferences
import org.thoughtcrime.securesms.webrtc.CallDataMode
import java.util.Arrays
import java.util.Random
import kotlin.math.abs
@Suppress("DEPRECATION")
class SettingsValues internal constructor(store: KeyValueStore, context: Context) : SignalStoreValues(store) {
companion object {
private val TAG = Log.tag(SettingsValues::class.java)
const val LINK_PREVIEWS = "settings.link_previews"
const val KEEP_MESSAGES_DURATION = "settings.keep_messages_duration"
const val PREFER_SYSTEM_CONTACT_PHOTOS = "settings.prefer.system.contact.photos"
private const val SIGNAL_BACKUP_DIRECTORY = "settings.signal.backup.directory"
private const val SIGNAL_LATEST_BACKUP_DIRECTORY = "settings.signal.backup.directory,latest"
private const val CALL_DATA_MODE = "settings.signal.call.bandwidth.mode"
const val THREAD_TRIM_LENGTH = "pref_trim_length"
const val THREAD_TRIM_ENABLED = "pref_trim_threads"
const val THEME = "settings.theme"
const val MESSAGE_FONT_SIZE = "settings.message.font.size"
const val LANGUAGE = "settings.language"
const val PREFER_SYSTEM_EMOJI = "settings.use.system.emoji"
const val ENTER_KEY_SENDS = "settings.enter.key.sends"
const val BACKUPS_ENABLED = "settings.backups.enabled"
const val BACKUPS_SCHEDULE_HOUR = "settings.backups.schedule.hour"
const val BACKUPS_SCHEDULE_MINUTE = "settings.backups.schedule.minute"
const val SIGNAL_BACKUPS_SCHEDULE_HOUR = "settings.signal.backups.schedule.hour"
const val SIGNAL_BACKUPS_SCHEDULE_MINUTE = "settings.signal.backups.schedule.minute"
const val SMS_DELIVERY_REPORTS_ENABLED = "settings.sms.delivery.reports.enabled"
const val WIFI_CALLING_COMPATIBILITY_MODE_ENABLED = "settings.wifi.calling.compatibility.mode.enabled"
const val MESSAGE_NOTIFICATIONS_ENABLED = "settings.message.notifications.enabled"
const val MESSAGE_NOTIFICATION_SOUND = "settings.message.notifications.sound"
const val MESSAGE_VIBRATE_ENABLED = "settings.message.vibrate.enabled"
const val MESSAGE_LED_COLOR = "settings.message.led.color"
const val MESSAGE_LED_BLINK_PATTERN = "settings.message.led.blink"
const val MESSAGE_IN_CHAT_SOUNDS_ENABLED = "settings.message.in.chats.sounds.enabled"
const val MESSAGE_REPEAT_ALERTS = "settings.message.repeat.alerts"
const val MESSAGE_NOTIFICATION_PRIVACY = "settings.message.notification.privacy"
const val MESSAGE_NOTIFICATION_REACTION = "settings.message.notification.reaction"
const val MESSAGE_NOTIFICATION_MUTED_CALLS = "settings.message.notifications.muted.call"
const val MESSAGE_NOTIFICATION_MUTED_MENTIONS = "settings.message.notifications.muted.mentions"
const val MESSAGE_NOTIFICATION_MUTED_REPLIES = "settings.message.notifications.muted.replies"
const val UNREAD_REMINDER_ENABLED = "settings.message.notifications.unread.reminder"
const val UNREAD_REMINDER_NEXT_NOTIFY_TIME = "settings.message.notifications.unread.reminder.next.notify.time"
const val UNREAD_BADGE_TYPE = "settings.notifications.badge.type"
const val INCLUDE_MUTED_IN_BADGE_COUNT = "settings.notifications.include.muted.badge.count"
const val CALL_NOTIFICATIONS_ENABLED = "settings.call.notifications.enabled"
const val CALL_RINGTONE = "settings.call.ringtone"
const val CALL_VIBRATE_ENABLED = "settings.call.vibrate.enabled"
const val NOTIFY_WHEN_CONTACT_JOINS_SIGNAL = "settings.notify.when.contact.joins.signal"
private const val UNIVERSAL_EXPIRE_TIMER = "settings.universal.expire.timer"
private const val SENT_MEDIA_QUALITY = "settings.sentMediaQuality"
private const val CENSORSHIP_CIRCUMVENTION_ENABLED = "settings.censorshipCircumventionEnabled"
private const val KEEP_MUTED_CHATS_ARCHIVED = "settings.keepMutedChatsArchived"
private const val USE_COMPACT_NAVIGATION_BAR = "settings.useCompactNavigationBar"
private const val THREAD_TRIM_SYNC_TO_LINKED_DEVICES = "settings.storage.syncThreadTrimDeletes"
private const val PASSPHRASE_DISABLED = "settings.passphrase.disabled"
private const val PASSPHRASE_TIMEOUT_ENABLED = "settings.passphrase.timeout.enabled"
private const val PASSPHRASE_TIMEOUT = "settings.passphrase.timeout"
private const val SCREEN_LOCK_ENABLED = "settings.screen.lock.enabled"
private const val SCREEN_LOCK_TIMEOUT = "settings.screen.lock.timeout"
private const val AUTOMATIC_VERIFICATION_ENABLED = "settings.automatic.verification.enabled"
private const val FORCE_WEBSOCKET_MODE = "settings.force.websocket.mode.2"
const val BACKUP_DEFAULT_HOUR = 2
const val BACKUP_DEFAULT_MINUTE = 0
}
private val configurationSettingChanged = SingleLiveEvent<String>()
init {
if (!store.containsKey(SCREEN_LOCK_ENABLED) && !Environment.IS_INSTRUMENTATION) {
migrateFromSharedPrefsV1(context)
}
}
public override fun onFirstEverAppLaunch() {
if (!store.containsKey(LINK_PREVIEWS)) {
isLinkPreviewsEnabled = true
}
if (!store.containsKey(BACKUPS_SCHEDULE_HOUR)) {
// Initialize backup time to a 5min interval between 1-5am
setBackupSchedule(Random().nextInt(5) + 1, Random().nextInt(12) * 5)
}
if (!store.containsKey(SIGNAL_BACKUPS_SCHEDULE_HOUR)) {
initSignalBackupsSchedule()
}
}
public override fun getKeysToIncludeInBackup(): List<String> = listOf(
LINK_PREVIEWS,
KEEP_MESSAGES_DURATION,
PREFER_SYSTEM_CONTACT_PHOTOS,
CALL_DATA_MODE,
THREAD_TRIM_LENGTH,
THREAD_TRIM_ENABLED,
LANGUAGE,
THEME,
MESSAGE_FONT_SIZE,
PREFER_SYSTEM_EMOJI,
ENTER_KEY_SENDS,
BACKUPS_ENABLED,
MESSAGE_NOTIFICATIONS_ENABLED,
MESSAGE_NOTIFICATION_SOUND,
MESSAGE_VIBRATE_ENABLED,
MESSAGE_LED_COLOR,
MESSAGE_LED_BLINK_PATTERN,
MESSAGE_IN_CHAT_SOUNDS_ENABLED,
MESSAGE_REPEAT_ALERTS,
MESSAGE_NOTIFICATION_PRIVACY,
CALL_NOTIFICATIONS_ENABLED,
CALL_RINGTONE,
CALL_VIBRATE_ENABLED,
NOTIFY_WHEN_CONTACT_JOINS_SIGNAL,
UNIVERSAL_EXPIRE_TIMER,
SENT_MEDIA_QUALITY,
KEEP_MUTED_CHATS_ARCHIVED,
USE_COMPACT_NAVIGATION_BAR,
THREAD_TRIM_SYNC_TO_LINKED_DEVICES,
PASSPHRASE_DISABLED,
PASSPHRASE_TIMEOUT_ENABLED,
PASSPHRASE_TIMEOUT,
SCREEN_LOCK_ENABLED,
SCREEN_LOCK_TIMEOUT
)
val onConfigurationSettingChanged: LiveData<String>
get() = configurationSettingChanged
var isLinkPreviewsEnabled: Boolean by booleanValue(LINK_PREVIEWS, false)
var keepMessagesDuration: KeepMessagesDuration
get() = KeepMessagesDuration.fromId(getInteger(KEEP_MESSAGES_DURATION, 0))
set(value) {
putInteger(KEEP_MESSAGES_DURATION, value.id)
}
var isTrimByLengthEnabled: Boolean by booleanValue(THREAD_TRIM_ENABLED, false)
var threadTrimLength: Int by integerValue(THREAD_TRIM_LENGTH, 500)
var syncThreadTrimDeletes: Boolean
get() {
if (!store.containsKey(THREAD_TRIM_SYNC_TO_LINKED_DEVICES)) {
syncThreadTrimDeletes = !isTrimByLengthEnabled && keepMessagesDuration == KeepMessagesDuration.FOREVER
}
return getBoolean(THREAD_TRIM_SYNC_TO_LINKED_DEVICES, true) && SignalStore.account.isPrimaryDevice
}
set(value) {
putBoolean(THREAD_TRIM_SYNC_TO_LINKED_DEVICES, value)
}
var signalBackupDirectory: Uri?
get() = getUri(SIGNAL_BACKUP_DIRECTORY)
set(value) {
putString(SIGNAL_BACKUP_DIRECTORY, value?.toString())
// The latest directory is intentionally left in place when clearing so we can re-offer the last-used location.
if (value != null) {
putString(SIGNAL_LATEST_BACKUP_DIRECTORY, value.toString())
}
}
val latestSignalBackupDirectory: Uri?
get() = getUri(SIGNAL_LATEST_BACKUP_DIRECTORY)
var isPreferSystemContactPhotos: Boolean by booleanValue(PREFER_SYSTEM_CONTACT_PHOTOS, false)
var callDataMode: CallDataMode
get() = CallDataMode.fromCode(getInteger(CALL_DATA_MODE, CallDataMode.HIGH_ALWAYS.code))
set(value) {
putInteger(CALL_DATA_MODE, value.code)
}
var theme: Theme
get() = Theme.deserialize(getString(THEME, TextSecurePreferences.getTheme(AppDependencies.application)))
set(value) {
putString(THEME, value.serialize())
configurationSettingChanged.postValue(THEME)
}
var messageFontSize: Int
get() = getInteger(MESSAGE_FONT_SIZE, TextSecurePreferences.getMessageBodyTextSize(AppDependencies.application))
set(value) {
putInteger(MESSAGE_FONT_SIZE, value)
}
fun getMessageQuoteFontSize(context: Context): Int {
val currentMessageSize = messageFontSize
val possibleMessageSizes = context.resources.getIntArray(R.array.pref_message_font_size_values)
val possibleQuoteSizes = context.resources.getIntArray(R.array.pref_message_font_quote_size_values)
var sizeIndex = Arrays.binarySearch(possibleMessageSizes, currentMessageSize)
if (sizeIndex < 0) {
val newSize = possibleMessageSizes.minBy { abs(it - currentMessageSize) }
Log.w(TAG, "Using non-standard font size of $currentMessageSize. Closest match was $newSize. Updating.")
messageFontSize = newSize
sizeIndex = Arrays.binarySearch(possibleMessageSizes, newSize)
}
return possibleQuoteSizes[sizeIndex]
}
var language: String
get() = TextSecurePreferences.getLanguage(AppDependencies.application)
set(value) {
TextSecurePreferences.setLanguage(AppDependencies.application, value)
configurationSettingChanged.postValue(LANGUAGE)
}
var isPreferSystemEmoji: Boolean
get() = getBoolean(PREFER_SYSTEM_EMOJI, TextSecurePreferences.isSystemEmojiPreferred(AppDependencies.application))
set(value) {
putBoolean(PREFER_SYSTEM_EMOJI, value)
}
var isEnterKeySends: Boolean
get() = getBoolean(ENTER_KEY_SENDS, TextSecurePreferences.isEnterSendsEnabled(AppDependencies.application))
set(value) {
putBoolean(ENTER_KEY_SENDS, value)
}
var isBackupEnabled: Boolean
get() = getBoolean(BACKUPS_ENABLED, TextSecurePreferences.isBackupEnabled(AppDependencies.application))
set(value) {
putBoolean(BACKUPS_ENABLED, value)
}
val backupHour: Int
get() = getInteger(BACKUPS_SCHEDULE_HOUR, BACKUP_DEFAULT_HOUR)
val backupMinute: Int
get() = getInteger(BACKUPS_SCHEDULE_MINUTE, BACKUP_DEFAULT_MINUTE)
val signalBackupHour: Int
get() {
val hour = getInteger(SIGNAL_BACKUPS_SCHEDULE_HOUR, -1)
return if (hour < 0) {
initSignalBackupsSchedule()
getInteger(SIGNAL_BACKUPS_SCHEDULE_HOUR, BACKUP_DEFAULT_HOUR)
} else {
hour
}
}
val signalBackupMinute: Int
get() {
val minute = getInteger(SIGNAL_BACKUPS_SCHEDULE_MINUTE, -1)
return if (minute < 0) {
initSignalBackupsSchedule()
getInteger(SIGNAL_BACKUPS_SCHEDULE_MINUTE, BACKUP_DEFAULT_MINUTE)
} else {
minute
}
}
fun setBackupSchedule(hour: Int, minute: Int) {
putInteger(BACKUPS_SCHEDULE_HOUR, hour)
putInteger(BACKUPS_SCHEDULE_MINUTE, minute)
}
private fun initSignalBackupsSchedule() {
setSignalBackupSchedule(Random().nextInt(5) + 1, Random().nextInt(12) * 5)
}
fun setSignalBackupSchedule(hour: Int, minute: Int) {
putInteger(SIGNAL_BACKUPS_SCHEDULE_HOUR, hour)
putInteger(SIGNAL_BACKUPS_SCHEDULE_MINUTE, minute)
}
var isSmsDeliveryReportsEnabled: Boolean
get() = getBoolean(SMS_DELIVERY_REPORTS_ENABLED, TextSecurePreferences.isSmsDeliveryReportsEnabled(AppDependencies.application))
set(value) {
putBoolean(SMS_DELIVERY_REPORTS_ENABLED, value)
}
var isWifiCallingCompatibilityModeEnabled: Boolean
get() = getBoolean(WIFI_CALLING_COMPATIBILITY_MODE_ENABLED, TextSecurePreferences.isWifiSmsEnabled(AppDependencies.application))
set(value) {
putBoolean(WIFI_CALLING_COMPATIBILITY_MODE_ENABLED, value)
}
var isMessageNotificationsEnabled: Boolean
get() = getBoolean(MESSAGE_NOTIFICATIONS_ENABLED, TextSecurePreferences.isNotificationsEnabled(AppDependencies.application))
set(value) {
putBoolean(MESSAGE_NOTIFICATIONS_ENABLED, value)
}
var messageNotificationSound: Uri
get() {
var result = getString(MESSAGE_NOTIFICATION_SOUND, TextSecurePreferences.getNotificationRingtone(AppDependencies.application).toString())
if (result.startsWith("file:")) {
result = Settings.System.DEFAULT_NOTIFICATION_URI.toString()
}
return Uri.parse(result)
}
set(value) {
putString(MESSAGE_NOTIFICATION_SOUND, value.toString())
}
var isMessageVibrateEnabled: Boolean
get() = getBoolean(MESSAGE_VIBRATE_ENABLED, TextSecurePreferences.isNotificationVibrateEnabled(AppDependencies.application))
set(value) {
putBoolean(MESSAGE_VIBRATE_ENABLED, value)
}
var messageLedColor: String
get() = getString(MESSAGE_LED_COLOR, TextSecurePreferences.getNotificationLedColor(AppDependencies.application))
set(value) {
putString(MESSAGE_LED_COLOR, value)
}
var messageLedBlinkPattern: String
get() = getString(MESSAGE_LED_BLINK_PATTERN, TextSecurePreferences.getNotificationLedPattern(AppDependencies.application))
set(value) {
putString(MESSAGE_LED_BLINK_PATTERN, value)
}
var isMessageNotificationsInChatSoundsEnabled: Boolean
get() = getBoolean(MESSAGE_IN_CHAT_SOUNDS_ENABLED, TextSecurePreferences.isInThreadNotifications(AppDependencies.application))
set(value) {
putBoolean(MESSAGE_IN_CHAT_SOUNDS_ENABLED, value)
}
var messageNotificationsRepeatAlerts: Int
get() = getInteger(MESSAGE_REPEAT_ALERTS, TextSecurePreferences.getRepeatAlertsCount(AppDependencies.application))
set(value) {
putInteger(MESSAGE_REPEAT_ALERTS, value)
}
var messageNotificationsPrivacy: NotificationPrivacyPreference
get() = NotificationPrivacyPreference(getString(MESSAGE_NOTIFICATION_PRIVACY, TextSecurePreferences.getNotificationPrivacy(AppDependencies.application).toString()))
set(value) {
putString(MESSAGE_NOTIFICATION_PRIVACY, value.toString())
}
var reactionNotifications: Boolean by booleanValue(MESSAGE_NOTIFICATION_REACTION, true)
var allowCallsWhileMuted: Boolean by booleanValue(MESSAGE_NOTIFICATION_MUTED_CALLS, false)
var allowMentionsWhileMuted: Boolean by booleanValue(MESSAGE_NOTIFICATION_MUTED_MENTIONS, true)
var allowRepliesWhileMuted: Boolean by booleanValue(MESSAGE_NOTIFICATION_MUTED_REPLIES, true)
var unreadReminderEnabled: Boolean by booleanValue(UNREAD_REMINDER_ENABLED, true)
// TODO(michelle): Use in unread job
var unreadReminderNextNotifyTime: Long by longValue(UNREAD_REMINDER_NEXT_NOTIFY_TIME, 0L)
val unreadBadgeType: UnreadBadgeType
get() = UnreadBadgeType.deserialize(getInteger(UNREAD_BADGE_TYPE, UnreadBadgeType.UNREAD_MESSAGES.serialize()))
/** Only used to round trip ios/desktop settings. */
fun setUnreadBadgeType(type: Int) {
putInteger(UNREAD_BADGE_TYPE, type)
}
val includeMutedInBadgeCount: Boolean?
get() = if (store.containsKey(INCLUDE_MUTED_IN_BADGE_COUNT)) getBoolean(INCLUDE_MUTED_IN_BADGE_COUNT, false) else null
/** Only used to round trip ios/desktop settings. */
fun setIncludeMutedInBadgeCount(include: Boolean) {
putBoolean(INCLUDE_MUTED_IN_BADGE_COUNT, include)
}
var isCallNotificationsEnabled: Boolean
get() = getBoolean(CALL_NOTIFICATIONS_ENABLED, TextSecurePreferences.isCallNotificationsEnabled(AppDependencies.application))
set(value) {
putBoolean(CALL_NOTIFICATIONS_ENABLED, value)
}
var callRingtone: Uri
get() {
var result = getString(CALL_RINGTONE, TextSecurePreferences.getCallNotificationRingtone(AppDependencies.application).toString())
if (result != null && result.startsWith("file:")) {
result = Settings.System.DEFAULT_RINGTONE_URI.toString()
}
return Uri.parse(result)
}
set(value) {
putString(CALL_RINGTONE, value.toString())
}
var isCallVibrateEnabled: Boolean
get() = getBoolean(CALL_VIBRATE_ENABLED, TextSecurePreferences.isCallNotificationVibrateEnabled(AppDependencies.application))
set(value) {
putBoolean(CALL_VIBRATE_ENABLED, value)
}
var isNotifyWhenContactJoinsSignal: Boolean
get() = getBoolean(NOTIFY_WHEN_CONTACT_JOINS_SIGNAL, TextSecurePreferences.isNewContactsNotificationEnabled(AppDependencies.application))
set(value) {
putBoolean(NOTIFY_WHEN_CONTACT_JOINS_SIGNAL, value)
}
var universalExpireTimer: Int by integerValue(UNIVERSAL_EXPIRE_TIMER, 0)
var sentMediaQuality: SentMediaQuality
get() = SentMediaQuality.fromCode(getInteger(SENT_MEDIA_QUALITY, SentMediaQuality.STANDARD.code))
set(value) {
putInteger(SENT_MEDIA_QUALITY, value.code)
}
val censorshipCircumventionEnabled: CensorshipCircumventionEnabled
get() = CensorshipCircumventionEnabled.deserialize(getInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, CensorshipCircumventionEnabled.DEFAULT.serialize()))
fun setCensorshipCircumventionEnabled(enabled: Boolean) {
Log.i(TAG, "Changing censorship circumvention state to: $enabled", Throwable())
putInteger(CENSORSHIP_CIRCUMVENTION_ENABLED, if (enabled) CensorshipCircumventionEnabled.ENABLED.serialize() else CensorshipCircumventionEnabled.DISABLED.serialize())
}
var keepMutedChatsArchived: Boolean by booleanValue(KEEP_MUTED_CHATS_ARCHIVED, false)
var useCompactNavigationBar: Boolean by booleanValue(USE_COMPACT_NAVIGATION_BAR, false)
var passphraseDisabled: Boolean by booleanValue(PASSPHRASE_DISABLED, true)
var passphraseTimeoutEnabled: Boolean by booleanValue(PASSPHRASE_TIMEOUT_ENABLED, false)
var passphraseTimeout: Int by integerValue(PASSPHRASE_TIMEOUT, 0)
var screenLockEnabled: Boolean by booleanValue(SCREEN_LOCK_ENABLED, false)
var screenLockTimeout: Long by longValue(SCREEN_LOCK_TIMEOUT, 0)
var automaticVerificationEnabled: Boolean
get() = getBoolean(AUTOMATIC_VERIFICATION_ENABLED, true)
set(value) {
Log.i(TAG, "Setting key transparency enabled to $value")
putBoolean(AUTOMATIC_VERIFICATION_ENABLED, value)
}
var forceWebsocketMode: ForceWebsocketMode
get() {
return if (store.containsKey(FORCE_WEBSOCKET_MODE)) {
ForceWebsocketMode.deserialize(getInteger(FORCE_WEBSOCKET_MODE, ForceWebsocketMode.DISABLED.serialize()))
} else if (getBoolean(FORCE_WEBSOCKET_MODE, false)) {
ForceWebsocketMode.ENABLED_BY_USER
} else {
ForceWebsocketMode.DISABLED
}
}
set(value) {
putInteger(FORCE_WEBSOCKET_MODE, value.serialize())
}
private fun getUri(key: String): Uri? {
val uri = getString(key, "")
return if (uri.isNullOrEmpty()) {
null
} else {
Uri.parse(uri)
}
}
private fun migrateFromSharedPrefsV1(context: Context) {
Log.i(TAG, "[V1] Migrating screen lock values from shared prefs.")
putBoolean(PASSPHRASE_DISABLED, TextSecurePreferences.getBooleanPreference(context, "pref_disable_passphrase", true))
putBoolean(PASSPHRASE_TIMEOUT_ENABLED, TextSecurePreferences.getBooleanPreference(context, "pref_timeout_passphrase", false))
putInteger(PASSPHRASE_TIMEOUT, TextSecurePreferences.getIntegerPreference(context, "pref_timeout_interval", 5 * 60))
putBoolean(SCREEN_LOCK_ENABLED, TextSecurePreferences.getBooleanPreference(context, "pref_android_screen_lock", false))
putLong(SCREEN_LOCK_TIMEOUT, TextSecurePreferences.getLongPreference(context, "pref_android_screen_lock_timeout", 0))
}
enum class CensorshipCircumventionEnabled(private val value: Int) {
DEFAULT(0),
ENABLED(1),
DISABLED(2);
fun serialize(): Int = value
companion object {
@JvmStatic
fun deserialize(value: Int): CensorshipCircumventionEnabled {
return entries.firstOrNull { it.value == value } ?: throw IllegalArgumentException("Bad value: $value")
}
}
}
enum class ForceWebsocketMode(private val value: Int) {
DISABLED(0),
ENABLED_BY_USER(1),
ENABLED_AUTOMATICALLY(2);
val isEnabled: Boolean
get() = this != DISABLED
fun serialize(): Int = value
companion object {
@JvmStatic
fun deserialize(value: Int): ForceWebsocketMode {
return entries.firstOrNull { it.value == value } ?: throw IllegalArgumentException("Bad value: $value")
}
}
}
enum class Theme(private val value: String) {
SYSTEM("system"),
LIGHT("light"),
DARK("dark");
fun serialize(): String = value
companion object {
@JvmStatic
fun deserialize(value: String): Theme {
return entries.firstOrNull { it.value == value } ?: throw IllegalArgumentException("Unrecognized value $value")
}
}
}
enum class UnreadBadgeType(private val value: Int) {
UNKNOWN_BADGE_TYPE(0),
UNREAD_MESSAGES(1),
UNREAD_CHATS(2);
fun serialize(): Int = value
companion object {
@JvmStatic
fun deserialize(value: Int): UnreadBadgeType {
return entries.firstOrNull { it.value == value } ?: throw IllegalArgumentException("Bad value: $value")
}
}
}
}
@@ -37,7 +37,7 @@ public class TrimByLengthSettingsMigrationJob extends MigrationJob {
void performMigration() throws Exception {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(AppDependencies.getApplication());
if (preferences.contains(THREAD_TRIM_ENABLED)) {
SignalStore.settings().setThreadTrimByLengthEnabled(preferences.getBoolean(THREAD_TRIM_ENABLED, false));
SignalStore.settings().setTrimByLengthEnabled(preferences.getBoolean(THREAD_TRIM_ENABLED, false));
//noinspection ConstantConditions
SignalStore.settings().setThreadTrimLength(Integer.parseInt(preferences.getString(THREAD_TRIM_LENGTH, "500")));
@@ -890,7 +890,7 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(rootUri, takeFlags)
SignalStore.settings.setSignalBackupDirectory(rootUri)
SignalStore.settings.signalBackupDirectory = rootUri
if (BackupUtil.canUserAccessBackupDirectory(context)) {
LocalBackupListener.setNextBackupTimeToIntervalFromNow(context)
@@ -70,7 +70,7 @@ class RestoreLocalBackupFragment : LoggingFragment(R.layout.fragment_restore_loc
Log.i(TAG, "Re-enabling backups with new directory")
val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
SignalStore.settings.setSignalBackupDirectory(backupDirectoryUri)
SignalStore.settings.signalBackupDirectory = backupDirectoryUri
requireContext().contentResolver.takePersistableUriPermission(backupDirectoryUri, takeFlags)
enableLocalBackups(requireContext())
@@ -180,7 +180,7 @@ object StorageSyncHelper {
preferredReactionEmoji = SignalStore.emoji.reactions
displayBadgesOnProfile = SignalStore.inAppPayments.getDisplayBadgesOnProfile()
subscriptionManuallyCancelled = isUserManuallyCancelled(InAppPaymentSubscriberRecord.Type.DONATION)
keepMutedChatsArchived = SignalStore.settings.shouldKeepMutedChatsArchived()
keepMutedChatsArchived = SignalStore.settings.keepMutedChatsArchived
hasSetMyStoriesPrivacy = SignalStore.story.userHasBeenNotifiedAboutStories
hasViewedOnboardingStory = SignalStore.story.userHasViewedOnboardingStory
storiesDisabled = SignalStore.story.isFeatureDisabled
@@ -280,7 +280,7 @@ object StorageSyncHelper {
SignalStore.settings.universalExpireTimer = update.new.proto.universalExpireTimer
SignalStore.emoji.reactions = update.new.proto.preferredReactionEmoji
SignalStore.inAppPayments.setDisplayBadgesOnProfile(update.new.proto.displayBadgesOnProfile)
SignalStore.settings.setKeepMutedChatsArchived(update.new.proto.keepMutedChatsArchived)
SignalStore.settings.keepMutedChatsArchived = update.new.proto.keepMutedChatsArchived
SignalStore.story.userHasBeenNotifiedAboutStories = update.new.proto.hasSetMyStoriesPrivacy
SignalStore.story.userHasViewedOnboardingStory = update.new.proto.hasViewedOnboardingStory
SignalStore.story.isFeatureDisabled = update.new.proto.storiesDisabled
@@ -161,7 +161,7 @@ public class BackupUtil {
return;
}
SignalStore.settings().clearSignalBackupDirectory();
SignalStore.settings().setSignalBackupDirectory(null);
try {
context.getContentResolver()
@@ -167,7 +167,7 @@ class FakeStorageServiceRule(val storageKey: StorageKey = StorageKey(Util.getSec
every { store.settings.isLinkPreviewsEnabled } returns true
every { store.settings.isPreferSystemContactPhotos } returns false
every { store.settings.universalExpireTimer } returns 0
every { store.settings.shouldKeepMutedChatsArchived() } returns false
every { store.settings.keepMutedChatsArchived } returns false
every { store.settings.automaticVerificationEnabled } returns true
mockkObject(RemoteConfig)