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
|
// MARK: - Properties
|
||||||
let color: Color
|
let color: Color
|
||||||
let opacity: Double
|
let opacity: Double
|
||||||
|
let batteryLevel: Int
|
||||||
@State private var batteryLevel: Int = 100
|
|
||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -31,27 +30,10 @@ struct BatteryOverlayView: View {
|
|||||||
}
|
}
|
||||||
.opacity(clamped)
|
.opacity(clamped)
|
||||||
.font(.callout.weight(.semibold))
|
.font(.callout.weight(.semibold))
|
||||||
.onAppear {
|
|
||||||
enableBatteryMonitoring()
|
|
||||||
updateBattery()
|
|
||||||
startBatteryObserver()
|
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
stopBatteryObserver()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
private func getBatteryIcon() -> String {
|
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
|
// Return battery icon based on level
|
||||||
switch batteryLevel {
|
switch batteryLevel {
|
||||||
case 75...100:
|
case 75...100:
|
||||||
@ -68,15 +50,6 @@ struct BatteryOverlayView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func getBatteryColor() -> Color {
|
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
|
// Color based on battery level
|
||||||
switch batteryLevel {
|
switch batteryLevel {
|
||||||
case 50...100:
|
case 50...100:
|
||||||
@ -89,54 +62,164 @@ struct BatteryOverlayView: View {
|
|||||||
return .red
|
return .red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private func enableBatteryMonitoring() {
|
|
||||||
#if canImport(UIKit)
|
// MARK: - Previews
|
||||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
#Preview("Battery Overlay - Full Charge") {
|
||||||
#endif
|
BatteryOverlayView(
|
||||||
}
|
color: .white,
|
||||||
|
opacity: 1.0,
|
||||||
private func updateBattery() {
|
batteryLevel: 100
|
||||||
#if canImport(UIKit)
|
)
|
||||||
let level = UIDevice.current.batteryLevel
|
}
|
||||||
if level >= 0 {
|
|
||||||
batteryLevel = Int((level * 100).rounded())
|
#Preview("Battery Overlay - High Charge") {
|
||||||
}
|
BatteryOverlayView(
|
||||||
#endif
|
color: .white,
|
||||||
}
|
opacity: 1.0,
|
||||||
|
batteryLevel: 75
|
||||||
private func startBatteryObserver() {
|
)
|
||||||
#if canImport(UIKit)
|
}
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
forName: UIDevice.batteryLevelDidChangeNotification,
|
#Preview("Battery Overlay - Medium Charge") {
|
||||||
object: nil,
|
BatteryOverlayView(
|
||||||
queue: .main
|
color: .white,
|
||||||
) { _ in
|
opacity: 1.0,
|
||||||
updateBattery()
|
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
|
||||||
|
)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
BatteryOverlayView(
|
||||||
forName: UIDevice.batteryStateDidChangeNotification,
|
color: .blue,
|
||||||
object: nil,
|
opacity: 1.0,
|
||||||
queue: .main
|
batteryLevel: 50
|
||||||
) { _ in
|
)
|
||||||
updateBattery()
|
|
||||||
}
|
BatteryOverlayView(
|
||||||
#endif
|
color: .green,
|
||||||
|
opacity: 1.0,
|
||||||
|
batteryLevel: 25
|
||||||
|
)
|
||||||
|
|
||||||
|
BatteryOverlayView(
|
||||||
|
color: .red,
|
||||||
|
opacity: 1.0,
|
||||||
|
batteryLevel: 10
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
private func stopBatteryObserver() {
|
.background(Color.black)
|
||||||
#if canImport(UIKit)
|
}
|
||||||
NotificationCenter.default.removeObserver(
|
|
||||||
self,
|
#Preview("Battery Overlay - Different Opacities") {
|
||||||
name: UIDevice.batteryLevelDidChangeNotification,
|
VStack(spacing: 20) {
|
||||||
object: nil
|
BatteryOverlayView(
|
||||||
|
color: .white,
|
||||||
|
opacity: 1.0,
|
||||||
|
batteryLevel: 75
|
||||||
)
|
)
|
||||||
NotificationCenter.default.removeObserver(
|
|
||||||
self,
|
BatteryOverlayView(
|
||||||
name: UIDevice.batteryStateDidChangeNotification,
|
color: .white,
|
||||||
object: nil
|
opacity: 0.7,
|
||||||
|
batteryLevel: 50
|
||||||
)
|
)
|
||||||
#endif
|
|
||||||
|
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 opacity: Double
|
||||||
let dateFormat: String
|
let dateFormat: String
|
||||||
|
|
||||||
|
@State private var batteryService = BatteryService.shared
|
||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
@ -27,12 +29,18 @@ struct TopOverlayView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if showBattery {
|
if showBattery {
|
||||||
BatteryOverlayView(color: color, opacity: opacity)
|
BatteryOverlayView(color: color, opacity: opacity, batteryLevel: batteryService.batteryLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, UIConstants.Spacing.medium)
|
.padding(.horizontal, UIConstants.Spacing.medium)
|
||||||
.padding(.vertical, UIConstants.Spacing.small)
|
.padding(.vertical, UIConstants.Spacing.small)
|
||||||
.cardStyle()
|
.cardStyle()
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
|
.onAppear {
|
||||||
|
batteryService.startMonitoring()
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
batteryService.stopMonitoring()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user