diff --git a/LaunchWidget/LaunchWidgetLiveActivity.swift b/LaunchWidget/LaunchWidgetLiveActivity.swift index 6aec846..b111d14 100644 --- a/LaunchWidget/LaunchWidgetLiveActivity.swift +++ b/LaunchWidget/LaunchWidgetLiveActivity.swift @@ -5,9 +5,9 @@ // Created by Laurent Morvillier on 25/01/2023. // -import ActivityKit import WidgetKit import SwiftUI +import ActivityKit struct LiveActivityView: View { @@ -105,17 +105,23 @@ struct LaunchWidgetLiveActivity_Previews: PreviewProvider { static let contentState = LaunchWidgetAttributes.ContentState(ended: false) static var previews: some View { - attributes - .previewContext(contentState, viewKind: .dynamicIsland(.compact)) - .previewDisplayName("Island Compact") - attributes - .previewContext(contentState, viewKind: .dynamicIsland(.expanded)) - .previewDisplayName("Island Expanded") - attributes - .previewContext(contentState, viewKind: .dynamicIsland(.minimal)) - .previewDisplayName("Minimal") - attributes - .previewContext(contentState, viewKind: .content) - .previewDisplayName("Notification") + + if #available(iOS 16.2, *) { + attributes + .previewContext(contentState, viewKind: .dynamicIsland(.compact)) + .previewDisplayName("Island Compact") + attributes + .previewContext(contentState, viewKind: .dynamicIsland(.expanded)) + .previewDisplayName("Island Expanded") + attributes + .previewContext(contentState, viewKind: .dynamicIsland(.minimal)) + .previewDisplayName("Minimal") + attributes + .previewContext(contentState, viewKind: .content) + .previewDisplayName("Notification") + } else { + Text("no preview available") + } + } } diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index 3f21876..60c8b5b 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ C445FA8F2987B83B0054D761 /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C445FA8E2987B83B0054D761 /* SoundPlayer.swift */; }; C445FA922987CC8A0054D761 /* Sound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C445FA912987CC8A0054D761 /* Sound.swift */; }; C445FA952987D01C0054D761 /* train_horn.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C445FA942987D01C0054D761 /* train_horn.mp3 */; }; + C4636D9C29AF46BD00994E31 /* ActivityKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4636D9B29AF46BD00994E31 /* ActivityKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + C4636D9D29AF46D900994E31 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C438C7D02981216200BF3EF9 /* WidgetKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; C473C2F029A8CFFC0056B38A /* TimerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B1D7298C0727005C86A5 /* TimerRouter.swift */; }; C473C2F129A8DA0B0056B38A /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; }; C473C2F229A8DA1F0056B38A /* CountdownScheduler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7C02980228B00BF3EF9 /* CountdownScheduler.swift */; }; @@ -300,6 +302,7 @@ C445FA902987C0CF0054D761 /* LeCountdown.0.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.2.xcdatamodel; sourceTree = ""; }; C445FA912987CC8A0054D761 /* Sound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sound.swift; sourceTree = ""; }; C445FA942987D01C0054D761 /* train_horn.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = train_horn.mp3; sourceTree = ""; }; + C4636D9B29AF46BD00994E31 /* ActivityKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ActivityKit.framework; path = System/Library/Frameworks/ActivityKit.framework; sourceTree = SDKROOT; }; C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidgetAttributes.swift; sourceTree = ""; }; C473C30229A91BB90056B38A /* en.xcloc */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = en.xcloc; path = Localizations/en.xcloc; sourceTree = ""; }; C473C30629A91BCB0056B38A /* fr.xcloc */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = fr.xcloc; path = Localizations/fr.xcloc; sourceTree = ""; }; @@ -396,6 +399,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + C4636D9D29AF46D900994E31 /* WidgetKit.framework in Frameworks */, + C4636D9C29AF46BD00994E31 /* ActivityKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -514,6 +519,7 @@ C438C7CF2981216200BF3EF9 /* Frameworks */ = { isa = PBXGroup; children = ( + C4636D9B29AF46BD00994E31 /* ActivityKit.framework */, C4BA2AFE299A3A9E00CB4FBA /* MusicKit.framework */, C438C7D02981216200BF3EF9 /* WidgetKit.framework */, C438C7D22981216200BF3EF9 /* SwiftUI.framework */, @@ -1265,7 +1271,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -1320,7 +1326,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.2; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; diff --git a/LeCountdown/AppDelegate.swift b/LeCountdown/AppDelegate.swift index ffe6ae0..9bc9403 100644 --- a/LeCountdown/AppDelegate.swift +++ b/LeCountdown/AppDelegate.swift @@ -59,5 +59,4 @@ extension AppDelegate: UNUserNotificationCenterDelegate { } - } diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift index e504354..c8419cd 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -6,11 +6,11 @@ // import Foundation -import ActivityKit import BackgroundTasks import SwiftUI import Intents import AudioToolbox +import ActivityKit enum BGTaskIdentifier : String { case refresh = "com.staxriver.lecountdown.refresh" @@ -243,29 +243,7 @@ class Conductor: ObservableObject { let interaction = INInteraction(intent: intent, response: nil) interaction.donate() } - - // MARK: - Live Activity - - fileprivate func _launchLiveActivity(stopwatch: Stopwatch, start: Date) { - - if ActivityAuthorizationInfo().areActivitiesEnabled { - - let contentState = LaunchWidgetAttributes.ContentState(ended: false) - let attributes = LaunchWidgetAttributes(id: stopwatch.stringId, name: stopwatch.displayName, date: start) - let activityContent = ActivityContent(state: contentState, staleDate: nil) - do { - let liveActivity = try ActivityKit.Activity.request(attributes: attributes, content: activityContent) - print("Requested a Live Activity: \(String(describing: liveActivity.id)).") - } catch (let error) { - Logger.error(error) - } - -// self._scheduleAppRefresh(countdown: countdown) - - } - } - fileprivate func _scheduleAppRefresh(countdown: Countdown) { let request = BGAppRefreshTaskRequest(identifier: BGTaskIdentifier.refresh.rawValue) request.earliestBeginDate = Date(timeIntervalSinceNow: countdown.duration) @@ -276,7 +254,33 @@ class Conductor: ObservableObject { Logger.error(error) } } + + // MARK: - Live Activity + fileprivate func _launchLiveActivity(stopwatch: Stopwatch, start: Date) { + + if #available(iOS 16.2, *) { + + if ActivityAuthorizationInfo().areActivitiesEnabled { + + let contentState = LaunchWidgetAttributes.ContentState(ended: false) + let attributes = LaunchWidgetAttributes(id: stopwatch.stringId, name: stopwatch.displayName, date: start) + + let activityContent = ActivityContent(state: contentState, staleDate: nil) + do { + let liveActivity = try ActivityKit.Activity.request(attributes: attributes, content: activityContent) + print("Requested a Live Activity: \(String(describing: liveActivity.id)).") + } catch (let error) { + Logger.error(error) + } + + } + } else { + // Fallback on earlier versions + } + + } + fileprivate func _liveActivity(timerId: String) -> ActivityKit.Activity? { return ActivityKit.Activity.activities.first(where: { $0.attributes.id == timerId } ) } @@ -289,7 +293,6 @@ class Conductor: ObservableObject { if interval.end < Date() { self._endLiveActivity(timerId: countdownId) } - // if let activity = self._liveActivity(countdownId: countdownId) { // @@ -310,17 +313,17 @@ class Conductor: ObservableObject { } fileprivate func _endLiveActivity(timerId: String) { - - print("Try to end the Live Activity: \(timerId)") - - if let activity = self._liveActivity(timerId: timerId) { - Task { - let state = LaunchWidgetAttributes.ContentState(ended: true) - let content = ActivityContent(state: state, staleDate: Date()) - await activity.end(content, dismissalPolicy: .immediate) - print("Ending the Live Activity: \(activity.id)") + if #available(iOS 16.2, *) { + print("Try to end the Live Activity: \(timerId)") + if let activity = self._liveActivity(timerId: timerId) { + Task { + let state = LaunchWidgetAttributes.ContentState(ended: true) + let content = ActivityContent(state: state, staleDate: Date()) + await activity.end(content, dismissalPolicy: .immediate) + print("Ending the Live Activity: \(activity.id)") + } } } } - + } diff --git a/LeCountdown/CountdownScheduler.swift b/LeCountdown/CountdownScheduler.swift index 889b7c3..f4e58d5 100644 --- a/LeCountdown/CountdownScheduler.swift +++ b/LeCountdown/CountdownScheduler.swift @@ -19,16 +19,6 @@ class CountdownScheduler { self._scheduleCountdownNotification(countdown: countdown, handler: handler) } - func cancelCurrentNotifications(countdownId: String) { - - UNUserNotificationCenter.current().getPendingNotificationRequests { requests in - let ids = requests.map { $0.identifier }.filter { $0.hasPrefix(countdownId) } - UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids) - } - -// Conductor.maestro.cancelCountdown(id: countdownId) - } - fileprivate func _scheduleCountdownNotification(countdown: Countdown, handler: @escaping (Result) -> Void) { let content = UNMutableNotificationContent() content.title = NSLocalizedString("It's time!", comment: "") @@ -47,7 +37,6 @@ class CountdownScheduler { let sound = countdown.soundName print("Selected sound = \(sound)") -// content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: sound)) content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0) content.interruptionLevel = .critical @@ -101,4 +90,14 @@ class CountdownScheduler { // } } + func cancelCurrentNotifications(countdownId: String) { + + UNUserNotificationCenter.current().getPendingNotificationRequests { requests in + let ids = requests.map { $0.identifier }.filter { $0.hasPrefix(countdownId) } + UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ids) + } + +// Conductor.maestro.cancelCountdown(id: countdownId) + } + }