TM-SGNL-iOS/SignalServiceKit/Util/Preferences.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

350 lines
12 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
private extension UserDefaults {
static var app: UserDefaults {
CurrentAppContext().appUserDefaults()
}
static func removeAll() {
UserDefaults.standard.removeAll()
app.removeAll()
}
private func removeAll() {
owsAssertDebug(CurrentAppContext().isMainApp)
for (key, _) in self.dictionaryRepresentation() {
self.removeObject(forKey: key)
}
self.synchronize()
}
}
public enum NotificationType: UInt {
case noNameNoPreview = 0
case nameNoPreview = 1
case namePreview = 2
public var displayName: String {
switch self {
case .namePreview:
return OWSLocalizedString("NOTIFICATIONS_SENDER_AND_MESSAGE", comment: "")
case .nameNoPreview:
return OWSLocalizedString("NOTIFICATIONS_SENDER_ONLY", comment: "")
case .noNameNoPreview:
return OWSLocalizedString("NOTIFICATIONS_NONE", comment: "")
}
}
}
@objc(OWSPreferences)
public class Preferences: NSObject {
private enum Key: String {
case screenSecurity = "Screen Security Key"
case notificationPreviewType = "Notification Preview Type Key"
case playSoundInForeground = "NotificationSoundInForeground"
case lastRecordedPushToken = "LastRecordedPushToken"
case callsHideIPAddress = "CallsHideIPAddress"
case hasDeclinedNoContactsView = "hasDeclinedNoContactsView"
case shouldShowUnidentifiedDeliveryIndicators = "OWSPreferencesKeyShouldShowUnidentifiedDeliveryIndicators"
case shouldNotifyOfNewAccountKey = "OWSPreferencesKeyShouldNotifyOfNewAccountKey"
case iOSUpgradeNagDate = "iOSUpgradeNagDate"
case systemCallLogEnabled = "OWSPreferencesKeySystemCallLogEnabled"
case wasViewOnceTooltipShown = "OWSPreferencesKeyWasViewOnceTooltipShown"
case wasDeleteForEveryoneConfirmationShown = "OWSPreferencesKeyWasDeleteForEveryoneConfirmationShown"
case wasBlurTooltipShown = "OWSPreferencesKeyWasBlurTooltipShown"
case wasGroupCallTooltipShown = "OWSPreferencesKeyWasGroupCallTooltipShown"
case wasGroupCallTooltipShownCount = "OWSPreferencesKeyWasGroupCallTooltipShownCount"
// Obsolete
// case callKitEnabled = "CallKitEnabled"
// case callKitPrivacyEnabled = "CallKitPrivacyEnabled"
}
private enum UserDefaultsKeys {
static let deviceScale = "OWSPreferencesKeyDeviceScale"
static let isAudibleErrorLoggingEnabled = "IsAudibleErrorLoggingEnabled"
}
private static let preferencesCollection = "SignalPreferences"
private let keyValueStore = KeyValueStore(collection: Preferences.preferencesCollection)
public override init() {
super.init()
if CurrentAppContext().hasUI {
CurrentAppContext().appUserDefaults().set(UIScreen.main.scale, forKey: UserDefaultsKeys.deviceScale)
}
SwiftSingletons.register(self)
}
// MARK: Helpers
public func removeAllValues() {
UserDefaults.removeAll()
// We don't need to clear our key-value store; database
// storage is cleared otherwise.
}
private func hasValue(forKey key: Key) -> Bool {
let result = SSKEnvironment.shared.databaseStorageRef.read { transaction in
return keyValueStore.hasValue(key.rawValue, transaction: transaction.asV2Read)
}
return result
}
private func removeValue(forKey key: Key) {
SSKEnvironment.shared.databaseStorageRef.write { transaction in
keyValueStore.removeValue(forKey: key.rawValue, transaction: transaction.asV2Write)
}
}
private func bool(forKey key: Key, defaultValue: Bool) -> Bool {
let result = SSKEnvironment.shared.databaseStorageRef.read { transaction in
keyValueStore.getBool(key.rawValue, defaultValue: defaultValue, transaction: transaction.asV2Read)
}
return result
}
private func setBool(_ value: Bool, forKey key: Key) {
SSKEnvironment.shared.databaseStorageRef.write { transaction in
setBool(value, forKey: key, tx: transaction)
}
}
private func setBool(_ value: Bool, forKey key: Key, tx: SDSAnyWriteTransaction) {
keyValueStore.setBool(value, key: key.rawValue, transaction: tx.asV2Write)
}
private func uint(forKey key: Key, defaultValue: UInt) -> UInt {
let result = SSKEnvironment.shared.databaseStorageRef.read { transaction in
keyValueStore.getUInt(key.rawValue, defaultValue: defaultValue, transaction: transaction.asV2Read)
}
return result
}
private func setUInt(_ value: UInt, forKey key: Key) {
SSKEnvironment.shared.databaseStorageRef.write { transaction in
keyValueStore.setUInt(value, key: key.rawValue, transaction: transaction.asV2Write)
}
}
private func date(forKey key: Key) -> Date? {
let date = SSKEnvironment.shared.databaseStorageRef.read { transaction in
keyValueStore.getDate(key.rawValue, transaction: transaction.asV2Read)
}
return date
}
private func setDate(_ value: Date, forKey key: Key) {
SSKEnvironment.shared.databaseStorageRef.write { transaction in
keyValueStore.setDate(value, key: key.rawValue, transaction: transaction.asV2Write)
}
}
private func string(forKey key: Key) -> String? {
return SSKEnvironment.shared.databaseStorageRef.read { tx in getString(for: key, tx: tx) }
}
private func getString(for key: Key, tx: SDSAnyReadTransaction) -> String? {
return keyValueStore.getString(key.rawValue, transaction: tx.asV2Read)
}
private func setString(_ value: String?, forKey key: Key) {
SSKEnvironment.shared.databaseStorageRef.write { tx in setString(value, for: key, tx: tx) }
}
private func setString(_ value: String?, for key: Key, tx: SDSAnyWriteTransaction) {
keyValueStore.setString(value, key: key.rawValue, transaction: tx.asV2Write)
}
// MARK: Logging
@objc
public static var isAudibleErrorLoggingEnabled: Bool {
UserDefaults.app.bool(forKey: UserDefaultsKeys.isAudibleErrorLoggingEnabled) && FeatureFlags.choochoo
}
public static func setIsAudibleErrorLoggingEnabled(_ value: Bool) {
UserDefaults.app.set(value, forKey: UserDefaultsKeys.isAudibleErrorLoggingEnabled)
}
// MARK: Specific Preferences
public var isScreenSecurityEnabled: Bool {
bool(forKey: .screenSecurity, defaultValue: false)
}
public func setIsScreenSecurityEnabled(_ value: Bool) {
setBool(value, forKey: .screenSecurity)
}
public var hasDeclinedNoContactsView: Bool {
bool(forKey: .hasDeclinedNoContactsView, defaultValue: false)
}
public func setHasDeclinedNoContactsView(_ value: Bool) {
setBool(value, forKey: .hasDeclinedNoContactsView)
}
public var iOSUpgradeNagDate: Date? {
date(forKey: .iOSUpgradeNagDate)
}
public func setIOSUpgradeNagDate(_ value: Date) {
setDate(value, forKey: .iOSUpgradeNagDate)
}
@objc
public var shouldShowUnidentifiedDeliveryIndicators: Bool {
bool(forKey: .shouldShowUnidentifiedDeliveryIndicators, defaultValue: false)
}
public func shouldShowUnidentifiedDeliveryIndicators(transaction: SDSAnyReadTransaction) -> Bool {
keyValueStore.getBool(
Key.shouldShowUnidentifiedDeliveryIndicators.rawValue,
defaultValue: false,
transaction: transaction.asV2Read
)
}
public func setShouldShowUnidentifiedDeliveryIndicatorsAndSendSyncMessage(_ value: Bool) {
setBool(value, forKey: .shouldShowUnidentifiedDeliveryIndicators)
SSKEnvironment.shared.syncManagerRef.sendConfigurationSyncMessage()
SSKEnvironment.shared.storageServiceManagerRef.recordPendingLocalAccountUpdates()
}
@objc
public func setShouldShowUnidentifiedDeliveryIndicators(_ value: Bool, transaction: SDSAnyWriteTransaction) {
keyValueStore.setBool(value, key: Key.shouldShowUnidentifiedDeliveryIndicators.rawValue, transaction: transaction.asV2Write)
}
public func shouldNotifyOfNewAccounts(transaction: SDSAnyReadTransaction) -> Bool {
keyValueStore.getBool(Key.shouldNotifyOfNewAccountKey.rawValue, defaultValue: false, transaction: transaction.asV2Read)
}
public func setShouldNotifyOfNewAccounts(_ value: Bool, transaction: SDSAnyWriteTransaction) {
keyValueStore.setBool(value, key: Key.shouldNotifyOfNewAccountKey.rawValue, transaction: transaction.asV2Write)
}
public var cachedDeviceScale: CGFloat {
guard !CurrentAppContext().hasUI else { return UIScreen.main.scale }
guard let cachedValue = CurrentAppContext().appUserDefaults().object(forKey: UserDefaultsKeys.deviceScale) as? CGFloat else {
return UIScreen.main.scale
}
return cachedValue
}
// MARK: Calls
public func isSystemCallLogEnabled(tx: SDSAnyReadTransaction) -> Bool {
return keyValueStore.getBool(Key.systemCallLogEnabled.rawValue, defaultValue: true, transaction: tx.asV2Read)
}
public func setIsSystemCallLogEnabled(_ value: Bool) {
setBool(value, forKey: .systemCallLogEnabled)
}
// Allow callers to connect directly, when desirable, vs. enforcing TURN only proxy connectivity
public var doCallsHideIPAddress: Bool {
bool(forKey: .callsHideIPAddress, defaultValue: false)
}
public func setDoCallsHideIPAddress(_ value: Bool) {
setBool(value, forKey: .callsHideIPAddress)
}
// MARK: UI Tooltips
public var wasViewOnceTooltipShown: Bool {
bool(forKey: .wasViewOnceTooltipShown, defaultValue: false)
}
public func setWasViewOnceTooltipShown() {
setBool(true, forKey: .wasViewOnceTooltipShown)
}
public func wasGroupCallTooltipShown(withTransaction transaction: SDSAnyReadTransaction) -> Bool {
keyValueStore.getBool(Key.wasGroupCallTooltipShown.rawValue, defaultValue: false, transaction: transaction.asV2Read)
}
public func incrementGroupCallTooltipShownCount() {
let currentCount = uint(forKey: .wasGroupCallTooltipShownCount, defaultValue: 0)
let incrementedCount = currentCount + 1
// If we have shown the tooltip more than 3 times, don't show it again.
if incrementedCount > 3 {
SSKEnvironment.shared.databaseStorageRef.write(block: setWasGroupCallTooltipShown(tx:))
} else {
setUInt(incrementedCount, forKey: .wasGroupCallTooltipShownCount)
}
}
public func setWasGroupCallTooltipShown(tx: SDSAnyWriteTransaction) {
setBool(true, forKey: .wasGroupCallTooltipShown, tx: tx)
}
public var wasBlurTooltipShown: Bool {
bool(forKey: .wasBlurTooltipShown, defaultValue: false)
}
public func setWasBlurTooltipShown( ) {
setBool(true, forKey: .wasBlurTooltipShown)
}
public var wasDeleteForEveryoneConfirmationShown: Bool {
bool(forKey: .wasDeleteForEveryoneConfirmationShown, defaultValue: false)
}
public func setWasDeleteForEveryoneConfirmationShown() {
setBool(true, forKey: .wasDeleteForEveryoneConfirmationShown)
}
// MARK: Notification Preferences
public var soundInForeground: Bool {
bool(forKey: .playSoundInForeground, defaultValue: true)
}
public func setSoundInForeground(_ value: Bool) {
setBool(value, forKey: .playSoundInForeground)
}
public func notificationPreviewType(tx: SDSAnyReadTransaction) -> NotificationType {
let rawValue = keyValueStore.getUInt(
Key.notificationPreviewType.rawValue,
transaction: tx.asV2Read
)
return rawValue.flatMap(NotificationType.init(rawValue:)) ?? .namePreview
}
public func setNotificationPreviewType(_ value: NotificationType) {
setUInt(value.rawValue, forKey: .notificationPreviewType)
}
// MARK: Push Tokens
public var pushToken: String? {
string(forKey: .lastRecordedPushToken)
}
public func getPushToken(tx: SDSAnyReadTransaction) -> String? {
return getString(for: .lastRecordedPushToken, tx: tx)
}
public func setPushToken(_ value: String, tx: SDSAnyWriteTransaction) {
setString(value, for: .lastRecordedPushToken, tx: tx)
}
public func unsetRecordedAPNSTokens() {
Logger.warn("Forgetting recorded APNS tokens")
removeValue(forKey: .lastRecordedPushToken)
}
}