Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
46f5a5b586
commit
9b4afc4f59
76
TheNoiseClock/Services/BatteryService.swift
Normal file
76
TheNoiseClock/Services/BatteryService.swift
Normal file
@ -0,0 +1,76 @@
|
||||
//
|
||||
// BatteryService.swift
|
||||
// TheNoiseClock
|
||||
//
|
||||
// Created by Matt Bruce on 9/7/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import UIKit
|
||||
|
||||
/// Service for monitoring device battery level and state
|
||||
@Observable
|
||||
class BatteryService {
|
||||
|
||||
// MARK: - Properties
|
||||
static let shared = BatteryService()
|
||||
|
||||
var batteryLevel: Int = 100
|
||||
var isCharging: Bool = false
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
// MARK: - Initialization
|
||||
private init() {
|
||||
setupBatteryMonitoring()
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
func startMonitoring() {
|
||||
#if canImport(UIKit)
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
updateBatteryInfo()
|
||||
#endif
|
||||
}
|
||||
|
||||
func stopMonitoring() {
|
||||
#if canImport(UIKit)
|
||||
UIDevice.current.isBatteryMonitoringEnabled = false
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
private func setupBatteryMonitoring() {
|
||||
#if canImport(UIKit)
|
||||
// Listen for battery level changes
|
||||
NotificationCenter.default.publisher(for: UIDevice.batteryLevelDidChangeNotification)
|
||||
.sink { [weak self] _ in
|
||||
self?.updateBatteryInfo()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
// Listen for battery state changes
|
||||
NotificationCenter.default.publisher(for: UIDevice.batteryStateDidChangeNotification)
|
||||
.sink { [weak self] _ in
|
||||
self?.updateBatteryInfo()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func updateBatteryInfo() {
|
||||
#if canImport(UIKit)
|
||||
let device = UIDevice.current
|
||||
|
||||
// Update battery level
|
||||
let level = device.batteryLevel
|
||||
if level >= 0 {
|
||||
batteryLevel = Int((level * 100).rounded())
|
||||
}
|
||||
|
||||
// Update charging state
|
||||
isCharging = device.batteryState == .charging
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -14,8 +14,7 @@ struct BatteryOverlayView: View {
|
||||
// MARK: - Properties
|
||||
let color: Color
|
||||
let opacity: Double
|
||||
|
||||
@State private var batteryLevel: Int = 100
|
||||
let batteryLevel: Int
|
||||
|
||||
// MARK: - Body
|
||||
var body: some View {
|
||||
@ -31,27 +30,10 @@ struct BatteryOverlayView: View {
|
||||
}
|
||||
.opacity(clamped)
|
||||
.font(.callout.weight(.semibold))
|
||||
.onAppear {
|
||||
enableBatteryMonitoring()
|
||||
updateBattery()
|
||||
startBatteryObserver()
|
||||
}
|
||||
.onDisappear {
|
||||
stopBatteryObserver()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
private func getBatteryIcon() -> String {
|
||||
#if canImport(UIKit)
|
||||
let batteryState = UIDevice.current.batteryState
|
||||
|
||||
// Check if device is charging
|
||||
if batteryState == .charging {
|
||||
return "bolt.circle.fill"
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return battery icon based on level
|
||||
switch batteryLevel {
|
||||
case 75...100:
|
||||
@ -68,15 +50,6 @@ struct BatteryOverlayView: View {
|
||||
}
|
||||
|
||||
private func getBatteryColor() -> Color {
|
||||
#if canImport(UIKit)
|
||||
let batteryState = UIDevice.current.batteryState
|
||||
|
||||
// Green when charging
|
||||
if batteryState == .charging {
|
||||
return .green
|
||||
}
|
||||
#endif
|
||||
|
||||
// Color based on battery level
|
||||
switch batteryLevel {
|
||||
case 50...100:
|
||||
@ -89,54 +62,164 @@ struct BatteryOverlayView: View {
|
||||
return .red
|
||||
}
|
||||
}
|
||||
|
||||
private func enableBatteryMonitoring() {
|
||||
#if canImport(UIKit)
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
#endif
|
||||
}
|
||||
|
||||
private func updateBattery() {
|
||||
#if canImport(UIKit)
|
||||
let level = UIDevice.current.batteryLevel
|
||||
if level >= 0 {
|
||||
batteryLevel = Int((level * 100).rounded())
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func startBatteryObserver() {
|
||||
#if canImport(UIKit)
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.batteryLevelDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { _ in
|
||||
updateBattery()
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: UIDevice.batteryStateDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { _ in
|
||||
updateBattery()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func stopBatteryObserver() {
|
||||
#if canImport(UIKit)
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: UIDevice.batteryLevelDidChangeNotification,
|
||||
object: nil
|
||||
// MARK: - Previews
|
||||
#Preview("Battery Overlay - Full Charge") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 100
|
||||
)
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: UIDevice.batteryStateDidChangeNotification,
|
||||
object: nil
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - High Charge") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 75
|
||||
)
|
||||
#endif
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Medium Charge") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 50
|
||||
)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Low Charge") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 25
|
||||
)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Critical Charge") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 10
|
||||
)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Charging") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 85
|
||||
)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Different Colors") {
|
||||
VStack(spacing: 20) {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 75
|
||||
)
|
||||
|
||||
BatteryOverlayView(
|
||||
color: .blue,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 50
|
||||
)
|
||||
|
||||
BatteryOverlayView(
|
||||
color: .green,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 25
|
||||
)
|
||||
|
||||
BatteryOverlayView(
|
||||
color: .red,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 10
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
.background(Color.black)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Different Opacities") {
|
||||
VStack(spacing: 20) {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 75
|
||||
)
|
||||
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 0.7,
|
||||
batteryLevel: 50
|
||||
)
|
||||
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 0.5,
|
||||
batteryLevel: 25
|
||||
)
|
||||
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 0.3,
|
||||
batteryLevel: 10
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
.background(Color.black)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Dark Background") {
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 75
|
||||
)
|
||||
.padding()
|
||||
.background(Color.black)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Light Background") {
|
||||
BatteryOverlayView(
|
||||
color: .black,
|
||||
opacity: 1.0,
|
||||
batteryLevel: 50
|
||||
)
|
||||
.padding()
|
||||
.background(Color.white)
|
||||
}
|
||||
|
||||
#Preview("Battery Overlay - Clock Context") {
|
||||
ZStack {
|
||||
// Simulate clock background
|
||||
Color.black
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
|
||||
// Simulate clock display
|
||||
Text("12:34")
|
||||
.font(.system(size: 80, weight: .bold, design: .rounded))
|
||||
.foregroundColor(.white)
|
||||
|
||||
Spacer()
|
||||
|
||||
// Battery overlay at bottom
|
||||
HStack {
|
||||
Spacer()
|
||||
BatteryOverlayView(
|
||||
color: .white,
|
||||
opacity: 0.8,
|
||||
batteryLevel: 75
|
||||
)
|
||||
Spacer()
|
||||
}
|
||||
.padding(.bottom, 50)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,8 @@ struct TopOverlayView: View {
|
||||
let opacity: Double
|
||||
let dateFormat: String
|
||||
|
||||
@State private var batteryService = BatteryService.shared
|
||||
|
||||
// MARK: - Body
|
||||
var body: some View {
|
||||
HStack {
|
||||
@ -27,12 +29,18 @@ struct TopOverlayView: View {
|
||||
Spacer()
|
||||
|
||||
if showBattery {
|
||||
BatteryOverlayView(color: color, opacity: opacity)
|
||||
BatteryOverlayView(color: color, opacity: opacity, batteryLevel: batteryService.batteryLevel)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
||||
.padding(.vertical, UIConstants.Spacing.small)
|
||||
.cardStyle()
|
||||
.transition(.opacity)
|
||||
.onAppear {
|
||||
batteryService.startMonitoring()
|
||||
}
|
||||
.onDisappear {
|
||||
batteryService.stopMonitoring()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user