mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Lock Screen indicator for focus mode
This commit is contained in:
parent
83cca50a66
commit
3fd190f24a
4 changed files with 103 additions and 9 deletions
|
|
@ -7084,6 +7084,9 @@
|
||||||
},
|
},
|
||||||
"Displays play/pause and skip buttons beside the notch while music is active. Disabled by default." : {
|
"Displays play/pause and skip buttons beside the notch while music is active. Disabled by default." : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Displays the current Focus state above the weather capsule whenever Focus detection is enabled." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Do Not Disturb" : {
|
"Do Not Disturb" : {
|
||||||
|
|
||||||
|
|
@ -8847,9 +8850,16 @@
|
||||||
},
|
},
|
||||||
"Fit to Notch" : {
|
"Fit to Notch" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Focus active: %@" : {
|
||||||
|
"comment" : "An accessibility label for the focus widget, describing the currently active focus mode.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Focus Status" : {
|
"Focus Status" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Focus Widget" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Free · %@" : {
|
"Free · %@" : {
|
||||||
"comment" : "A label displaying the amount of free storage on a disk device. The argument is the number of free bytes on the disk.",
|
"comment" : "A label displaying the amount of free storage on a disk device. The argument is the number of free bytes on the disk.",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Defaults
|
||||||
|
|
||||||
|
|
||||||
struct LockScreenWeatherWidget: View {
|
struct LockScreenWeatherWidget: View {
|
||||||
let snapshot: LockScreenWeatherSnapshot
|
let snapshot: LockScreenWeatherSnapshot
|
||||||
|
@ObservedObject private var focusManager = DoNotDisturbManager.shared
|
||||||
|
@Default(.enableDoNotDisturbDetection) private var focusDetectionEnabled
|
||||||
|
@Default(.showDoNotDisturbIndicator) private var focusIndicatorEnabled
|
||||||
|
@Default(.enableLockScreenFocusWidget) private var lockScreenFocusWidgetEnabled
|
||||||
|
|
||||||
private let inlinePrimaryFont = Font.system(size: 22, weight: .semibold, design: .rounded)
|
private let inlinePrimaryFont = Font.system(size: 22, weight: .semibold, design: .rounded)
|
||||||
private let inlineSecondaryFont = Font.system(size: 13, weight: .medium, design: .rounded)
|
private let inlineSecondaryFont = Font.system(size: 13, weight: .medium, design: .rounded)
|
||||||
|
|
@ -20,6 +25,24 @@ struct LockScreenWeatherWidget: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: focusWidgetSpacing) {
|
||||||
|
if shouldShowFocusWidget {
|
||||||
|
focusWidget
|
||||||
|
}
|
||||||
|
mainWidgetRow
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.foregroundStyle(Color.white.opacity(0.65))
|
||||||
|
.padding(.horizontal, 10)
|
||||||
|
.padding(.top, topPadding)
|
||||||
|
.padding(.bottom, bottomPadding)
|
||||||
|
.background(Color.clear)
|
||||||
|
.shadow(color: .black.opacity(0.35), radius: 8, x: 0, y: 3)
|
||||||
|
.accessibilityElement(children: .ignore)
|
||||||
|
.accessibilityLabel(accessibilityLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mainWidgetRow: some View {
|
||||||
HStack(alignment: stackAlignment, spacing: stackSpacing) {
|
HStack(alignment: stackAlignment, spacing: stackSpacing) {
|
||||||
if let charging = snapshot.charging {
|
if let charging = snapshot.charging {
|
||||||
chargingSegment(for: charging)
|
chargingSegment(for: charging)
|
||||||
|
|
@ -43,15 +66,6 @@ struct LockScreenWeatherWidget: View {
|
||||||
locationSegment
|
locationSegment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
|
||||||
.foregroundStyle(Color.white.opacity(0.65))
|
|
||||||
.padding(.horizontal, 10)
|
|
||||||
.padding(.top, topPadding)
|
|
||||||
.padding(.bottom, bottomPadding)
|
|
||||||
.background(Color.clear)
|
|
||||||
.shadow(color: .black.opacity(0.35), radius: 8, x: 0, y: 3)
|
|
||||||
.accessibilityElement(children: .ignore)
|
|
||||||
.accessibilityLabel(accessibilityLabel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|
@ -336,6 +350,61 @@ struct LockScreenWeatherWidget: View {
|
||||||
snapshot.showsLocation && (snapshot.locationName?.isEmpty == false)
|
snapshot.showsLocation && (snapshot.locationName?.isEmpty == false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var focusWidgetSpacing: CGFloat {
|
||||||
|
guard shouldShowFocusWidget else { return 0 }
|
||||||
|
return isInline ? 14 : 20
|
||||||
|
}
|
||||||
|
|
||||||
|
private var shouldShowFocusWidget: Bool {
|
||||||
|
lockScreenFocusWidgetEnabled &&
|
||||||
|
focusDetectionEnabled &&
|
||||||
|
focusIndicatorEnabled &&
|
||||||
|
focusManager.isDoNotDisturbActive &&
|
||||||
|
!focusDisplayName.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
private var focusDisplayName: String {
|
||||||
|
let trimmed = focusManager.currentFocusModeName.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
if !trimmed.isEmpty {
|
||||||
|
return trimmed
|
||||||
|
}
|
||||||
|
if focusMode == .doNotDisturb {
|
||||||
|
return "Do Not Disturb"
|
||||||
|
}
|
||||||
|
let fallback = focusMode.displayName
|
||||||
|
return fallback.isEmpty ? "Focus" : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
private var focusMode: FocusModeType {
|
||||||
|
FocusModeType.resolve(
|
||||||
|
identifier: focusManager.currentFocusModeIdentifier,
|
||||||
|
name: focusManager.currentFocusModeName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var focusIcon: Image {
|
||||||
|
focusMode
|
||||||
|
.resolvedActiveIcon(usePrivateSymbol: true)
|
||||||
|
.renderingMode(.template)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var focusWidget: some View {
|
||||||
|
HStack(alignment: .center, spacing: 8) {
|
||||||
|
focusIcon
|
||||||
|
.font(.system(size: 20, weight: .semibold))
|
||||||
|
.frame(width: 26, height: 26)
|
||||||
|
|
||||||
|
Text(focusDisplayName)
|
||||||
|
.font(inlinePrimaryFont)
|
||||||
|
.lineLimit(1)
|
||||||
|
.minimumScaleFactor(0.85)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.padding(.horizontal, 2)
|
||||||
|
.accessibilityLabel("Focus active: \(focusDisplayName)")
|
||||||
|
}
|
||||||
|
|
||||||
private func chargingIconName(for info: LockScreenWeatherSnapshot.ChargingInfo) -> String? {
|
private func chargingIconName(for info: LockScreenWeatherSnapshot.ChargingInfo) -> String? {
|
||||||
let icon = info.iconName
|
let icon = info.iconName
|
||||||
return icon.isEmpty ? nil : icon
|
return icon.isEmpty ? nil : icon
|
||||||
|
|
@ -549,6 +618,10 @@ struct LockScreenWeatherWidget: View {
|
||||||
components.append(accessibilityBatteryText(for: battery))
|
components.append(accessibilityBatteryText(for: battery))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if shouldShowFocusWidget {
|
||||||
|
components.append("Focus active: \(focusDisplayName)")
|
||||||
|
}
|
||||||
|
|
||||||
return components.joined(separator: ". ")
|
return components.joined(separator: ". ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2583,6 +2583,7 @@ struct LockScreenSettings: View {
|
||||||
@Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget
|
@Default(.enableLockScreenMediaWidget) private var enableLockScreenMediaWidget
|
||||||
@Default(.enableLockScreenTimerWidget) private var enableLockScreenTimerWidget
|
@Default(.enableLockScreenTimerWidget) private var enableLockScreenTimerWidget
|
||||||
@Default(.enableLockScreenWeatherWidget) private var enableLockScreenWeatherWidget
|
@Default(.enableLockScreenWeatherWidget) private var enableLockScreenWeatherWidget
|
||||||
|
@Default(.enableLockScreenFocusWidget) private var enableLockScreenFocusWidget
|
||||||
@Default(.lockScreenWeatherWidgetStyle) private var lockScreenWeatherWidgetStyle
|
@Default(.lockScreenWeatherWidgetStyle) private var lockScreenWeatherWidgetStyle
|
||||||
@Default(.lockScreenWeatherProviderSource) private var lockScreenWeatherProviderSource
|
@Default(.lockScreenWeatherProviderSource) private var lockScreenWeatherProviderSource
|
||||||
@Default(.lockScreenWeatherTemperatureUnit) private var lockScreenWeatherTemperatureUnit
|
@Default(.lockScreenWeatherTemperatureUnit) private var lockScreenWeatherTemperatureUnit
|
||||||
|
|
@ -2738,6 +2739,15 @@ struct LockScreenSettings: View {
|
||||||
Text("Enable the weather capsule and configure its layout, provider, units, and optional battery/AQI indicators.")
|
Text("Enable the weather capsule and configure its layout, provider, units, and optional battery/AQI indicators.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Show focus widget", key: .enableLockScreenFocusWidget)
|
||||||
|
.settingsHighlight(id: highlightID("Show focus widget"))
|
||||||
|
} header: {
|
||||||
|
Text("Focus Widget")
|
||||||
|
} footer: {
|
||||||
|
Text("Displays the current Focus state above the weather capsule whenever Focus detection is enabled.")
|
||||||
|
}
|
||||||
|
|
||||||
LockScreenPositioningControls()
|
LockScreenPositioningControls()
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,7 @@ extension Defaults.Keys {
|
||||||
// Enable lock screen media widget (shows the standalone panel when screen is locked)
|
// Enable lock screen media widget (shows the standalone panel when screen is locked)
|
||||||
static let enableLockScreenMediaWidget = Key<Bool>("enableLockScreenMediaWidget", default: true)
|
static let enableLockScreenMediaWidget = Key<Bool>("enableLockScreenMediaWidget", default: true)
|
||||||
static let enableLockScreenWeatherWidget = Key<Bool>("enableLockScreenWeatherWidget", default: true)
|
static let enableLockScreenWeatherWidget = Key<Bool>("enableLockScreenWeatherWidget", default: true)
|
||||||
|
static let enableLockScreenFocusWidget = Key<Bool>("enableLockScreenFocusWidget", default: true)
|
||||||
static let enableLockScreenReminderWidget = Key<Bool>("enableLockScreenReminderWidget", default: true)
|
static let enableLockScreenReminderWidget = Key<Bool>("enableLockScreenReminderWidget", default: true)
|
||||||
static let enableLockScreenTimerWidget = Key<Bool>("enableLockScreenTimerWidget", default: true)
|
static let enableLockScreenTimerWidget = Key<Bool>("enableLockScreenTimerWidget", default: true)
|
||||||
static let lockScreenWeatherRefreshInterval = Key<TimeInterval>("lockScreenWeatherRefreshInterval", default: 30 * 60)
|
static let lockScreenWeatherRefreshInterval = Key<TimeInterval>("lockScreenWeatherRefreshInterval", default: 30 * 60)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue