Add DeviceBatteryLevelManager

This commit is contained in:
Harry 2025-04-23 09:14:48 -07:00 committed by GitHub
parent 44e6771c8d
commit 25b4f617ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 123 additions and 0 deletions

View File

@ -1190,6 +1190,8 @@
66F6D6A52C7D0E0000EFAF75 /* ColorOrGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F6D6A42C7D0E0000EFAF75 /* ColorOrGradient.swift */; };
66F6D6A72C7D0FF300EFAF75 /* Wallpaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F6D6A62C7D0FF300EFAF75 /* Wallpaper.swift */; };
66F6D6A92C7D106100EFAF75 /* Wallpaper+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F6D6A82C7D106100EFAF75 /* Wallpaper+Constants.swift */; };
66F98DE02DB710F8009F1A86 /* DeviceBatteryLevelManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F98DDF2DB710EA009F1A86 /* DeviceBatteryLevelManager.swift */; };
66F98DE22DB71220009F1A86 /* DeviceBatteryLevelManagerImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F98DE12DB71219009F1A86 /* DeviceBatteryLevelManagerImpl.swift */; };
66FA2B1D28CB0DE1006845CD /* PaymentsBiometryLockPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FA2B1C28CB0DE1006845CD /* PaymentsBiometryLockPromptViewController.swift */; };
66FBC4E128DA820900BD9E8B /* MyStorySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FBC4E028DA820900BD9E8B /* MyStorySettingsViewController.swift */; };
66FBC4E328DA82AA00BD9E8B /* SelectMyStoryRecipientsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66FBC4E228DA82AA00BD9E8B /* SelectMyStoryRecipientsViewController.swift */; };
@ -5097,6 +5099,8 @@
66F6D6A42C7D0E0000EFAF75 /* ColorOrGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorOrGradient.swift; sourceTree = "<group>"; };
66F6D6A62C7D0FF300EFAF75 /* Wallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallpaper.swift; sourceTree = "<group>"; };
66F6D6A82C7D106100EFAF75 /* Wallpaper+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Wallpaper+Constants.swift"; sourceTree = "<group>"; };
66F98DDF2DB710EA009F1A86 /* DeviceBatteryLevelManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceBatteryLevelManager.swift; sourceTree = "<group>"; };
66F98DE12DB71219009F1A86 /* DeviceBatteryLevelManagerImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceBatteryLevelManagerImpl.swift; sourceTree = "<group>"; };
66FA2B1C28CB0DE1006845CD /* PaymentsBiometryLockPromptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentsBiometryLockPromptViewController.swift; sourceTree = "<group>"; };
66FA2B1E28CBA4A5006845CD /* DeviceOwnerAuthenticationType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceOwnerAuthenticationType.swift; sourceTree = "<group>"; };
66FBC4E028DA820900BD9E8B /* MyStorySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyStorySettingsViewController.swift; sourceTree = "<group>"; };
@ -10420,6 +10424,7 @@
4C751BE423FA0284002A8AF1 /* ContactSupportActionSheet.swift */,
50A1CE392A00931900730C40 /* DebugLogger+MainApp.swift */,
34067EAA2710D61A000407C3 /* DebugLogs.swift */,
66F98DE12DB71219009F1A86 /* DeviceBatteryLevelManagerImpl.swift */,
505347F82DA08548004AF0B5 /* DeviceSleepManagerImpl.swift */,
34A95500271B503E00B05242 /* DisplayableText.swift */,
F9D289B5291EDC8D00187394 /* DonationJobError.swift */,
@ -14271,6 +14276,7 @@
F94C912128FDEAF50065DF75 /* Decimal+IsInteger.swift */,
F94C911F28FDEA2E0065DF75 /* Decimal+Rounded.swift */,
F9C5CB79289453B200548EEE /* DecodableDefaults.swift */,
66F98DDF2DB710EA009F1A86 /* DeviceBatteryLevelManager.swift */,
F9C5CB3A289453B200548EEE /* DeviceNames.swift */,
66FA2B1E28CBA4A5006845CD /* DeviceOwnerAuthenticationType.swift */,
348F2EAD1F0D21BC00D4ECE0 /* DeviceSleepManager.swift */,
@ -16868,6 +16874,7 @@
887B6DC925F6C3E900E677D4 /* DeleteAccountConfirmationViewController.swift in Sources */,
D9E8EDF32C0FD8C800923E3C /* DeleteForMeInfoSheetCoordinator.swift in Sources */,
D9E8EDF12C0FCB3000923E3C /* DeleteForMeSyncMessageInfoSheet.swift in Sources */,
66F98DE22DB71220009F1A86 /* DeviceBatteryLevelManagerImpl.swift in Sources */,
C17A54982D7B3DCD00E1D267 /* DeviceProvisioningURL.swift in Sources */,
505347F92DA08548004AF0B5 /* DeviceSleepManagerImpl.swift in Sources */,
887CD4772472FEA500FDD265 /* DeviceTransferOperation.swift in Sources */,
@ -17611,6 +17618,7 @@
D9247EA82BFD28E800DFEF6F /* DeleteForMeSyncMessageReceiver.swift in Sources */,
F9C5CC34289453B300548EEE /* DeliveryReceiptContext.swift in Sources */,
6698FC1A2980AB45004EFC30 /* DependenciesBridge.swift in Sources */,
66F98DE02DB710F8009F1A86 /* DeviceBatteryLevelManager.swift in Sources */,
50F401CC2D483BF40094CA56 /* DeviceId.swift in Sources */,
505C2ED629971D4E00C23FB2 /* DeviceLimitExceededError.swift in Sources */,
50E5E4B129932D9B00E15A1C /* DeviceMessage.swift in Sources */,

View File

@ -183,6 +183,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
MessageFetchBGRefreshTask.register(appReadiness: appReadiness)
let deviceBatteryLevelManager = DeviceBatteryLevelManagerImpl()
let deviceSleepManager = DeviceSleepManagerImpl()
let keychainStorage = KeychainStorageImpl(isUsingProductionService: TSConstants.isUsingProductionService)
let deviceTransferService = DeviceTransferService(
@ -255,6 +256,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
let launchContext = LaunchContext(
appContext: mainAppContext,
databaseStorage: databaseStorage,
deviceBatteryLevelManager: deviceBatteryLevelManager,
deviceSleepManager: deviceSleepManager,
keychainStorage: keychainStorage,
launchStartedAt: launchStartedAt,
@ -368,6 +370,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
private struct LaunchContext {
var appContext: MainAppContext
var databaseStorage: SDSDatabaseStorage
var deviceBatteryLevelManager: DeviceBatteryLevelManagerImpl
var deviceSleepManager: DeviceSleepManagerImpl
var keychainStorage: any KeychainStorage
var launchStartedAt: CFTimeInterval
@ -412,6 +415,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
appContext: launchContext.appContext,
appReadiness: appReadiness,
databaseStorage: launchContext.databaseStorage,
deviceBatteryLevelManager: launchContext.deviceBatteryLevelManager,
deviceSleepManager: launchContext.deviceSleepManager,
paymentsEvents: PaymentsEventsMainApp(),
mobileCoinHelper: MobileCoinHelperSDK(),

View File

@ -0,0 +1,72 @@
//
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
public import SignalServiceKit
public class DeviceBatteryLevelMonitorImpl: DeviceBatteryLevelMonitor {
fileprivate let reason: String
fileprivate var wasReleased: Bool = false
fileprivate init(reason: String) {
self.reason = reason
}
deinit {
if !wasReleased {
owsFailDebug("End battery monitoring before releasing ref!")
}
}
public var batteryLevel: Float {
// On simulators, the battery level always reports -1.0 (invalid).
if Platform.isSimulator {
return 1
} else {
return UIDevice.current.batteryLevel
}
}
public var notification: Notification.Name { UIDevice.batteryLevelDidChangeNotification }
}
public class DeviceBatteryLevelManagerImpl: DeviceBatteryLevelManager {
private var reasons = Set<String>()
public init() {}
public func beginMonitoring(reason: String) -> DeviceBatteryLevelMonitor {
if reasons.isEmpty {
UIDevice.current.isBatteryMonitoringEnabled = true
}
let (duplicate, _) = reasons.insert(reason)
if !duplicate {
owsFailDebug("Duplicate monitoring reason!")
}
return DeviceBatteryLevelMonitorImpl(
reason: reason
)
}
public func endMonitoring(_ monitor: DeviceBatteryLevelMonitor) {
guard let monitor = monitor as? DeviceBatteryLevelMonitorImpl else {
owsFailDebug("Invalid type combination")
return
}
if monitor.wasReleased {
owsFailDebug("Ending monitoring twice!")
}
reasons.remove(monitor.reason)
monitor.wasReleased = true
if reasons.isEmpty {
UIDevice.current.isBatteryMonitoringEnabled = false
}
}
public var isLowPowerModeEnabled: Bool { ProcessInfo.processInfo.isLowPowerModeEnabled }
public var isLowPowerModeChangedNotification: Notification.Name { .NSProcessInfoPowerStateDidChange }
}

View File

@ -144,6 +144,7 @@ class NSEEnvironment {
appContext: CurrentAppContext(),
appReadiness: appReadiness,
databaseStorage: databaseStorage,
deviceBatteryLevelManager: nil,
deviceSleepManager: nil,
paymentsEvents: PaymentsEventsAppExtension(),
mobileCoinHelper: MobileCoinHelperMinimal(),

View File

@ -92,6 +92,7 @@ public class AppSetup {
appContext: AppContext,
appReadiness: AppReadiness,
databaseStorage: SDSDatabaseStorage,
deviceBatteryLevelManager: (any DeviceBatteryLevelManager)?,
deviceSleepManager: (any DeviceSleepManager)?,
paymentsEvents: PaymentsEvents,
mobileCoinHelper: MobileCoinHelper,
@ -948,6 +949,7 @@ public class AppSetup {
backupAttachmentDownloadStore: backupAttachmentDownloadStore,
dateProvider: dateProvider,
db: db,
deviceBatteryLevelManager: deviceBatteryLevelManager,
mediaBandwidthPreferenceStore: mediaBandwidthPreferenceStore,
messageBackupKeyMaterial: messageBackupKeyMaterial,
messageBackupRequestManager: messageBackupRequestManager,

View File

@ -46,6 +46,7 @@ public class BackupAttachmentDownloadManagerImpl: BackupAttachmentDownloadManage
private let backupAttachmentDownloadStore: BackupAttachmentDownloadStore
private let dateProvider: DateProvider
private let db: any DB
private let deviceBatteryLevelManager: (any DeviceBatteryLevelManager)?
private let listMediaManager: ListMediaManager
private let mediaBandwidthPreferenceStore: MediaBandwidthPreferenceStore
private let reachabilityManager: SSKReachabilityManager
@ -63,6 +64,7 @@ public class BackupAttachmentDownloadManagerImpl: BackupAttachmentDownloadManage
backupAttachmentDownloadStore: BackupAttachmentDownloadStore,
dateProvider: @escaping DateProvider,
db: any DB,
deviceBatteryLevelManager: (any DeviceBatteryLevelManager)?,
mediaBandwidthPreferenceStore: MediaBandwidthPreferenceStore,
messageBackupKeyMaterial: MessageBackupKeyMaterial,
messageBackupRequestManager: MessageBackupRequestManager,
@ -76,6 +78,7 @@ public class BackupAttachmentDownloadManagerImpl: BackupAttachmentDownloadManage
self.backupAttachmentDownloadStore = backupAttachmentDownloadStore
self.dateProvider = dateProvider
self.db = db
self.deviceBatteryLevelManager = deviceBatteryLevelManager
self.mediaBandwidthPreferenceStore = mediaBandwidthPreferenceStore
self.reachabilityManager = reachabilityManager
self.remoteConfigProvider = remoteConfigProvider

View File

@ -42,6 +42,7 @@ public class MockSSKEnvironment {
databaseFileUrl: SDSDatabaseStorage.grdbDatabaseFileUrl,
keychainStorage: MockKeychainStorage()
),
deviceBatteryLevelManager: nil,
deviceSleepManager: nil,
paymentsEvents: PaymentsEventsNoop(),
mobileCoinHelper: MobileCoinHelperMock(),

View File

@ -0,0 +1,31 @@
//
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
@MainActor
public protocol DeviceBatteryLevelMonitor {
/// A value from 0 to 1
var batteryLevel: Float { get }
/// Notification fired on NotificationCenter.default when the battery level changes.
var notification: Notification.Name { get }
}
@MainActor
public protocol DeviceBatteryLevelManager {
/// Setting `UIDevice.isBatteryMonitoringEnabled` incurs a cost and Apple would like we
/// only do it as strictly needed. To manage this app wide, use a unique reason string. As long
/// as some monitor is active, battery monitoring will be enabled (thus allowing checking the
/// current battery level and getting battery level change notifications).
func beginMonitoring(reason: String) -> DeviceBatteryLevelMonitor
/// MUST call this before releasing the reference to the DeviceBatteryLevelMonitor from `beginMonitoring`.
func endMonitoring(_ monitor: DeviceBatteryLevelMonitor)
var isLowPowerModeEnabled: Bool { get }
var isLowPowerModeChangedNotification: Notification.Name { get }
}

View File

@ -59,6 +59,7 @@ public class ShareViewController: UIViewController, ShareViewDelegate, SAEFailed
appContext: appContext,
appReadiness: appReadiness,
databaseStorage: databaseStorage,
deviceBatteryLevelManager: nil,
deviceSleepManager: nil,
paymentsEvents: PaymentsEventsAppExtension(),
mobileCoinHelper: MobileCoinHelperMinimal(),