diff --git a/LaunchIntents/Info.plist b/LaunchIntents/Info.plist
index 6e03edf..bd524a4 100644
--- a/LaunchIntents/Info.plist
+++ b/LaunchIntents/Info.plist
@@ -12,6 +12,7 @@
IntentsSupported
+ LaunchTimerIntent
SelectTimerIntent
diff --git a/LaunchIntents/IntentHandler.swift b/LaunchIntents/IntentHandler.swift
index 69b21dc..c35f7c7 100644
--- a/LaunchIntents/IntentHandler.swift
+++ b/LaunchIntents/IntentHandler.swift
@@ -7,7 +7,36 @@
import Intents
-class IntentHandler: INExtension, SelectTimerIntentHandling {
+class IntentHandler: INExtension, SelectTimerIntentHandling, LaunchTimerIntentHandling {
+
+ // MARK: - SelectTimerIntentHandling
+
+ func resolveTimer(for intent: LaunchTimerIntent) async -> TimerIdentifierResolutionResult {
+ if let timer = intent.timer {
+ print("resolveTimer(for intent: LaunchTimerIntent) success !")
+ return .success(with: timer)
+ }
+ print("resolveTimer(for intent: LaunchTimerIntent) needsValue")
+ return .needsValue()
+ }
+
+ func handle(intent: LaunchTimerIntent) async -> LaunchTimerIntentResponse {
+ if let timerIdentifier = intent.timer,
+ let timerId = timerIdentifier.identifier,
+ let timer = IntentDataProvider.main.timer(id: timerId) {
+ do {
+ let _ = try await TimerRouter.performAction(timer: timer)
+ print("handle(intent: LaunchTimerIntent) success !")
+ return .success(timer: timerIdentifier)
+ } catch {
+ return LaunchTimerIntentResponse(code: LaunchTimerIntentResponseCode(rawValue: 1)!, userActivity: nil)
+ }
+ }
+ print("handle(intent: LaunchTimerIntent) no timer")
+ return LaunchTimerIntentResponse(code: LaunchTimerIntentResponseCode(rawValue: 1)!, userActivity: nil)
+ }
+
+ // MARK: - SelectTimerIntentHandling
func resolveTimer(for intent: SelectTimerIntent) async -> [TimerPropertiesResolutionResult] {
print("***resolveCountdown")
diff --git a/LaunchIntents/fr.lproj/Localizable.strings b/LaunchIntents/fr.lproj/Localizable.strings
new file mode 100644
index 0000000..4216c7e
--- /dev/null
+++ b/LaunchIntents/fr.lproj/Localizable.strings
@@ -0,0 +1,15 @@
+/* No comment provided by engineer. */
+"Custom" = "Personnalisé";
+
+/* No comment provided by engineer. */
+"It's time!" = "C'est l'heure !";
+
+/* No comment provided by engineer. */
+"Nature" = "Nature";
+
+/* No comment provided by engineer. */
+"Time's up for (name)!" = "C'est l'heure pour (name) !";
+
+/* No comment provided by engineer. */
+"Your (duration.minuteSecond) countdown is over!" = "Votre minuteur de (duration.minuteSecond) est terminé !";
+
diff --git a/LaunchWidget/LaunchWidgetLiveActivity.swift b/LaunchWidget/LaunchWidgetLiveActivity.swift
index 46d62cb..6aec846 100644
--- a/LaunchWidget/LaunchWidgetLiveActivity.swift
+++ b/LaunchWidget/LaunchWidgetLiveActivity.swift
@@ -9,19 +9,6 @@ import ActivityKit
import WidgetKit
import SwiftUI
-struct LaunchWidgetAttributes: ActivityAttributes {
- public struct ContentState: Codable, Hashable {
- // Dynamic stateful properties about your activity go here!
- var ended: Bool
- }
-
- // Fixed non-changing properties about your activity go here!
- var id: String
- var name: String
- var date: Date
-
-}
-
struct LiveActivityView: View {
var name: String
diff --git a/LaunchWidget/LaunchWidget.intentdefinition b/LaunchWidget/en.lproj/LaunchWidget.intentdefinition
similarity index 100%
rename from LaunchWidget/LaunchWidget.intentdefinition
rename to LaunchWidget/en.lproj/LaunchWidget.intentdefinition
diff --git a/LaunchWidget/fr.lproj/LaunchWidget.intentdefinition b/LaunchWidget/fr.lproj/LaunchWidget.intentdefinition
new file mode 100644
index 0000000..0eb2c8e
--- /dev/null
+++ b/LaunchWidget/fr.lproj/LaunchWidget.intentdefinition
@@ -0,0 +1,264 @@
+
+
+
+
+ INEnums
+
+ INIntentDefinitionModelVersion
+ 1.2
+ INIntentDefinitionNamespace
+ 88xZPY
+ INIntentDefinitionSystemVersion
+ 22A400
+ INIntentDefinitionToolsBuildVersion
+ 14C18
+ INIntentDefinitionToolsVersion
+ 14.2
+ INIntents
+
+
+ INIntentCategory
+ information
+ INIntentDescriptionID
+ dIYBJB
+ INIntentEligibleForWidgets
+
+ INIntentIneligibleForSuggestions
+
+ INIntentLastParameterTag
+ 3
+ INIntentName
+ SelectTimer
+ INIntentParameters
+
+
+ INIntentParameterArraySizes
+
+
+ INIntentParameterArraySizeSize
+ 1
+ INIntentParameterArraySizeSizeClass
+ Small
+
+
+ INIntentParameterArraySizeSize
+ 4
+ INIntentParameterArraySizeSizeClass
+ Medium
+
+
+ INIntentParameterArraySizeSize
+ 8
+ INIntentParameterArraySizeSizeClass
+ Large
+
+
+ INIntentParameterArraySizeSize
+ 12
+ INIntentParameterArraySizeSizeClass
+ ExtraLarge
+
+
+ INIntentParameterArraySizeSize
+ 1
+ INIntentParameterArraySizeSizeClass
+ AccessoryInline
+
+
+ INIntentParameterArraySizeSize
+ 1
+ INIntentParameterArraySizeSizeClass
+ AccessoryCorner
+
+
+ INIntentParameterArraySizeSize
+ 1
+ INIntentParameterArraySizeSizeClass
+ AccessoryCircular
+
+
+ INIntentParameterArraySizeSize
+ 1
+ INIntentParameterArraySizeSizeClass
+ AccessoryRectangular
+
+
+ INIntentParameterConfigurable
+
+ INIntentParameterCustomDisambiguation
+
+ INIntentParameterDisplayName
+ Compteur
+ INIntentParameterDisplayNameID
+ lE8mOk
+ INIntentParameterDisplayPriority
+ 1
+ INIntentParameterFixedSizeArray
+ 1
+ INIntentParameterName
+ timer
+ INIntentParameterObjectType
+ TimerProperties
+ INIntentParameterObjectTypeNamespace
+ 88xZPY
+ INIntentParameterPromptDialogs
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Configuration
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Primary
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ Il y a ${count} options correspondant à ‘${timer}’.
+ INIntentParameterPromptDialogFormatStringID
+ gtJyOP
+ INIntentParameterPromptDialogType
+ DisambiguationIntroduction
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ Merci de confirmer, vous souhaitez ‘${timer}’?
+ INIntentParameterPromptDialogFormatStringID
+ nntWsg
+ INIntentParameterPromptDialogType
+ Confirmation
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ Laquelle ?
+ INIntentParameterPromptDialogFormatStringID
+ tTDiOr
+ INIntentParameterPromptDialogType
+ DisambiguationSelection
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ Nous avons aussi,
+ INIntentParameterPromptDialogFormatStringID
+ VvsWeN
+ INIntentParameterPromptDialogType
+ SubsequentIntroduction
+
+
+ INIntentParameterSupportsDynamicEnumeration
+
+ INIntentParameterSupportsMultipleValues
+
+ INIntentParameterSupportsResolution
+
+ INIntentParameterTag
+ 3
+ INIntentParameterType
+ Object
+
+
+ INIntentResponse
+
+ INIntentResponseCodes
+
+
+ INIntentResponseCodeName
+ success
+ INIntentResponseCodeSuccess
+
+
+
+ INIntentResponseCodeName
+ failure
+
+
+
+ INIntentTitle
+ Choisissez un compteur
+ INIntentTitleID
+ Dm6sPw
+ INIntentType
+ Custom
+ INIntentVerb
+ View
+
+
+ INTypes
+
+
+ INTypeDisplayName
+ TimerProperties
+ INTypeDisplayNameID
+ ZTfW1g
+ INTypeLastPropertyTag
+ 102
+ INTypeName
+ TimerProperties
+ INTypeProperties
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 1
+ INTypePropertyName
+ identifier
+ INTypePropertyTag
+ 1
+ INTypePropertyType
+ String
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 2
+ INTypePropertyName
+ displayString
+ INTypePropertyTag
+ 2
+ INTypePropertyType
+ String
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 3
+ INTypePropertyName
+ pronunciationHint
+ INTypePropertyTag
+ 3
+ INTypePropertyType
+ String
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 4
+ INTypePropertyName
+ alternativeSpeakableMatches
+ INTypePropertySupportsMultipleValues
+
+ INTypePropertyTag
+ 4
+ INTypePropertyType
+ SpeakableString
+
+
+
+
+
+
diff --git a/LaunchWidget/fr.lproj/Localizable.strings b/LaunchWidget/fr.lproj/Localizable.strings
new file mode 100644
index 0000000..0418c89
--- /dev/null
+++ b/LaunchWidget/fr.lproj/Localizable.strings
@@ -0,0 +1,18 @@
+/* No comment provided by engineer. */
+"Configure me!" = "Configurez moi !";
+
+/* No comment provided by engineer. */
+"Configure me!\n" = "Configurez moi !";
+
+/* No comment provided by engineer. */
+"Launch Widget" = "Widget de lancement";
+
+/* No comment provided by engineer. */
+"Min" = "Min";
+
+/* No comment provided by engineer. */
+"Select and launch your timers" = "Sélectionnez et lancer vos minuteurs";
+
+/* No comment provided by engineer. */
+"Stop" = "Arrêter";
+
diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj
index 211d254..40c38a5 100644
--- a/LeCountdown.xcodeproj/project.pbxproj
+++ b/LeCountdown.xcodeproj/project.pbxproj
@@ -28,15 +28,12 @@
C438C7D82981216200BF3EF9 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; };
C438C7DA2981216200BF3EF9 /* LaunchWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D92981216200BF3EF9 /* LaunchWidget.swift */; };
C438C7DD2981216300BF3EF9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C438C7DC2981216300BF3EF9 /* Assets.xcassets */; };
- C438C7DF2981216300BF3EF9 /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */; };
- C438C7E02981216300BF3EF9 /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */; };
C438C7E32981216300BF3EF9 /* LaunchWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C438C7CE2981216200BF3EF9 /* LaunchWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
C438C7E82981255D00BF3EF9 /* TimeInterval+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */; };
C438C7EB2981266F00BF3EF9 /* SingleTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7E92981260D00BF3EF9 /* SingleTimerView.swift */; };
C438C7F229812BB200BF3EF9 /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C438C7F129812BB200BF3EF9 /* Intents.framework */; };
C438C7F529812BB200BF3EF9 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7F429812BB200BF3EF9 /* IntentHandler.swift */; };
C438C7F929812BB200BF3EF9 /* LaunchIntents.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C438C7F029812BB200BF3EF9 /* LaunchIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
- C438C7FD29812BF700BF3EF9 /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */; };
C438C7FF2981300500BF3EF9 /* IntentDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */; };
C438C800298130E900BF3EF9 /* IntentDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */; };
C438C8012981327600BF3EF9 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4060DC8297AE73D003FAB80 /* Persistence.swift */; };
@@ -57,6 +54,33 @@
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 */; };
+ 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 */; };
+ C473C2F329A8DA6F0056B38A /* LiveTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E5A0298D543900E90DE0 /* LiveTimer.swift */; };
+ C473C2F429A8DAE70056B38A /* Model+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C806298195E600BF3EF9 /* Model+Extensions.swift */; };
+ C473C2F529A8DAF30056B38A /* PropertyWrappers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C81029829EAF00BF3EF9 /* PropertyWrappers.swift */; };
+ C473C2F629A8DB1D0056B38A /* Sound.swift in Sources */ = {isa = PBXBuildFile; fileRef = C445FA912987CC8A0054D761 /* Sound.swift */; };
+ C473C2F729A8DB6F0056B38A /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B2C299E2DEE00CB4FBA /* Preferences.swift */; };
+ C473C2F929A8DC0A0056B38A /* LaunchWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */; };
+ C473C2FA29A8DC1E0056B38A /* LaunchWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */; };
+ C473C2FB29A8DC3A0056B38A /* LaunchWidgetAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */; };
+ C473C2FC29A8DC4B0056B38A /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6929A4BE1800CB4FBA /* Date+Extensions.swift */; };
+ C473C2FD29A8DC690056B38A /* CoreDataRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C80C2982847300BF3EF9 /* CoreDataRequests.swift */; };
+ C473C30329A91BB90056B38A /* en.xcloc in Resources */ = {isa = PBXBuildFile; fileRef = C473C30229A91BB90056B38A /* en.xcloc */; };
+ C473C30429A91BB90056B38A /* en.xcloc in Resources */ = {isa = PBXBuildFile; fileRef = C473C30229A91BB90056B38A /* en.xcloc */; };
+ C473C30529A91BB90056B38A /* en.xcloc in Resources */ = {isa = PBXBuildFile; fileRef = C473C30229A91BB90056B38A /* en.xcloc */; };
+ C473C30729A91BCC0056B38A /* fr.xcloc in Resources */ = {isa = PBXBuildFile; fileRef = C473C30629A91BCB0056B38A /* fr.xcloc */; };
+ C473C30829A91BCC0056B38A /* fr.xcloc in Resources */ = {isa = PBXBuildFile; fileRef = C473C30629A91BCB0056B38A /* fr.xcloc */; };
+ C473C30929A91BCC0056B38A /* fr.xcloc in Resources */ = {isa = PBXBuildFile; fileRef = C473C30629A91BCB0056B38A /* fr.xcloc */; };
+ C473C30C29A9212E0056B38A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473C30A29A9212E0056B38A /* Localizable.strings */; };
+ C473C30F29A9212E0056B38A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473C30D29A9212E0056B38A /* Localizable.strings */; };
+ C473C31229A9212E0056B38A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473C31029A9212E0056B38A /* Localizable.strings */; };
+ C473C31329A925D50056B38A /* SiriIntents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C473C31629A925D50056B38A /* SiriIntents.intentdefinition */; };
+ C473C31429A925D50056B38A /* SiriIntents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C473C31629A925D50056B38A /* SiriIntents.intentdefinition */; };
+ C473C31829A926F50056B38A /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */; };
+ C473C31929A926F50056B38A /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */; };
+ C473C31A29A926F50056B38A /* LaunchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */; };
C4742B5729840F6400D5D950 /* CoolPic.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B5629840F6400D5D950 /* CoolPic.swift */; };
C4742B59298411E800D5D950 /* CountdownFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B58298411E800D5D950 /* CountdownFormView.swift */; };
C4742B5B298414B000D5D950 /* ImageSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B5A298414B000D5D950 /* ImageSelectionView.swift */; };
@@ -127,7 +151,6 @@
C4BA2B6529A3C37D00CB4FBA /* Filter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6429A3C37D00CB4FBA /* Filter.swift */; };
C4BA2B6829A3C4AC00CB4FBA /* Context+Calculations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6729A3C4AC00CB4FBA /* Context+Calculations.swift */; };
C4BA2B6A29A4BE1800CB4FBA /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6929A4BE1800CB4FBA /* Date+Extensions.swift */; };
- C4BA2B7129A51CA000CB4FBA /* SiriIntents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B7029A51CA000CB4FBA /* SiriIntents.intentdefinition */; };
C4BA2B7329A60CF000CB4FBA /* Shortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B7229A60CF000CB4FBA /* Shortcut.swift */; };
C4BA2B7929A65C1400CB4FBA /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */; };
C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; };
@@ -256,7 +279,6 @@
C438C7D52981216200BF3EF9 /* LaunchWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidgetBundle.swift; sourceTree = ""; };
C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidgetLiveActivity.swift; sourceTree = ""; };
C438C7D92981216200BF3EF9 /* LaunchWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidget.swift; sourceTree = ""; };
- C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = LaunchWidget.intentdefinition; sourceTree = ""; };
C438C7DC2981216300BF3EF9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
C438C7DE2981216300BF3EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
C438C7E92981260D00BF3EF9 /* SingleTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTimerView.swift; sourceTree = ""; };
@@ -275,6 +297,14 @@
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 = ""; };
+ 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 = ""; };
+ C473C30B29A9212E0056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; };
+ C473C30E29A9212E0056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; };
+ C473C31129A9212E0056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; };
+ C473C31729A9262A0056B38A /* en */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = en; path = en.lproj/SiriIntents.intentdefinition; sourceTree = ""; };
+ C473C31D29A926F80056B38A /* en */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = en; path = en.lproj/LaunchWidget.intentdefinition; sourceTree = ""; };
C4742B5629840F6400D5D950 /* CoolPic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolPic.swift; sourceTree = ""; };
C4742B58298411E800D5D950 /* CountdownFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownFormView.swift; sourceTree = ""; };
C4742B5A298414B000D5D950 /* ImageSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSelectionView.swift; sourceTree = ""; };
@@ -326,7 +356,6 @@
C4BA2B6729A3C4AC00CB4FBA /* Context+Calculations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Context+Calculations.swift"; sourceTree = ""; };
C4BA2B6929A4BE1800CB4FBA /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; };
C4BA2B6B29A4C47100CB4FBA /* LeCountdown.0.6.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.6.2.xcdatamodel; sourceTree = ""; };
- C4BA2B7029A51CA000CB4FBA /* SiriIntents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = SiriIntents.intentdefinition; sourceTree = ""; };
C4BA2B7229A60CF000CB4FBA /* Shortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcut.swift; sourceTree = ""; };
C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = ""; };
C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = ""; };
@@ -442,8 +471,11 @@
C4BA2B55299FFA3700CB4FBA /* Subscription */,
C4060DC3297AE73D003FAB80 /* Assets.xcassets */,
C438C80429813B3100BF3EF9 /* LeCountdown.entitlements */,
- C4BA2B7029A51CA000CB4FBA /* SiriIntents.intentdefinition */,
+ C473C30229A91BB90056B38A /* en.xcloc */,
+ C473C30629A91BCB0056B38A /* fr.xcloc */,
+ C473C31629A925D50056B38A /* SiriIntents.intentdefinition */,
C4060DCD297AE73D003FAB80 /* Info.plist */,
+ C473C31029A9212E0056B38A /* Localizable.strings */,
C4060DC5297AE73D003FAB80 /* Preview Content */,
);
path = LeCountdown;
@@ -493,9 +525,10 @@
C438C7D92981216200BF3EF9 /* LaunchWidget.swift */,
C438C7E92981260D00BF3EF9 /* SingleTimerView.swift */,
C4BA2B30299F759700CB4FBA /* DefaultView.swift */,
- C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */,
+ C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */,
C438C7DC2981216300BF3EF9 /* Assets.xcassets */,
C438C7DE2981216300BF3EF9 /* Info.plist */,
+ C473C30D29A9212E0056B38A /* Localizable.strings */,
);
path = LaunchWidget;
sourceTree = "";
@@ -506,6 +539,7 @@
C438C7F429812BB200BF3EF9 /* IntentHandler.swift */,
C438C80329813B2500BF3EF9 /* LaunchIntents.entitlements */,
C438C7F629812BB200BF3EF9 /* Info.plist */,
+ C473C30A29A9212E0056B38A /* Localizable.strings */,
);
path = LaunchIntents;
sourceTree = "";
@@ -514,6 +548,7 @@
isa = PBXGroup;
children = (
C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */,
+ C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */,
);
path = Widget;
sourceTree = "";
@@ -834,7 +869,7 @@
hasScannedForEncodings = 0;
knownRegions = (
en,
- Base,
+ fr,
);
mainGroup = C4060DB3297AE73B003FAB80;
productRefGroup = C4060DBD297AE73B003FAB80 /* Products */;
@@ -856,7 +891,9 @@
buildActionMask = 2147483647;
files = (
C4060DC7297AE73D003FAB80 /* Preview Assets.xcassets in Resources */,
+ C473C30729A91BCC0056B38A /* fr.xcloc in Resources */,
C4BA2AEA2995AD1C00CB4FBA /* SEM_Synths_Loop4_Nothing_Like_You.wav in Resources */,
+ C473C31229A9212E0056B38A /* Localizable.strings in Resources */,
C4BA2AE42995AC0D00CB4FBA /* Arpeggio_Loop_River.wav in Resources */,
C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */,
C4BA2AE22995ABE700CB4FBA /* SquareArp_Loop_River.wav in Resources */,
@@ -865,6 +902,7 @@
C445FA952987D01C0054D761 /* train_horn.mp3 in Resources */,
C4BA2AE02995ABD200CB4FBA /* HighChords_Loop_River.wav in Resources */,
C4060DC4297AE73D003FAB80 /* Assets.xcassets in Resources */,
+ C473C30329A91BB90056B38A /* en.xcloc in Resources */,
C4BA2AE62995AC3F00CB4FBA /* Loop_ToneSD_Boavista.wav in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -887,8 +925,11 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ C473C30F29A9212E0056B38A /* Localizable.strings in Resources */,
C445FA882984487F0054D761 /* Assets.xcassets in Resources */,
+ C473C30429A91BB90056B38A /* en.xcloc in Resources */,
C438C7DD2981216300BF3EF9 /* Assets.xcassets in Resources */,
+ C473C30829A91BCC0056B38A /* fr.xcloc in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -896,6 +937,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ C473C30529A91BB90056B38A /* en.xcloc in Resources */,
+ C473C30C29A9212E0056B38A /* Localizable.strings in Resources */,
+ C473C30929A91BCC0056B38A /* fr.xcloc in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -935,6 +979,7 @@
C498E5A5299152B400E90DE0 /* GreenCheckmarkView.swift in Sources */,
C4BA2B3E299FC86800CB4FBA /* StatsView.swift in Sources */,
C4F8B1B8298AC81D005C86A5 /* CountdownDialView.swift in Sources */,
+ C473C2F929A8DC0A0056B38A /* LaunchWidgetAttributes.swift in Sources */,
C445FA922987CC8A0054D761 /* Sound.swift in Sources */,
C4F8B1D0298BF2E2005C86A5 /* DialView.swift in Sources */,
C4BA2B11299BE61E00CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */,
@@ -948,7 +993,7 @@
C438C7C12980228B00BF3EF9 /* CountdownScheduler.swift in Sources */,
C498E5A3298D720600E90DE0 /* TestView.swift in Sources */,
C4BA2B06299A8F8D00CB4FBA /* PresetsView.swift in Sources */,
- C4BA2B7129A51CA000CB4FBA /* SiriIntents.intentdefinition in Sources */,
+ C473C31329A925D50056B38A /* SiriIntents.intentdefinition in Sources */,
C445FA8F2987B83B0054D761 /* SoundPlayer.swift in Sources */,
C4F8B1A7298AC2FC005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
C438C7C929803CA000BF3EF9 /* AppDelegate.swift in Sources */,
@@ -987,7 +1032,7 @@
C4F8B1AC298AC3A0005C86A5 /* Alarm+CoreDataProperties.swift in Sources */,
C498E59F298D4DEA00E90DE0 /* LiveTimerListView.swift in Sources */,
C4060DC0297AE73B003FAB80 /* LeCountdownApp.swift in Sources */,
- C438C7E02981216300BF3EF9 /* LaunchWidget.intentdefinition in Sources */,
+ C473C31829A926F50056B38A /* LaunchWidget.intentdefinition in Sources */,
C4742B5729840F6400D5D950 /* CoolPic.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1031,9 +1076,10 @@
C4BA2B4D299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4F8B194298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C438C8152982BD9000BF3EF9 /* IntentDataProvider.swift in Sources */,
- C438C7DF2981216300BF3EF9 /* LaunchWidget.intentdefinition in Sources */,
+ C473C31929A926F50056B38A /* LaunchWidget.intentdefinition in Sources */,
C4F8B15B29892D40005C86A5 /* SoundPlayer.swift in Sources */,
C4BA2B5C299FFAB000CB4FBA /* Logger.swift in Sources */,
+ C473C2FA29A8DC1E0056B38A /* LaunchWidgetAttributes.swift in Sources */,
C438C7E82981255D00BF3EF9 /* TimeInterval+Extensions.swift in Sources */,
C4BA2B37299F82FF00CB4FBA /* Fakes.swift in Sources */,
C4F8B18F298AC288005C86A5 /* AbstractTimer+CoreDataClass.swift in Sources */,
@@ -1057,11 +1103,15 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ C473C2FD29A8DC690056B38A /* CoreDataRequests.swift in Sources */,
+ C473C2F329A8DA6F0056B38A /* LiveTimer.swift in Sources */,
C4F8B1C6298ACC1F005C86A5 /* SoundPlayer.swift in Sources */,
C4F8B1A2298AC288005C86A5 /* Record+CoreDataProperties.swift in Sources */,
C4F8B1B2298AC451005C86A5 /* Alarm+CoreDataProperties.swift in Sources */,
+ C473C2F229A8DA1F0056B38A /* CountdownScheduler.swift in Sources */,
C4BA2AF92996A4F000CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
C4F8B1A1298AC288005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
+ C473C2F729A8DB6F0056B38A /* Preferences.swift in Sources */,
C4BA2B38299F82FF00CB4FBA /* Fakes.swift in Sources */,
C438C7F529812BB200BF3EF9 /* IntentHandler.swift in Sources */,
C4F8B19C298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */,
@@ -1070,6 +1120,7 @@
C4F8B1A3298AC288005C86A5 /* Activity+CoreDataClass.swift in Sources */,
C4F8B19A298AC288005C86A5 /* Alarm+CoreDataClass.swift in Sources */,
C438C80529813FB400BF3EF9 /* TimeInterval+Extensions.swift in Sources */,
+ C473C2F629A8DB1D0056B38A /* Sound.swift in Sources */,
C4BA2B1B299BE6A100CB4FBA /* Interval+CoreDataProperties.swift in Sources */,
C438C802298132B900BF3EF9 /* LeCountdown.xcdatamodeld in Sources */,
C4F8B19B298AC288005C86A5 /* Stopwatch+CoreDataClass.swift in Sources */,
@@ -1080,14 +1131,21 @@
C4BA2AF82996A4F000CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4F8B199298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */,
C438C8012981327600BF3EF9 /* Persistence.swift in Sources */,
- C438C7FD29812BF700BF3EF9 /* LaunchWidget.intentdefinition in Sources */,
+ C473C31A29A926F50056B38A /* LaunchWidget.intentdefinition in Sources */,
C4BA2B5D299FFAB000CB4FBA /* Logger.swift in Sources */,
+ C473C2FB29A8DC3A0056B38A /* LaunchWidgetAttributes.swift in Sources */,
C4BA2B4B299FCE0C00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C4F8B1B1298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
+ C473C2F129A8DA0B0056B38A /* Conductor.swift in Sources */,
C4BA2B1E299BE6A100CB4FBA /* Interval+CoreDataClass.swift in Sources */,
+ C473C2FC29A8DC4B0056B38A /* Date+Extensions.swift in Sources */,
+ C473C2F429A8DAE70056B38A /* Model+Extensions.swift in Sources */,
+ C473C31429A925D50056B38A /* SiriIntents.intentdefinition in Sources */,
C445FA86298448720054D761 /* CoolPic.swift in Sources */,
C4BA2B4E299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4BA2B23299BE82E00CB4FBA /* AbstractSoundTimer+CoreDataProperties.swift in Sources */,
+ C473C2F529A8DAF30056B38A /* PropertyWrappers.swift in Sources */,
+ C473C2F029A8CFFC0056B38A /* TimerRouter.swift in Sources */,
C438C800298130E900BF3EF9 /* IntentDataProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1117,6 +1175,49 @@
};
/* End PBXTargetDependency section */
+/* Begin PBXVariantGroup section */
+ C473C30A29A9212E0056B38A /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ C473C30B29A9212E0056B38A /* fr */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
+ C473C30D29A9212E0056B38A /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ C473C30E29A9212E0056B38A /* fr */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
+ C473C31029A9212E0056B38A /* Localizable.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ C473C31129A9212E0056B38A /* fr */,
+ );
+ name = Localizable.strings;
+ sourceTree = "";
+ };
+ C473C31629A925D50056B38A /* SiriIntents.intentdefinition */ = {
+ isa = PBXVariantGroup;
+ children = (
+ C473C31729A9262A0056B38A /* en */,
+ );
+ name = SiriIntents.intentdefinition;
+ sourceTree = "";
+ };
+ C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */ = {
+ isa = PBXVariantGroup;
+ children = (
+ C473C31D29A926F80056B38A /* en */,
+ );
+ name = LaunchWidget.intentdefinition;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
/* Begin XCBuildConfiguration section */
C4060DE4297AE73D003FAB80 /* Debug */ = {
isa = XCBuildConfiguration;
diff --git a/LeCountdown/AppDelegate.swift b/LeCountdown/AppDelegate.swift
index eba54b4..a2db5ed 100644
--- a/LeCountdown/AppDelegate.swift
+++ b/LeCountdown/AppDelegate.swift
@@ -12,11 +12,23 @@ class AppDelegate : NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
+ Logger.log("didfinish > countdowns count = \(Conductor.maestro.currentCountdowns.count)")
+
UNUserNotificationCenter.current().delegate = self
Conductor.maestro.cleanup()
return true
}
+ func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
+
+ if userActivity.interaction == nil {
+
+ Logger.log("restorationHandler called! interaction is nil")
+ return false
+ }
+ return true
+ }
+
}
extension AppDelegate: UNUserNotificationCenterDelegate {
diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift
index da16b08..31e40ef 100644
--- a/LeCountdown/Conductor.swift
+++ b/LeCountdown/Conductor.swift
@@ -9,7 +9,11 @@ import Foundation
import ActivityKit
import BackgroundTasks
import SwiftUI
-//import Intents
+import Intents
+
+enum BGTaskIdentifier : String {
+ case refresh = "com.staxriver.lecountdown.refresh"
+}
class Conductor: ObservableObject {
@@ -32,6 +36,7 @@ class Conductor: ObservableObject {
@Published var currentCountdowns: [String : DateInterval] = [:] {
didSet {
Conductor.savedCountdowns = currentCountdowns
+ Logger.log("**** currentCountdowns didSet, count = \(currentCountdowns.count)")
withAnimation {
self._buildLiveTimers()
}
@@ -46,8 +51,6 @@ class Conductor: ObservableObject {
}
}
}
-
- fileprivate var _cleanupTimers: [String : Timer] = [:]
func removeLiveTimer(id: String) {
self.liveTimers.removeAll(where: { $0.id == id })
@@ -56,6 +59,8 @@ class Conductor: ObservableObject {
fileprivate func _buildLiveTimers() {
+ Logger.log("_buildLiveTimers")
+
let liveCountdowns = self.currentCountdowns.map {
return LiveTimer(id: $0, date: $1.end)
}
@@ -105,15 +110,25 @@ class Conductor: ObservableObject {
// MARK: - Countdown
func startCountdown(_ date: Date, countdown: Countdown) {
- DispatchQueue.main.async {
+// DispatchQueue.main.async {
+
+ Logger.log("Starts countdown: \(countdown.displayName)")
+
+ // cleanup existing countdowns
+ self.removeLiveTimer(id: countdown.stringId)
+
+// self._cleanupTimers.removeValue(forKey: countdown.stringId)
+
let dateInterval = DateInterval(start: Date(), end: date)
self.currentCountdowns[countdown.stringId] = dateInterval
// self._launchLiveActivity(countdown: countdown, endDate: date)
- self._cleanupTimers.removeValue(forKey: countdown.stringId)
+ self._createTimerIntent(countdown)
- }
+ Logger.log("countdowns count = \(self.currentCountdowns.count)")
+
+// }
}
func cancelCountdown(id: String) {
@@ -129,26 +144,6 @@ class Conductor: ObservableObject {
self._recordActivity(countdownId: countdownId)
}
self.currentCountdowns.removeValue(forKey: countdownId)
-
-// if self.currentCountdowns.removeValue(forKey: countdownId) != nil {
-// self._endLiveActivity(countdownId: countdownId)
-// }
-
-// self.removeLiveTimer(id: countdownId)
- }
- }
-
- func cleanup() {
- self._cleanupCountdowns()
- self._buildLiveTimers()
- }
-
- fileprivate func _cleanupCountdowns() {
- let now = Date()
- for (key, value) in self.currentCountdowns {
- if value.end < now {
- self._endCountdown(countdownId: key, cancel: false)
- }
}
}
@@ -160,7 +155,8 @@ class Conductor: ObservableObject {
Conductor.maestro.currentStopwatches[stopwatch.stringId] = now
self._launchLiveActivity(stopwatch: stopwatch, start: now)
-// LaunchTim
+ self._createTimerIntent(stopwatch)
+
}
}
@@ -176,6 +172,22 @@ class Conductor: ObservableObject {
}
}
+ // MARK: - Cleanup
+
+ func cleanup() {
+ self._cleanupCountdowns()
+ self._buildLiveTimers()
+ }
+
+ fileprivate func _cleanupCountdowns() {
+ let now = Date()
+ for (key, value) in self.currentCountdowns {
+ if value.end < now {
+ self._endCountdown(countdownId: key, cancel: false)
+ }
+ }
+ }
+
// MARK: - Sound
fileprivate func _playSound(timerId: String) {
@@ -217,6 +229,18 @@ class Conductor: ObservableObject {
self.soundPlayer = nil
}
+ // MARK: - Intent
+
+ fileprivate func _createTimerIntent(_ timer: AbstractTimer) {
+ let intent = LaunchTimerIntent()
+ intent.suggestedInvocationPhrase = "\(NSLocalizedString("Launch", comment: "")) \(timer.displayName)"
+
+ intent.timer = TimerIdentifier(identifier: timer.stringId, display: timer.displayName)
+
+ let interaction = INInteraction(intent: intent, response: nil)
+ interaction.donate()
+ }
+
// MARK: - Live Activity
fileprivate func _launchLiveActivity(stopwatch: Stopwatch, start: Date) {
diff --git a/LeCountdown/CountdownScheduler.swift b/LeCountdown/CountdownScheduler.swift
index 88735cf..680d37f 100644
--- a/LeCountdown/CountdownScheduler.swift
+++ b/LeCountdown/CountdownScheduler.swift
@@ -78,11 +78,11 @@ class CountdownScheduler {
handler(.failure(error))
print("Scheduling error = \(error)")
} else {
-
+
if offset == 0.0 {
if let triggerDate = trigger.nextTriggerDate() {
Conductor.maestro.startCountdown(triggerDate, countdown: countdown)
- handler(.success(trigger.nextTriggerDate()))
+ handler(.success(triggerDate))
} else {
let backupDate = Date().addingTimeInterval(duration)
Conductor.maestro.startCountdown(backupDate, countdown: countdown)
@@ -92,6 +92,11 @@ class CountdownScheduler {
}
}
+// DispatchQueue.main.async {
+// let d = countdown.duration + offset
+// let td = Date().addingTimeInterval(d)
+// Conductor.maestro.startCountdown(td, countdown: countdown)
+// }
}
}
diff --git a/LeCountdown/Info.plist b/LeCountdown/Info.plist
index 2c97560..40adea8 100644
--- a/LeCountdown/Info.plist
+++ b/LeCountdown/Info.plist
@@ -8,9 +8,9 @@
NSUserActivityTypes
- app.kikai.NewCountdown
LaunchTimerIntent
SelectTimerIntent
+ app.kikai.NewCountdown
UIApplicationSceneManifest
diff --git a/LeCountdown/LeCountdownApp.swift b/LeCountdown/LeCountdownApp.swift
index 275cdf4..9e66ae0 100644
--- a/LeCountdown/LeCountdownApp.swift
+++ b/LeCountdown/LeCountdownApp.swift
@@ -10,10 +10,6 @@ import CoreData
import BackgroundTasks
import AVFoundation
-enum BGTaskIdentifier : String {
- case refresh = "com.staxriver.lecountdown.refresh"
-}
-
@main
struct LeCountdownApp: App {
@@ -50,9 +46,9 @@ struct LeCountdownApp: App {
.environment(\.managedObjectContext, persistenceController.container.viewContext)
#endif
}
- .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
- self._willEnterForegroundNotification()
- }
+// .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
+// self._willEnterForegroundNotification()
+// }
.onAppear {
self._onAppear()
}
@@ -60,6 +56,10 @@ struct LeCountdownApp: App {
switch newPhase {
case .inactive:
Conductor.maestro.stopSoundIfPossible()
+ case .active:
+ Logger.log("onChange(of: scenePhase) active")
+ Logger.log(Conductor.maestro.currentCountdowns.count)
+ Conductor.maestro.cleanup()
default:
break
}
@@ -68,12 +68,14 @@ struct LeCountdownApp: App {
}
- fileprivate func _willEnterForegroundNotification() {
- Conductor.maestro.cleanup()
- }
+// fileprivate func _willEnterForegroundNotification() {
+// Conductor.maestro.cleanup()
+// }
fileprivate func _onAppear() {
+ Logger.log("Locale = \(Locale.preferredLanguages.first)")
+
Sound.computeSoundDurationsIfNecessary()
self._patch()
diff --git a/LeCountdown/Localizations/en.xcloc/Localized Contents/en.xliff b/LeCountdown/Localizations/en.xcloc/Localized Contents/en.xliff
new file mode 100644
index 0000000..8d989fa
--- /dev/null
+++ b/LeCountdown/Localizations/en.xcloc/Localized Contents/en.xliff
@@ -0,0 +1,647 @@
+
+
+
+
+
+
+ LaunchIntents
+ LaunchIntents
+ Bundle display name
+
+
+ LaunchIntents
+ LaunchIntents
+ Bundle name
+
+
+
+
+ Copyright (human-readable)
+
+
+
+
+
+
+
+ Custom
+ Custom
+ No comment provided by engineer.
+
+
+ Démarre
+ Démarre
+ No comment provided by engineer.
+
+
+ Forest stream
+ Forest stream
+ No comment provided by engineer.
+
+
+ Fun
+ Fun
+ No comment provided by engineer.
+
+
+ It's time!
+ It's time!
+ No comment provided by engineer.
+
+
+ Nature
+ Nature
+ No comment provided by engineer.
+
+
+ Time's up for (name)!
+ Time's up for (name)!
+ No comment provided by engineer.
+
+
+ Train horn
+ Train horn
+ No comment provided by engineer.
+
+
+ Your (duration.minuteSecond) countdown is over!
+ Your (duration.minuteSecond) countdown is over!
+ No comment provided by engineer.
+
+
+
+
+
+
+
+ LaunchWidget
+ LaunchWidget
+ Bundle display name
+
+
+ LaunchWidgetExtension
+ LaunchWidgetExtension
+ Bundle name
+
+
+
+
+ Copyright (human-readable)
+
+
+
+
+
+
+
+ 4:00
+ 4:00
+ No comment provided by engineer.
+
+
+ Configure me!
+ Configure me!
+ No comment provided by engineer.
+
+
+ Configure me!
+
+ Configure me!
+
+ No comment provided by engineer.
+
+
+ L
+ L
+ No comment provided by engineer.
+
+
+ Launch Widget
+ Launch Widget
+ No comment provided by engineer.
+
+
+ Min
+ Min
+ No comment provided by engineer.
+
+
+ Select and launch your timers
+ Select and launch your timers
+ No comment provided by engineer.
+
+
+ Stop
+ Stop
+ No comment provided by engineer.
+
+
+ T
+ T
+ No comment provided by engineer.
+
+
+
+
+
+
+
+ LeCountdown
+ LeCountdown
+ Bundle name
+
+
+ NSAppleMusicUsageDescription
+ NSAppleMusicUsageDescription
+ Privacy - Media Library Usage Description
+
+
+
+
+
+
+
+ %d
+ %d
+ No comment provided by engineer.
+
+
+ .create countdown
+ .create countdown
+ No comment provided by engineer.
+
+
+ .create stopwatch
+ .create stopwatch
+ No comment provided by engineer.
+
+
+ Activities
+ Activities
+ No comment provided by engineer.
+
+
+ All
+ All
+ No comment provided by engineer.
+
+
+ Average duration
+ Average duration
+ No comment provided by engineer.
+
+
+ Background
+ Background
+ No comment provided by engineer.
+
+
+ Black tea
+ Black tea
+ No comment provided by engineer.
+
+
+ Cancel
+ Cancel
+ No comment provided by engineer.
+
+
+ Cancelled
+ Cancelled
+ No comment provided by engineer.
+
+
+ Chill
+ Chill
+ No comment provided by engineer.
+
+
+ Cooking
+ Cooking
+ No comment provided by engineer.
+
+
+ Coundown
+ Coundown
+ No comment provided by engineer.
+
+
+ Count
+ Count
+ No comment provided by engineer.
+
+
+ Create
+ Create
+ No comment provided by engineer.
+
+
+ Create a new countdown
+ Create a new countdown
+ No comment provided by engineer.
+
+
+ Create countdown
+ Create countdown
+ No comment provided by engineer.
+
+
+ Custom
+ Custom
+ No comment provided by engineer.
+
+
+ Démarre
+ Démarre
+ No comment provided by engineer.
+
+
+ Do you really want to delete?
+ Do you really want to delete?
+ No comment provided by engineer.
+
+
+ Do you wish to rename or create a new activity
+ Do you wish to rename or create a new activity
+ No comment provided by engineer.
+
+
+ Don't show this again
+ Don't show this again
+ No comment provided by engineer.
+
+
+ Done
+ Done
+ No comment provided by engineer.
+
+
+ Duration
+ Duration
+ No comment provided by engineer.
+
+
+ Edit
+ Edit
+ No comment provided by engineer.
+
+
+ Edit alarm
+ Edit alarm
+ No comment provided by engineer.
+
+
+ Edit countdown
+ Edit countdown
+ No comment provided by engineer.
+
+
+ Edit stopwatch
+ Edit stopwatch
+ No comment provided by engineer.
+
+
+ Forest stream
+ Forest stream
+ No comment provided by engineer.
+
+
+ Fun
+ Fun
+ No comment provided by engineer.
+
+
+ Green tea
+ Green tea
+ No comment provided by engineer.
+
+
+ Hard boiled eggs
+ Hard boiled eggs
+ No comment provided by engineer.
+
+
+ Hello Store!
+ Hello Store!
+ No comment provided by engineer.
+
+
+ Home
+ Home
+ No comment provided by engineer.
+
+
+ It's time!
+ It's time!
+ No comment provided by engineer.
+
+
+ L
+ L
+ No comment provided by engineer.
+
+
+ Make sure your device is not on silent mode
+ Make sure your device is not on silent mode
+ No comment provided by engineer.
+
+
+ Meditation
+ Meditation
+ No comment provided by engineer.
+
+
+ Medium boiled eggs
+ Medium boiled eggs
+ No comment provided by engineer.
+
+
+ Min
+ Min
+ No comment provided by engineer.
+
+
+ Minutes
+ Minutes
+ No comment provided by engineer.
+
+
+ Month
+ Month
+ No comment provided by engineer.
+
+
+ Name (activates tracking)
+ Name (activates tracking)
+ No comment provided by engineer.
+
+
+ Name for tracking the activity
+ Name for tracking the activity
+ No comment provided by engineer.
+
+
+ Nap
+ Nap
+ No comment provided by engineer.
+
+
+ Nature
+ Nature
+ No comment provided by engineer.
+
+
+ New activity
+ New activity
+ No comment provided by engineer.
+
+
+ New alarm
+ New alarm
+ No comment provided by engineer.
+
+
+ New countdown
+ New countdown
+ No comment provided by engineer.
+
+
+ New stopwatch
+ New stopwatch
+ No comment provided by engineer.
+
+
+ No
+ No
+ No comment provided by engineer.
+
+
+ OK
+ OK
+ No comment provided by engineer.
+
+
+ Other
+ Other
+ No comment provided by engineer.
+
+
+ Play sound on end
+ Play sound on end
+ No comment provided by engineer.
+
+
+ Presets
+ Presets
+ No comment provided by engineer.
+
+
+ Properties
+ Properties
+ No comment provided by engineer.
+
+
+ Rename
+ Rename
+ No comment provided by engineer.
+
+
+ Repeat Count
+ Repeat Count
+ No comment provided by engineer.
+
+
+ Running splits
+ Running splits
+ No comment provided by engineer.
+
+
+ Save
+ Save
+ No comment provided by engineer.
+
+
+ Seconds
+ Seconds
+ No comment provided by engineer.
+
+
+ Show permissions
+ Show permissions
+ No comment provided by engineer.
+
+
+ Soft boiled eggs
+ Soft boiled eggs
+ No comment provided by engineer.
+
+
+ Sound
+ Sound
+ No comment provided by engineer.
+
+
+ Sounds
+ Sounds
+ No comment provided by engineer.
+
+
+ Stats
+ Stats
+ No comment provided by engineer.
+
+
+ Stop
+ Stop
+ No comment provided by engineer.
+
+
+ Stopwatch
+ Stopwatch
+ No comment provided by engineer.
+
+
+ T
+ T
+ No comment provided by engineer.
+
+
+ Tea
+ Tea
+ No comment provided by engineer.
+
+
+ Time
+ Time
+ No comment provided by engineer.
+
+
+ Time's up for (name)!
+ Time's up for (name)!
+ No comment provided by engineer.
+
+
+ Tooth brushing
+ Tooth brushing
+ No comment provided by engineer.
+
+
+ Train horn
+ Train horn
+ No comment provided by engineer.
+
+
+ Workout
+ Workout
+ No comment provided by engineer.
+
+
+ Year
+ Year
+ No comment provided by engineer.
+
+
+ Yes
+ Yes
+ No comment provided by engineer.
+
+
+ You can ask Siri to create and launch countdowns and stopwatches
+ You can ask Siri to create and launch countdowns and stopwatches
+ No comment provided by engineer.
+
+
+ You can edit the duration, sound and label before adding
+ You can edit the duration, sound and label before adding
+ No comment provided by engineer.
+
+
+ You don't have any recorded activity yet
+ You don't have any recorded activity yet
+ No comment provided by engineer.
+
+
+ You need to accept notifications, please check your settings
+ You need to accept notifications, please check your settings
+ No comment provided by engineer.
+
+
+ Your (duration.minuteSecond) countdown is over!
+ Your (duration.minuteSecond) countdown is over!
+ No comment provided by engineer.
+
+
+ alarm
+ alarm
+ No comment provided by engineer.
+
+
+ countdown
+ countdown
+ No comment provided by engineer.
+
+
+ date
+ date
+ No comment provided by engineer.
+
+
+ haha
+ haha
+ No comment provided by engineer.
+
+
+ missing dial view
+ missing dial view
+ No comment provided by engineer.
+
+
+ missing edit view
+ missing edit view
+ No comment provided by engineer.
+
+
+ name
+ name
+ No comment provided by engineer.
+
+
+ sounds
+ sounds
+ sounds
+
+
+ stopwatch
+ stopwatch
+ No comment provided by engineer.
+
+
+ todo
+ todo
+ No comment provided by engineer.
+
+
+ unmanaged timer: %@
+ unmanaged timer: %@
+ No comment provided by engineer.
+
+
+ value
+ value
+ No comment provided by engineer.
+
+
+
+
diff --git a/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..c7abb4a
--- /dev/null
+++ b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings
@@ -0,0 +1,6 @@
+/* Bundle display name */
+"CFBundleDisplayName" = "LaunchIntents";
+/* Bundle name */
+"CFBundleName" = "LaunchIntents";
+/* Copyright (human-readable) */
+"NSHumanReadableCopyright" = "";
diff --git a/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings
new file mode 100644
index 0000000..bba65fa
Binary files /dev/null and b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings differ
diff --git a/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..3eff3a6
--- /dev/null
+++ b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings
@@ -0,0 +1,6 @@
+/* Bundle display name */
+"CFBundleDisplayName" = "LaunchWidget";
+/* Bundle name */
+"CFBundleName" = "LaunchWidgetExtension";
+/* Copyright (human-readable) */
+"NSHumanReadableCopyright" = "";
diff --git a/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings
new file mode 100644
index 0000000..b133a5f
--- /dev/null
+++ b/LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* No comment provided by engineer. */
+"4:00" = "4:00";
+
+/* No comment provided by engineer. */
+"Configure me!" = "Configure me!";
+
+/* No comment provided by engineer. */
+"Configure me!\n" = "Configure me!\n";
+
+/* No comment provided by engineer. */
+"L" = "L";
+
+/* No comment provided by engineer. */
+"Launch Widget" = "Launch Widget";
+
+/* No comment provided by engineer. */
+"Min" = "Min";
+
+/* No comment provided by engineer. */
+"Select and launch your timers" = "Select and launch your timers";
+
+/* No comment provided by engineer. */
+"Stop" = "Stop";
+
+/* No comment provided by engineer. */
+"T" = "T";
+
diff --git a/LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings b/LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..3e9e4d7
--- /dev/null
+++ b/LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings
@@ -0,0 +1,4 @@
+/* Bundle name */
+"CFBundleName" = "LeCountdown";
+/* Privacy - Media Library Usage Description */
+"NSAppleMusicUsageDescription" = "NSAppleMusicUsageDescription";
diff --git a/LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings b/LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings
new file mode 100644
index 0000000..b8367af
Binary files /dev/null and b/LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings differ
diff --git a/LeCountdown/Localizations/en.xcloc/contents.json b/LeCountdown/Localizations/en.xcloc/contents.json
new file mode 100644
index 0000000..162c5d0
--- /dev/null
+++ b/LeCountdown/Localizations/en.xcloc/contents.json
@@ -0,0 +1,12 @@
+{
+ "developmentRegion" : "en",
+ "project" : "LeCountdown.xcodeproj",
+ "targetLocale" : "en",
+ "toolInfo" : {
+ "toolBuildNumber" : "14C18",
+ "toolID" : "com.apple.dt.xcode",
+ "toolName" : "Xcode",
+ "toolVersion" : "14.2"
+ },
+ "version" : "1.0"
+}
\ No newline at end of file
diff --git a/LeCountdown/Localizations/fr.xcloc/Localized Contents/fr.xliff b/LeCountdown/Localizations/fr.xcloc/Localized Contents/fr.xliff
new file mode 100644
index 0000000..944ef4b
--- /dev/null
+++ b/LeCountdown/Localizations/fr.xcloc/Localized Contents/fr.xliff
@@ -0,0 +1,617 @@
+
+
+
+
+
+
+ LaunchIntents
+ Bundle display name
+
+
+ LaunchIntents
+ Bundle name
+
+
+
+ Copyright (human-readable)
+
+
+
+
+
+
+
+ Custom
+ Personnalisé
+ No comment provided by engineer.
+
+
+ Démarre
+ No comment provided by engineer.
+
+
+ Forest stream
+ No comment provided by engineer.
+
+
+ Fun
+ No comment provided by engineer.
+
+
+ It's time!
+ C'est l'heure !
+ No comment provided by engineer.
+
+
+ Nature
+ Nature
+ No comment provided by engineer.
+
+
+ Time's up for (name)!
+ C'est l'heure pour (name) !
+ No comment provided by engineer.
+
+
+ Train horn
+ No comment provided by engineer.
+
+
+ Your (duration.minuteSecond) countdown is over!
+ Votre minuteur de (duration.minuteSecond) est terminé !
+ No comment provided by engineer.
+
+
+
+
+
+
+
+ LaunchWidget
+ Bundle display name
+
+
+ LaunchWidgetExtension
+ Bundle name
+
+
+
+ Copyright (human-readable)
+
+
+
+
+
+
+
+ 4:00
+ No comment provided by engineer.
+
+
+ Configure me!
+ Configurez moi !
+ No comment provided by engineer.
+
+
+ Configure me!
+
+ Configurez moi !
+ No comment provided by engineer.
+
+
+ L
+ No comment provided by engineer.
+
+
+ Launch Widget
+ Widget de lancement
+ No comment provided by engineer.
+
+
+ Min
+ Min
+ No comment provided by engineer.
+
+
+ Select and launch your timers
+ Sélectionnez et lancer vos minuteurs
+ No comment provided by engineer.
+
+
+ Stop
+ Arrêter
+ No comment provided by engineer.
+
+
+ T
+ No comment provided by engineer.
+
+
+
+
+
+
+
+ LeCountdown
+ Bundle name
+
+
+ NSAppleMusicUsageDescription
+ Privacy - Media Library Usage Description
+
+
+
+
+
+
+
+ %d
+ No comment provided by engineer.
+
+
+ .create countdown
+ .Créer minuteur
+ No comment provided by engineer.
+
+
+ .create stopwatch
+ .Créer un chrono
+ No comment provided by engineer.
+
+
+ Activities
+ Activités
+ No comment provided by engineer.
+
+
+ All
+ Tout
+ No comment provided by engineer.
+
+
+ Average duration
+ Durée moyenne
+ No comment provided by engineer.
+
+
+ Background
+ No comment provided by engineer.
+
+
+ Black tea
+ Thé noir
+ No comment provided by engineer.
+
+
+ Cancel
+ Annuler
+ No comment provided by engineer.
+
+
+ Cancelled
+ Annulé
+ No comment provided by engineer.
+
+
+ Chill
+ No comment provided by engineer.
+
+
+ Cooking
+ Cuisine
+ No comment provided by engineer.
+
+
+ Coundown
+ Minuteur
+ No comment provided by engineer.
+
+
+ Count
+ Nombre
+ No comment provided by engineer.
+
+
+ Create
+ Nouveau
+ No comment provided by engineer.
+
+
+ Create a new countdown
+ Créer un nouveau minuteur
+ No comment provided by engineer.
+
+
+ Create countdown
+ Créer un minuteur
+ No comment provided by engineer.
+
+
+ Custom
+ Personnalisé
+ No comment provided by engineer.
+
+
+ Démarre
+ No comment provided by engineer.
+
+
+ Do you really want to delete?
+ Voulez-vous vraiment effacer ?
+ No comment provided by engineer.
+
+
+ Do you wish to rename or create a new activity
+ Souhaitez-vous renommer ou créer une nouvelle activité ?
+ No comment provided by engineer.
+
+
+ Don't show this again
+ Ne plus montrer
+ No comment provided by engineer.
+
+
+ Done
+ Sauver
+ No comment provided by engineer.
+
+
+ Duration
+ Durée
+ No comment provided by engineer.
+
+
+ Edit
+ Editer
+ No comment provided by engineer.
+
+
+ Edit alarm
+ Éditer alarme
+ No comment provided by engineer.
+
+
+ Edit countdown
+ Éditer minuteur
+ No comment provided by engineer.
+
+
+ Edit stopwatch
+ Éditer chrono
+ No comment provided by engineer.
+
+
+ Forest stream
+ No comment provided by engineer.
+
+
+ Fun
+ Fun
+ No comment provided by engineer.
+
+
+ Green tea
+ Thé vert
+ No comment provided by engineer.
+
+
+ Hard boiled eggs
+ Oeufs dur
+ No comment provided by engineer.
+
+
+ Hello Store!
+ No comment provided by engineer.
+
+
+ Home
+ Kikai
+ No comment provided by engineer.
+
+
+ It's time!
+ C'est l'heure !
+ No comment provided by engineer.
+
+
+ L
+ No comment provided by engineer.
+
+
+ Make sure your device is not on silent mode
+ Vérifiez que votre appareil n'est pas en mode silencieux
+ No comment provided by engineer.
+
+
+ Meditation
+ Méditation
+ No comment provided by engineer.
+
+
+ Medium boiled eggs
+ Oeufs mollets
+ No comment provided by engineer.
+
+
+ Min
+ Min
+ No comment provided by engineer.
+
+
+ Minutes
+ Minutes
+ No comment provided by engineer.
+
+
+ Month
+ Mois
+ No comment provided by engineer.
+
+
+ Name (activates tracking)
+ Nom (active le suivi)
+ No comment provided by engineer.
+
+
+ Name for tracking the activity
+ Nom pour suivre l'activité
+ No comment provided by engineer.
+
+
+ Nap
+ Sieste
+ No comment provided by engineer.
+
+
+ Nature
+ Nature
+ No comment provided by engineer.
+
+
+ New activity
+ Nouvelle activité
+ No comment provided by engineer.
+
+
+ New alarm
+ Nouvelle alarme
+ No comment provided by engineer.
+
+
+ New countdown
+ Nouveau minuteur
+ No comment provided by engineer.
+
+
+ New stopwatch
+ Nouveau chrono
+ No comment provided by engineer.
+
+
+ No
+ Non
+ No comment provided by engineer.
+
+
+ OK
+ OK
+ No comment provided by engineer.
+
+
+ Other
+ Autre
+ No comment provided by engineer.
+
+
+ Play sound on end
+ Jouer un son de fin
+ No comment provided by engineer.
+
+
+ Presets
+ Préselection
+ No comment provided by engineer.
+
+
+ Properties
+ Propriétes
+ No comment provided by engineer.
+
+
+ Rename
+ Renommer
+ No comment provided by engineer.
+
+
+ Repeat Count
+ Nombre de répétitions
+ No comment provided by engineer.
+
+
+ Running splits
+ No comment provided by engineer.
+
+
+ Save
+ Sauvegarder
+ No comment provided by engineer.
+
+
+ Seconds
+ Secondes
+ No comment provided by engineer.
+
+
+ Show permissions
+ Voir permissions
+ No comment provided by engineer.
+
+
+ Soft boiled eggs
+ Oeufs coque
+ No comment provided by engineer.
+
+
+ Sound
+ Son
+ No comment provided by engineer.
+
+
+ Sounds
+ Sons
+ No comment provided by engineer.
+
+
+ Stats
+ Statistiques
+ No comment provided by engineer.
+
+
+ Stop
+ Arrêter
+ No comment provided by engineer.
+
+
+ Stopwatch
+ Chrono
+ No comment provided by engineer.
+
+
+ T
+ No comment provided by engineer.
+
+
+ Tea
+ Thé
+ No comment provided by engineer.
+
+
+ Time
+ Temps
+ No comment provided by engineer.
+
+
+ Time's up for (name)!
+ C'est l'heure pour (name) !
+ No comment provided by engineer.
+
+
+ Tooth brushing
+ Brossage de dent
+ No comment provided by engineer.
+
+
+ Train horn
+ No comment provided by engineer.
+
+
+ Workout
+ Exercice
+ No comment provided by engineer.
+
+
+ Year
+ Année
+ No comment provided by engineer.
+
+
+ Yes
+ Oui
+ No comment provided by engineer.
+
+
+ You can ask Siri to create and launch countdowns and stopwatches
+ Vous pouvez demander à Siri de vous lancer les minuteurs et les chronos
+ No comment provided by engineer.
+
+
+ You can edit the duration, sound and label before adding
+ Vous pouvez changer la durée, le son et le label avant l'ajout
+ No comment provided by engineer.
+
+
+ You don't have any recorded activity yet
+ Vous n'avez pas encore d'activités enregistrées
+ No comment provided by engineer.
+
+
+ You need to accept notifications, please check your settings
+ Vous devez accepter les notifications, veuillez vérifier vos réglages
+ No comment provided by engineer.
+
+
+ Your (duration.minuteSecond) countdown is over!
+ Votre minuteur de (duration.minuteSecond) est terminé !
+ No comment provided by engineer.
+
+
+ alarm
+ Alarme
+ No comment provided by engineer.
+
+
+ countdown
+ compteur
+ No comment provided by engineer.
+
+
+ date
+ Date
+ No comment provided by engineer.
+
+
+ haha
+
+ No comment provided by engineer.
+
+
+ missing dial view
+ No comment provided by engineer.
+
+
+ missing edit view
+ No comment provided by engineer.
+
+
+ name
+ nom
+ No comment provided by engineer.
+
+
+ sounds
+ sons
+ sounds
+
+
+ stopwatch
+ chrono
+ No comment provided by engineer.
+
+
+ todo
+ No comment provided by engineer.
+
+
+ unmanaged timer: %@
+ No comment provided by engineer.
+
+
+ value
+ valeur
+ No comment provided by engineer.
+
+
+
+
diff --git a/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..c7abb4a
--- /dev/null
+++ b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings
@@ -0,0 +1,6 @@
+/* Bundle display name */
+"CFBundleDisplayName" = "LaunchIntents";
+/* Bundle name */
+"CFBundleName" = "LaunchIntents";
+/* Copyright (human-readable) */
+"NSHumanReadableCopyright" = "";
diff --git a/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings
new file mode 100644
index 0000000..bba65fa
Binary files /dev/null and b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings differ
diff --git a/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..3eff3a6
--- /dev/null
+++ b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings
@@ -0,0 +1,6 @@
+/* Bundle display name */
+"CFBundleDisplayName" = "LaunchWidget";
+/* Bundle name */
+"CFBundleName" = "LaunchWidgetExtension";
+/* Copyright (human-readable) */
+"NSHumanReadableCopyright" = "";
diff --git a/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings
new file mode 100644
index 0000000..b133a5f
--- /dev/null
+++ b/LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* No comment provided by engineer. */
+"4:00" = "4:00";
+
+/* No comment provided by engineer. */
+"Configure me!" = "Configure me!";
+
+/* No comment provided by engineer. */
+"Configure me!\n" = "Configure me!\n";
+
+/* No comment provided by engineer. */
+"L" = "L";
+
+/* No comment provided by engineer. */
+"Launch Widget" = "Launch Widget";
+
+/* No comment provided by engineer. */
+"Min" = "Min";
+
+/* No comment provided by engineer. */
+"Select and launch your timers" = "Select and launch your timers";
+
+/* No comment provided by engineer. */
+"Stop" = "Stop";
+
+/* No comment provided by engineer. */
+"T" = "T";
+
diff --git a/LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings b/LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..3e9e4d7
--- /dev/null
+++ b/LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings
@@ -0,0 +1,4 @@
+/* Bundle name */
+"CFBundleName" = "LeCountdown";
+/* Privacy - Media Library Usage Description */
+"NSAppleMusicUsageDescription" = "NSAppleMusicUsageDescription";
diff --git a/LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings b/LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings
new file mode 100644
index 0000000..b8367af
Binary files /dev/null and b/LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings differ
diff --git a/LeCountdown/Localizations/fr.xcloc/contents.json b/LeCountdown/Localizations/fr.xcloc/contents.json
new file mode 100644
index 0000000..423be3c
--- /dev/null
+++ b/LeCountdown/Localizations/fr.xcloc/contents.json
@@ -0,0 +1,12 @@
+{
+ "developmentRegion" : "en",
+ "project" : "LeCountdown.xcodeproj",
+ "targetLocale" : "fr",
+ "toolInfo" : {
+ "toolBuildNumber" : "14C18",
+ "toolID" : "com.apple.dt.xcode",
+ "toolName" : "Xcode",
+ "toolVersion" : "14.2"
+ },
+ "version" : "1.0"
+}
\ No newline at end of file
diff --git a/LeCountdown/Model/Model+Extensions.swift b/LeCountdown/Model/Model+Extensions.swift
index e13ce5e..017c097 100644
--- a/LeCountdown/Model/Model+Extensions.swift
+++ b/LeCountdown/Model/Model+Extensions.swift
@@ -9,9 +9,6 @@ import Foundation
import SwiftUI
import CoreData
-enum TimerError: Error {
- case notificationAuthorizationMissing
-}
extension AbstractSoundTimer {
@@ -106,19 +103,6 @@ extension Record {
}
}
- func point(stat: Stat) -> Point? {
- if let start {
- let day = start.startOfDay
- switch stat {
- case .count:
- return Point(date: day, value: NSNumber(value: 1))
- case .totalDuration, .averageDuration:
- return Point(date: day, value: NSNumber(value: self.duration / 3600.0))
- }
- }
- return nil
- }
-
}
extension Activity {
diff --git a/LeCountdown/Stats/Context+Calculations.swift b/LeCountdown/Stats/Context+Calculations.swift
index 4090fa8..bdfcf7a 100644
--- a/LeCountdown/Stats/Context+Calculations.swift
+++ b/LeCountdown/Stats/Context+Calculations.swift
@@ -94,3 +94,20 @@ extension NSManagedObjectContext {
return statValues
}
}
+
+fileprivate extension Record {
+
+ func point(stat: Stat) -> Point? {
+ if let start {
+ let day = start.startOfDay
+ switch stat {
+ case .count:
+ return Point(date: day, value: NSNumber(value: 1))
+ case .totalDuration, .averageDuration:
+ return Point(date: day, value: NSNumber(value: self.duration / 3600.0))
+ }
+ }
+ return nil
+ }
+
+}
diff --git a/LeCountdown/TimerRouter.swift b/LeCountdown/TimerRouter.swift
index 74a02ff..17110cf 100644
--- a/LeCountdown/TimerRouter.swift
+++ b/LeCountdown/TimerRouter.swift
@@ -8,17 +8,38 @@
import Foundation
import NotificationCenter
+enum TimerRouterError: Error {
+ case notificationAuthorizationMissing
+ case unmanagedTimer(_ timer: AbstractTimer)
+}
+
class TimerRouter {
- static func performAction(timer: AbstractTimer, handler: @escaping (Result) -> Void) {
+ static func performAction(timer: AbstractTimer) async throws -> Bool {
+
+ return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in
+ self.performAction(timer: timer) { result in
+ switch result {
+ case .success:
+ continuation.resume(returning: true)
+ case .failure(let failure):
+ continuation.resume(throwing: failure)
+ }
+ }
+ }
+
+ }
+
+ static func performAction(timer: AbstractTimer, handler: @escaping (Result) -> Void) {
switch timer {
case let countdown as Countdown:
self._launchCountdown(countdown, handler: handler)
- case let alarm as Alarm:
- self._scheduleAlarm(alarm, handler: handler)
case let stopwatch as Stopwatch:
self._startStopwatch(stopwatch, handler: handler)
+ case let alarm as Alarm:
+ self._scheduleAlarm(alarm, handler: handler)
default:
+ handler(Result.failure(TimerRouterError.unmanagedTimer(timer)))
print("missing launcher for \(self)")
}
@@ -35,17 +56,17 @@ class TimerRouter {
}
}
- fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result) -> Void) {
+ fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result) -> Void) {
UNUserNotificationCenter.current().getNotificationSettings { settings in
switch settings.authorizationStatus {
case .notDetermined, .denied:
- handler(.failure(TimerError.notificationAuthorizationMissing))
+ handler(.failure(TimerRouterError.notificationAuthorizationMissing))
default:
CountdownScheduler.master.scheduleIfPossible(countdown: countdown) { result in
switch result {
- case .success(_):
- handler(.success(true))
+ case .success:
+ handler(.success(Void()))
case .failure(let failure):
handler(.failure(failure))
}
@@ -55,12 +76,13 @@ class TimerRouter {
}
- fileprivate static func _scheduleAlarm(_ alarm: Alarm, handler: @escaping (Result) -> Void) {
+ fileprivate static func _scheduleAlarm(_ alarm: Alarm, handler: @escaping (Result) -> Void) {
}
- fileprivate static func _startStopwatch(_ stopwatch: Stopwatch, handler: @escaping (Result) -> Void) {
+ fileprivate static func _startStopwatch(_ stopwatch: Stopwatch, handler: @escaping (Result) -> Void) {
Conductor.maestro.startStopwatch(stopwatch)
+ handler(.success(Void()))
}
fileprivate static func _stopStopwatch(_ stopwatch: Stopwatch, end: Date? = nil) {
diff --git a/LeCountdown/Views/ContentView.swift b/LeCountdown/Views/ContentView.swift
index b9a5305..0379191 100644
--- a/LeCountdown/Views/ContentView.swift
+++ b/LeCountdown/Views/ContentView.swift
@@ -8,7 +8,6 @@
import SwiftUI
import CoreData
import Combine
-//import CloudKit
class BoringContext : ObservableObject {
@@ -42,12 +41,6 @@ class TimerSpot : Identifiable, Equatable {
}
-//class TimersModel : ObservableObject {
-//
-// @Published var spots: [TimerSpot] = []
-//
-//}
-
struct ContentView: View {
@StateObject var boringContext: BoringContext = BoringContext()
@@ -60,11 +53,6 @@ struct ContentView: View {
animation: .default)
private var timers: FetchedResults
-// var coreDataPublisher: NotificationCenter.Publisher { NotificationCenter.default
-// .publisher(for: .NSManagedObjectContextDidSave, object: viewContext) }
-
- @State private var showLiveTimersSheet = false
-
@State private var isEditing: Bool = false
fileprivate let itemSpacing: CGFloat = 10.0
@@ -139,22 +127,9 @@ struct ContentView: View {
}
}
}
-// .onReceive(cloudkitPublisher, perform: { _ in
-// print(">>>cloudkitPublisher")
-// withAnimation {
-// self._buildItemsList()
-// }
-// })
-// .onReceive(coreDataPublisher, perform: { _ in
-// withAnimation {
-// self._buildItemsList()
-// }
-// })
.onAppear {
-// self._buildItemsList()
self._askPermissions()
-
- self.showLiveTimersSheet = !conductor.liveTimers.isEmpty
+// self.showLiveTimersSheet = !conductor.liveTimers.isEmpty
}
.onOpenURL { url in
self._performActionIfPossible(url: url)
@@ -177,59 +152,9 @@ struct ContentView: View {
fileprivate func _columns() -> [GridItem] {
return (0..) -> some View {
-// switch T.self {
-// case is Countdown.Type:
-// NewCountdownView(isPresented: isPresented, tabSelection: self.tabse)
-// case is Alarm.Type:
-// NewAlarmView(isPresented: isPresented)
-// case is Stopwatch.Type:
-// NewStopwatchView(isPresented: isPresented, tabSelection: <#Binding#>)
-// default:
-// NewDataView(isPresented: isPresented, tabSelection: <#Binding#>)
-// }
-//
-// }
-
+
// MARK: - Business
-// fileprivate func _reorderSpots(from: IndexSet, to: Int) {
-// var spots: [TimerSpot] = self.model.spots
-// spots.move(fromOffsets: from, toOffset: to)
-// self.model.spots = spots
-// }
-
fileprivate func _reorder(from: IndexSet, to: Int) {
var timers: [AbstractTimer] = Array(self.timers)
timers.move(fromOffsets: from, toOffset: to)
@@ -245,7 +170,7 @@ struct ContentView: View {
}
fileprivate func _askPermissions() {
- UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .criticalAlert]) { success, error in
+ UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { success, error in
print("requestAuthorization > success = \(success), error = \(String(describing: error))")
}
}
@@ -269,7 +194,7 @@ struct ContentView: View {
break
case .failure(let failure):
switch failure {
- case TimerError.notificationAuthorizationMissing:
+ case TimerRouterError.notificationAuthorizationMissing:
self.boringContext.showPermissionAlert = true
default:
self.boringContext.error = failure
@@ -333,13 +258,13 @@ fileprivate extension Countdown {
return endDate != nil
}
- var colorForStatus: Color {
- if isLive {
- return Color(red: 0.9, green: 1.0, blue: 0.95)
- } else {
- return Color(red: 0.9, green: 0.95, blue: 1.0)
- }
- }
+// var colorForStatus: Color {
+// if isLive {
+// return Color(red: 0.9, green: 1.0, blue: 0.95)
+// } else {
+// return Color(red: 0.9, green: 0.95, blue: 1.0)
+// }
+// }
}
diff --git a/LeCountdown/Views/Countdown/NewCountdownView.swift b/LeCountdown/Views/Countdown/NewCountdownView.swift
index 23fbdfb..1d79e5a 100644
--- a/LeCountdown/Views/Countdown/NewCountdownView.swift
+++ b/LeCountdown/Views/Countdown/NewCountdownView.swift
@@ -8,6 +8,7 @@
import SwiftUI
import CoreData
import WidgetKit
+import Intents
struct NewCountdownView : View {
@@ -21,6 +22,8 @@ struct NewCountdownView : View {
_isPresented = isPresented
self.tabSelection = tabSelection
self.userActivity = Shortcut.newCountdown.userActivity
+
+// let shortcut = INShortcut(userActivity: self.userActivity)
}
var body: some View {
@@ -312,13 +315,16 @@ struct CountdownEditView : View {
guard let countdown else {
return
}
-
+ // remove the siri donation
+ INInteraction.delete(with: [countdown.stringId]) { _ in }
+
viewContext.delete(countdown)
self._saveContext()
WidgetCenter.shared.reloadAllTimelines() // refreshes the visual of existing widgets
self._popOrDismiss()
+
}
fileprivate func _saveContext() {
diff --git a/LeCountdown/Views/DialView.swift b/LeCountdown/Views/DialView.swift
index fbd264f..022c710 100644
--- a/LeCountdown/Views/DialView.swift
+++ b/LeCountdown/Views/DialView.swift
@@ -132,7 +132,7 @@ struct DialView: View {
break
case .failure(let failure):
switch failure {
- case TimerError.notificationAuthorizationMissing:
+ case TimerRouterError.notificationAuthorizationMissing:
self.boringContext.showPermissionAlert = true
default:
self.boringContext.error = failure
diff --git a/LeCountdown/Views/HomeView.swift b/LeCountdown/Views/HomeView.swift
index 4acfabc..b76f651 100644
--- a/LeCountdown/Views/HomeView.swift
+++ b/LeCountdown/Views/HomeView.swift
@@ -21,36 +21,33 @@ struct CompactHomeView: View {
var body: some View {
+ NavigationView {
+
TabView(selection: $tabSelection) {
- NavigationStack {
- PresetsView(tabSelection: $tabSelection)
- .environment(\.managedObjectContext, viewContext)
- .tabItem { Label("Presets", systemImage: "globe") }
+ PresetsView(tabSelection: $tabSelection)
+ .environment(\.managedObjectContext, viewContext)
+ .tabItem { Label("Presets", systemImage: "globe") }
.tag(0)
- }
- NavigationStack {
- ContentView()
- .environment(\.managedObjectContext, viewContext)
- .environmentObject(Conductor.maestro)
- .tabItem { Label("Home", systemImage: "clock.fill") }
+ ContentView()
+ .environment(\.managedObjectContext, viewContext)
+ .environmentObject(Conductor.maestro)
+ .tabItem { Label("Home", systemImage: "clock.fill") }
.tag(1)
- }
- NavigationStack {
- ActivitiesView()
- .environment(\.managedObjectContext, viewContext)
- .tabItem { Label("Stats", systemImage: "chart.bar.fill") }
+ ActivitiesView()
+ .environment(\.managedObjectContext, viewContext)
+ .tabItem { Label("Stats", systemImage: "chart.bar.fill") }
.tag(2)
- }
}
.tabViewStyle(.page(indexDisplayMode: .never))
- .onAppear {
- if self.timers.count > 0 {
- self.tabSelection = 1
- }
- }
- .onOpenURL { _ in
+ }
+ .onAppear {
+ if self.timers.count > 0 {
self.tabSelection = 1
}
+ }
+ .onOpenURL { _ in
+ self.tabSelection = 1
+ }
}
}
diff --git a/LeCountdown/Views/Stopwatch/NewStopwatchView.swift b/LeCountdown/Views/Stopwatch/NewStopwatchView.swift
index 599b35f..6c95477 100644
--- a/LeCountdown/Views/Stopwatch/NewStopwatchView.swift
+++ b/LeCountdown/Views/Stopwatch/NewStopwatchView.swift
@@ -8,6 +8,7 @@
import SwiftUI
import CoreData
import WidgetKit
+import Intents
struct NewStopwatchView: View {
@@ -294,13 +295,16 @@ struct StopwatchEditView: View {
guard let stopwatch else {
return
}
-
+ // remove the siri donation
+ INInteraction.delete(with: [stopwatch.stringId]) { _ in }
+
viewContext.delete(stopwatch)
self._saveContext()
WidgetCenter.shared.reloadAllTimelines() // refreshes the visual of existing widgets
self._popOrDismiss()
+
}
fileprivate func _saveContext() {
diff --git a/LeCountdown/Widget/LaunchWidgetAttributes.swift b/LeCountdown/Widget/LaunchWidgetAttributes.swift
new file mode 100644
index 0000000..1273017
--- /dev/null
+++ b/LeCountdown/Widget/LaunchWidgetAttributes.swift
@@ -0,0 +1,22 @@
+//
+// LaunchWidgetAttributes.swift
+// LeCountdown
+//
+// Created by Laurent Morvillier on 24/02/2023.
+//
+
+import Foundation
+import ActivityKit
+
+struct LaunchWidgetAttributes: ActivityAttributes {
+ public struct ContentState: Codable, Hashable {
+ // Dynamic stateful properties about your activity go here!
+ var ended: Bool
+ }
+
+ // Fixed non-changing properties about your activity go here!
+ var id: String
+ var name: String
+ var date: Date
+
+}
diff --git a/LeCountdown/SiriIntents.intentdefinition b/LeCountdown/en.lproj/SiriIntents.intentdefinition
similarity index 88%
rename from LeCountdown/SiriIntents.intentdefinition
rename to LeCountdown/en.lproj/SiriIntents.intentdefinition
index ca40c31..2116907 100644
--- a/LeCountdown/SiriIntents.intentdefinition
+++ b/LeCountdown/en.lproj/SiriIntents.intentdefinition
@@ -19,46 +19,28 @@
INIntentCategory
start
- INIntentConfigurable
-
INIntentDescription
Launch timers and stopwatches
INIntentDescriptionID
NdKydA
- INIntentInput
- timer
- INIntentKeyParameter
- timer
INIntentLastParameterTag
2
- INIntentManagedParameterCombinations
-
- timer
-
- INIntentParameterCombinationSupportsBackgroundExecution
-
- INIntentParameterCombinationTitle
- Launch your timer
- INIntentParameterCombinationTitleID
- PE02Dd
- INIntentParameterCombinationUpdatesLinked
-
-
-
INIntentName
LaunchTimer
INIntentParameterCombinations
timer
- INIntentParameterCombinationIsLinked
-
INIntentParameterCombinationIsPrimary
+ INIntentParameterCombinationSubtitle
+ Starts immediately
+ INIntentParameterCombinationSubtitleID
+ r4Ikzx
INIntentParameterCombinationSupportsBackgroundExecution
INIntentParameterCombinationTitle
- Launch your timer
+ Launch ${timer}
INIntentParameterCombinationTitleID
JfqtH6
@@ -77,7 +59,7 @@
INIntentParameterName
timer
INIntentParameterObjectType
- Type
+ TimerIdentifier
INIntentParameterObjectTypeNamespace
ggxqDx
INIntentParameterPromptDialogs
@@ -142,7 +124,7 @@
INIntentResponseParameterName
timer
INIntentResponseParameterObjectType
- Type
+ TimerIdentifier
INIntentResponseParameterObjectTypeNamespace
ggxqDx
INIntentResponseParameterTag
@@ -166,13 +148,13 @@
INTypeDisplayName
- Timer
+ Timer Identifier
INTypeDisplayNameID
02RXTq
INTypeLastPropertyTag
99
INTypeName
- Type
+ TimerIdentifier
INTypeProperties
diff --git a/LeCountdown/fr.lproj/Localizable.strings b/LeCountdown/fr.lproj/Localizable.strings
new file mode 100644
index 0000000..b2265a5
--- /dev/null
+++ b/LeCountdown/fr.lproj/Localizable.strings
@@ -0,0 +1,241 @@
+/* No comment provided by engineer. */
+".create countdown" = ".Créer minuteur";
+
+/* No comment provided by engineer. */
+".create stopwatch" = ".Créer un chrono";
+
+/* No comment provided by engineer. */
+"Activities" = "Activités";
+
+/* No comment provided by engineer. */
+"alarm" = "Alarme";
+
+/* No comment provided by engineer. */
+"All" = "Tout";
+
+/* No comment provided by engineer. */
+"Average duration" = "Durée moyenne";
+
+/* No comment provided by engineer. */
+"Black tea" = "Thé noir";
+
+/* No comment provided by engineer. */
+"Cancel" = "Annuler";
+
+/* No comment provided by engineer. */
+"Cancelled" = "Annulé";
+
+/* No comment provided by engineer. */
+"Cooking" = "Cuisine";
+
+/* No comment provided by engineer. */
+"Coundown" = "Minuteur";
+
+/* No comment provided by engineer. */
+"Count" = "Nombre";
+
+/* No comment provided by engineer. */
+"countdown" = "compteur";
+
+/* No comment provided by engineer. */
+"Create" = "Nouveau";
+
+/* No comment provided by engineer. */
+"Create a new countdown" = "Créer un nouveau minuteur";
+
+/* No comment provided by engineer. */
+"Create countdown" = "Créer un minuteur";
+
+/* No comment provided by engineer. */
+"Custom" = "Personnalisé";
+
+/* No comment provided by engineer. */
+"date" = "Date";
+
+/* No comment provided by engineer. */
+"Do you really want to delete?" = "Voulez-vous vraiment effacer ?";
+
+/* No comment provided by engineer. */
+"Do you wish to rename or create a new activity" = "Souhaitez-vous renommer ou créer une nouvelle activité ?";
+
+/* No comment provided by engineer. */
+"Don't show this again" = "Ne plus montrer";
+
+/* No comment provided by engineer. */
+"Done" = "Sauver";
+
+/* No comment provided by engineer. */
+"Duration" = "Durée";
+
+/* No comment provided by engineer. */
+"Edit" = "Editer";
+
+/* No comment provided by engineer. */
+"Edit alarm" = "Éditer alarme";
+
+/* No comment provided by engineer. */
+"Edit countdown" = "Éditer minuteur";
+
+/* No comment provided by engineer. */
+"Edit stopwatch" = "Éditer chrono";
+
+/* No comment provided by engineer. */
+"Fun" = "Fun";
+
+/* No comment provided by engineer. */
+"Green tea" = "Thé vert";
+
+/* No comment provided by engineer. */
+"haha" = "";
+
+/* No comment provided by engineer. */
+"Hard boiled eggs" = "Oeufs dur";
+
+/* No comment provided by engineer. */
+"Home" = "Kikai";
+
+/* No comment provided by engineer. */
+"It's time!" = "C'est l'heure !";
+
+/* No comment provided by engineer. */
+"Make sure your device is not on silent mode" = "Vérifiez que votre appareil n'est pas en mode silencieux";
+
+/* No comment provided by engineer. */
+"Meditation" = "Méditation";
+
+/* No comment provided by engineer. */
+"Medium boiled eggs" = "Oeufs mollets";
+
+/* No comment provided by engineer. */
+"Min" = "Min";
+
+/* No comment provided by engineer. */
+"Minutes" = "Minutes";
+
+/* No comment provided by engineer. */
+"Month" = "Mois";
+
+/* No comment provided by engineer. */
+"name" = "nom";
+
+/* No comment provided by engineer. */
+"Name (activates tracking)" = "Nom (active le suivi)";
+
+/* No comment provided by engineer. */
+"Name for tracking the activity" = "Nom pour suivre l'activité";
+
+/* No comment provided by engineer. */
+"Nap" = "Sieste";
+
+/* No comment provided by engineer. */
+"Nature" = "Nature";
+
+/* No comment provided by engineer. */
+"New activity" = "Nouvelle activité";
+
+/* No comment provided by engineer. */
+"New alarm" = "Nouvelle alarme";
+
+/* No comment provided by engineer. */
+"New countdown" = "Nouveau minuteur";
+
+/* No comment provided by engineer. */
+"New stopwatch" = "Nouveau chrono";
+
+/* No comment provided by engineer. */
+"No" = "Non";
+
+/* No comment provided by engineer. */
+"OK" = "OK";
+
+/* No comment provided by engineer. */
+"Other" = "Autre";
+
+/* No comment provided by engineer. */
+"Play sound on end" = "Jouer un son de fin";
+
+/* No comment provided by engineer. */
+"Presets" = "Préselection";
+
+/* No comment provided by engineer. */
+"Properties" = "Propriétes";
+
+/* No comment provided by engineer. */
+"Rename" = "Renommer";
+
+/* No comment provided by engineer. */
+"Repeat Count" = "Nombre de répétitions";
+
+/* No comment provided by engineer. */
+"Save" = "Sauvegarder";
+
+/* No comment provided by engineer. */
+"Seconds" = "Secondes";
+
+/* No comment provided by engineer. */
+"Show permissions" = "Voir permissions";
+
+/* No comment provided by engineer. */
+"Soft boiled eggs" = "Oeufs coque";
+
+/* No comment provided by engineer. */
+"Sound" = "Son";
+
+/* sounds */
+"sounds" = "sons";
+
+/* No comment provided by engineer. */
+"Sounds" = "Sons";
+
+/* No comment provided by engineer. */
+"Stats" = "Statistiques";
+
+/* No comment provided by engineer. */
+"Stop" = "Arrêter";
+
+/* No comment provided by engineer. */
+"stopwatch" = "chrono";
+
+/* No comment provided by engineer. */
+"Stopwatch" = "Chrono";
+
+/* No comment provided by engineer. */
+"Tea" = "Thé";
+
+/* No comment provided by engineer. */
+"Time" = "Temps";
+
+/* No comment provided by engineer. */
+"Time's up for (name)!" = "C'est l'heure pour (name) !";
+
+/* No comment provided by engineer. */
+"Tooth brushing" = "Brossage de dent";
+
+/* No comment provided by engineer. */
+"value" = "valeur";
+
+/* No comment provided by engineer. */
+"Workout" = "Exercice";
+
+/* No comment provided by engineer. */
+"Year" = "Année";
+
+/* No comment provided by engineer. */
+"Yes" = "Oui";
+
+/* No comment provided by engineer. */
+"You can ask Siri to create and launch countdowns and stopwatches" = "Vous pouvez demander à Siri de vous lancer les minuteurs et les chronos";
+
+/* No comment provided by engineer. */
+"You can edit the duration, sound and label before adding" = "Vous pouvez changer la durée, le son et le label avant l'ajout";
+
+/* No comment provided by engineer. */
+"You don't have any recorded activity yet" = "Vous n'avez pas encore d'activités enregistrées";
+
+/* No comment provided by engineer. */
+"You need to accept notifications, please check your settings" = "Vous devez accepter les notifications, veuillez vérifier vos réglages";
+
+/* No comment provided by engineer. */
+"Your (duration.minuteSecond) countdown is over!" = "Votre minuteur de (duration.minuteSecond) est terminé !";
+
+"Launch ${timer}" = "Démarrer ${timer}";
diff --git a/LeCountdown/fr.lproj/SiriIntents.intentdefinition b/LeCountdown/fr.lproj/SiriIntents.intentdefinition
new file mode 100644
index 0000000..444e012
--- /dev/null
+++ b/LeCountdown/fr.lproj/SiriIntents.intentdefinition
@@ -0,0 +1,214 @@
+
+
+
+
+ INEnums
+
+ INIntentDefinitionModelVersion
+ 1.2
+ INIntentDefinitionNamespace
+ ggxqDx
+ INIntentDefinitionSystemVersion
+ 22A400
+ INIntentDefinitionToolsBuildVersion
+ 14C18
+ INIntentDefinitionToolsVersion
+ 14.2
+ INIntents
+
+
+ INIntentCategory
+ start
+ INIntentDescription
+ Minuteurs et chronos
+ INIntentDescriptionID
+ NdKydA
+ INIntentLastParameterTag
+ 2
+ INIntentName
+ LaunchTimer
+ INIntentParameterCombinations
+
+ timer
+
+ INIntentParameterCombinationIsPrimary
+
+ INIntentParameterCombinationSubtitle
+ Lancement immédiat
+ INIntentParameterCombinationSubtitleID
+ r4Ikzx
+ INIntentParameterCombinationSupportsBackgroundExecution
+
+ INIntentParameterCombinationTitle
+ Démarrer ${timer}
+ INIntentParameterCombinationTitleID
+ JfqtH6
+
+
+ INIntentParameters
+
+
+ INIntentParameterConfigurable
+
+ INIntentParameterDisplayName
+ Compteur
+ INIntentParameterDisplayNameID
+ wU1mYs
+ INIntentParameterDisplayPriority
+ 1
+ INIntentParameterName
+ timer
+ INIntentParameterObjectType
+ TimerIdentifier
+ INIntentParameterObjectTypeNamespace
+ ggxqDx
+ INIntentParameterPromptDialogs
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogType
+ Configuration
+
+
+ INIntentParameterPromptDialogCustom
+
+ INIntentParameterPromptDialogFormatString
+ This is the mandatory prompt for your ${timer}
+ INIntentParameterPromptDialogFormatStringID
+ 6ZcaR8
+ INIntentParameterPromptDialogType
+ Primary
+
+
+ INIntentParameterSupportsResolution
+
+ INIntentParameterTag
+ 2
+ INIntentParameterType
+ Object
+
+
+ INIntentResponse
+
+ INIntentResponseCodes
+
+
+ INIntentResponseCodeFormatString
+ Your ${timer} has been launched
+ INIntentResponseCodeFormatStringID
+ E3Sz5n
+ INIntentResponseCodeName
+ success
+ INIntentResponseCodeSuccess
+
+
+
+ INIntentResponseCodeName
+ failure
+
+
+ INIntentResponseLastParameterTag
+ 2
+ INIntentResponseOutput
+ timer
+ INIntentResponseParameters
+
+
+ INIntentResponseParameterDisplayName
+ Timer
+ INIntentResponseParameterDisplayNameID
+ Vfpf1t
+ INIntentResponseParameterDisplayPriority
+ 1
+ INIntentResponseParameterName
+ timer
+ INIntentResponseParameterObjectType
+ TimerIdentifier
+ INIntentResponseParameterObjectTypeNamespace
+ ggxqDx
+ INIntentResponseParameterTag
+ 2
+ INIntentResponseParameterType
+ Object
+
+
+
+ INIntentTitle
+ Démarrer vos compteurs
+ INIntentTitleID
+ nrTIGB
+ INIntentType
+ Custom
+ INIntentVerb
+ Start
+
+
+ INTypes
+
+
+ INTypeDisplayName
+ Timer Identifier
+ INTypeDisplayNameID
+ 02RXTq
+ INTypeLastPropertyTag
+ 99
+ INTypeName
+ TimerIdentifier
+ INTypeProperties
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 1
+ INTypePropertyName
+ identifier
+ INTypePropertyTag
+ 1
+ INTypePropertyType
+ String
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 2
+ INTypePropertyName
+ displayString
+ INTypePropertyTag
+ 2
+ INTypePropertyType
+ String
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 3
+ INTypePropertyName
+ pronunciationHint
+ INTypePropertyTag
+ 3
+ INTypePropertyType
+ String
+
+
+ INTypePropertyDefault
+
+ INTypePropertyDisplayPriority
+ 4
+ INTypePropertyName
+ alternativeSpeakableMatches
+ INTypePropertySupportsMultipleValues
+
+ INTypePropertyTag
+ 4
+ INTypePropertyType
+ SpeakableString
+
+
+
+
+
+