various improvements

release
Laurent 3 years ago
parent 88d85ed49c
commit 89fdb849e4
  1. 1
      LaunchIntents/Info.plist
  2. 31
      LaunchIntents/IntentHandler.swift
  3. 15
      LaunchIntents/fr.lproj/Localizable.strings
  4. 13
      LaunchWidget/LaunchWidgetLiveActivity.swift
  5. 0
      LaunchWidget/en.lproj/LaunchWidget.intentdefinition
  6. 264
      LaunchWidget/fr.lproj/LaunchWidget.intentdefinition
  7. 18
      LaunchWidget/fr.lproj/Localizable.strings
  8. 127
      LeCountdown.xcodeproj/project.pbxproj
  9. 12
      LeCountdown/AppDelegate.swift
  10. 78
      LeCountdown/Conductor.swift
  11. 9
      LeCountdown/CountdownScheduler.swift
  12. 2
      LeCountdown/Info.plist
  13. 22
      LeCountdown/LeCountdownApp.swift
  14. 647
      LeCountdown/Localizations/en.xcloc/Localized Contents/en.xliff
  15. 6
      LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings
  16. BIN
      LeCountdown/Localizations/en.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings
  17. 6
      LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings
  18. 27
      LeCountdown/Localizations/en.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings
  19. 4
      LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings
  20. BIN
      LeCountdown/Localizations/en.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings
  21. 12
      LeCountdown/Localizations/en.xcloc/contents.json
  22. 617
      LeCountdown/Localizations/fr.xcloc/Localized Contents/fr.xliff
  23. 6
      LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/InfoPlist.strings
  24. BIN
      LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchIntents/en.lproj/Localizable.strings
  25. 6
      LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/InfoPlist.strings
  26. 27
      LeCountdown/Localizations/fr.xcloc/Source Contents/LaunchWidget/en.lproj/Localizable.strings
  27. 4
      LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/InfoPlist.strings
  28. BIN
      LeCountdown/Localizations/fr.xcloc/Source Contents/LeCountdown/en.lproj/Localizable.strings
  29. 12
      LeCountdown/Localizations/fr.xcloc/contents.json
  30. 16
      LeCountdown/Model/Model+Extensions.swift
  31. 17
      LeCountdown/Stats/Context+Calculations.swift
  32. 40
      LeCountdown/TimerRouter.swift
  33. 97
      LeCountdown/Views/ContentView.swift
  34. 8
      LeCountdown/Views/Countdown/NewCountdownView.swift
  35. 2
      LeCountdown/Views/DialView.swift
  36. 41
      LeCountdown/Views/HomeView.swift
  37. 6
      LeCountdown/Views/Stopwatch/NewStopwatchView.swift
  38. 22
      LeCountdown/Widget/LaunchWidgetAttributes.swift
  39. 36
      LeCountdown/en.lproj/SiriIntents.intentdefinition
  40. 241
      LeCountdown/fr.lproj/Localizable.strings
  41. 214
      LeCountdown/fr.lproj/SiriIntents.intentdefinition

@ -12,6 +12,7 @@
<array/>
<key>IntentsSupported</key>
<array>
<string>LaunchTimerIntent</string>
<string>SelectTimerIntent</string>
</array>
</dict>

@ -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")

@ -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é !";

@ -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

@ -0,0 +1,264 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>88xZPY</string>
<key>INIntentDefinitionSystemVersion</key>
<string>22A400</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>14C18</string>
<key>INIntentDefinitionToolsVersion</key>
<string>14.2</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentDescriptionID</key>
<string>dIYBJB</string>
<key>INIntentEligibleForWidgets</key>
<true/>
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentLastParameterTag</key>
<integer>3</integer>
<key>INIntentName</key>
<string>SelectTimer</string>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterArraySizes</key>
<array>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>1</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Small</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>4</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Medium</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>8</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>Large</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>12</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>ExtraLarge</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>1</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>AccessoryInline</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>1</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>AccessoryCorner</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>1</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>AccessoryCircular</string>
</dict>
<dict>
<key>INIntentParameterArraySizeSize</key>
<integer>1</integer>
<key>INIntentParameterArraySizeSizeClass</key>
<string>AccessoryRectangular</string>
</dict>
</array>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterCustomDisambiguation</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>Compteur</string>
<key>INIntentParameterDisplayNameID</key>
<string>lE8mOk</string>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterFixedSizeArray</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>timer</string>
<key>INIntentParameterObjectType</key>
<string>TimerProperties</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>88xZPY</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Il y a ${count} options correspondant à ‘${timer}’.</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>gtJyOP</string>
<key>INIntentParameterPromptDialogType</key>
<string>DisambiguationIntroduction</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Merci de confirmer, vous souhaitez ‘${timer}’?</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>nntWsg</string>
<key>INIntentParameterPromptDialogType</key>
<string>Confirmation</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Laquelle ?</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>tTDiOr</string>
<key>INIntentParameterPromptDialogType</key>
<string>DisambiguationSelection</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Nous avons aussi,</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>VvsWeN</string>
<key>INIntentParameterPromptDialogType</key>
<string>SubsequentIntroduction</string>
</dict>
</array>
<key>INIntentParameterSupportsDynamicEnumeration</key>
<true/>
<key>INIntentParameterSupportsMultipleValues</key>
<true/>
<key>INIntentParameterSupportsResolution</key>
<true/>
<key>INIntentParameterTag</key>
<integer>3</integer>
<key>INIntentParameterType</key>
<string>Object</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>Choisissez un compteur</string>
<key>INIntentTitleID</key>
<string>Dm6sPw</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>View</string>
</dict>
</array>
<key>INTypes</key>
<array>
<dict>
<key>INTypeDisplayName</key>
<string>TimerProperties</string>
<key>INTypeDisplayNameID</key>
<string>ZTfW1g</string>
<key>INTypeLastPropertyTag</key>
<integer>102</integer>
<key>INTypeName</key>
<string>TimerProperties</string>
<key>INTypeProperties</key>
<array>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>1</integer>
<key>INTypePropertyName</key>
<string>identifier</string>
<key>INTypePropertyTag</key>
<integer>1</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>2</integer>
<key>INTypePropertyName</key>
<string>displayString</string>
<key>INTypePropertyTag</key>
<integer>2</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>3</integer>
<key>INTypePropertyName</key>
<string>pronunciationHint</string>
<key>INTypePropertyTag</key>
<integer>3</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>4</integer>
<key>INTypePropertyName</key>
<string>alternativeSpeakableMatches</string>
<key>INTypePropertySupportsMultipleValues</key>
<true/>
<key>INTypePropertyTag</key>
<integer>4</integer>
<key>INTypePropertyType</key>
<string>SpeakableString</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>

@ -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";

@ -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 = "<group>"; };
C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidgetLiveActivity.swift; sourceTree = "<group>"; };
C438C7D92981216200BF3EF9 /* LaunchWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidget.swift; sourceTree = "<group>"; };
C438C7DB2981216200BF3EF9 /* LaunchWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = LaunchWidget.intentdefinition; sourceTree = "<group>"; };
C438C7DC2981216300BF3EF9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C438C7DE2981216300BF3EF9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C438C7E92981260D00BF3EF9 /* SingleTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTimerView.swift; sourceTree = "<group>"; };
@ -275,6 +297,14 @@
C445FA902987C0CF0054D761 /* LeCountdown.0.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.2.xcdatamodel; sourceTree = "<group>"; };
C445FA912987CC8A0054D761 /* Sound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sound.swift; sourceTree = "<group>"; };
C445FA942987D01C0054D761 /* train_horn.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = train_horn.mp3; sourceTree = "<group>"; };
C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchWidgetAttributes.swift; sourceTree = "<group>"; };
C473C30229A91BB90056B38A /* en.xcloc */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = en.xcloc; path = Localizations/en.xcloc; sourceTree = "<group>"; };
C473C30629A91BCB0056B38A /* fr.xcloc */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = fr.xcloc; path = Localizations/fr.xcloc; sourceTree = "<group>"; };
C473C30B29A9212E0056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
C473C30E29A9212E0056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
C473C31129A9212E0056B38A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
C473C31729A9262A0056B38A /* en */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = en; path = en.lproj/SiriIntents.intentdefinition; sourceTree = "<group>"; };
C473C31D29A926F80056B38A /* en */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = en; path = en.lproj/LaunchWidget.intentdefinition; sourceTree = "<group>"; };
C4742B5629840F6400D5D950 /* CoolPic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoolPic.swift; sourceTree = "<group>"; };
C4742B58298411E800D5D950 /* CountdownFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownFormView.swift; sourceTree = "<group>"; };
C4742B5A298414B000D5D950 /* ImageSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSelectionView.swift; sourceTree = "<group>"; };
@ -326,7 +356,6 @@
C4BA2B6729A3C4AC00CB4FBA /* Context+Calculations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Context+Calculations.swift"; sourceTree = "<group>"; };
C4BA2B6929A4BE1800CB4FBA /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = "<group>"; };
C4BA2B6B29A4C47100CB4FBA /* LeCountdown.0.6.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.6.2.xcdatamodel; sourceTree = "<group>"; };
C4BA2B7029A51CA000CB4FBA /* SiriIntents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = SiriIntents.intentdefinition; sourceTree = "<group>"; };
C4BA2B7229A60CF000CB4FBA /* Shortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcut.swift; sourceTree = "<group>"; };
C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = "<group>"; };
C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
@ -506,6 +539,7 @@
C438C7F429812BB200BF3EF9 /* IntentHandler.swift */,
C438C80329813B2500BF3EF9 /* LaunchIntents.entitlements */,
C438C7F629812BB200BF3EF9 /* Info.plist */,
C473C30A29A9212E0056B38A /* Localizable.strings */,
);
path = LaunchIntents;
sourceTree = "<group>";
@ -514,6 +548,7 @@
isa = PBXGroup;
children = (
C438C7FE2981300500BF3EF9 /* IntentDataProvider.swift */,
C473C2F829A8DC0A0056B38A /* LaunchWidgetAttributes.swift */,
);
path = Widget;
sourceTree = "<group>";
@ -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 = "<group>";
};
C473C30D29A9212E0056B38A /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
C473C30E29A9212E0056B38A /* fr */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
C473C31029A9212E0056B38A /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
C473C31129A9212E0056B38A /* fr */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
C473C31629A925D50056B38A /* SiriIntents.intentdefinition */ = {
isa = PBXVariantGroup;
children = (
C473C31729A9262A0056B38A /* en */,
);
name = SiriIntents.intentdefinition;
sourceTree = "<group>";
};
C473C31C29A926F50056B38A /* LaunchWidget.intentdefinition */ = {
isa = PBXVariantGroup;
children = (
C473C31D29A926F80056B38A /* en */,
);
name = LaunchWidget.intentdefinition;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C4060DE4297AE73D003FAB80 /* Debug */ = {
isa = XCBuildConfiguration;

@ -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 {

@ -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) {

@ -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)
// }
}
}

@ -8,9 +8,9 @@
</array>
<key>NSUserActivityTypes</key>
<array>
<string>app.kikai.NewCountdown</string>
<string>LaunchTimerIntent</string>
<string>SelectTimerIntent</string>
<string>app.kikai.NewCountdown</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>

@ -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()

@ -0,0 +1,647 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="LaunchIntents/en.lproj/InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
<source>LaunchIntents</source>
<target>LaunchIntents</target>
<note>Bundle display name</note>
</trans-unit>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>LaunchIntents</source>
<target>LaunchIntents</target>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
<source/>
<target/>
<note>Copyright (human-readable)</note>
</trans-unit>
</body>
</file>
<file original="LaunchIntents/en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="Custom" xml:space="preserve">
<source>Custom</source>
<target>Custom</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Démarre" xml:space="preserve">
<source>Démarre</source>
<target>Démarre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forest stream" xml:space="preserve">
<source>Forest stream</source>
<target>Forest stream</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fun" xml:space="preserve">
<source>Fun</source>
<target>Fun</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It's time!" xml:space="preserve">
<source>It's time!</source>
<target>It's time!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nature" xml:space="preserve">
<source>Nature</source>
<target>Nature</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Time's up for (name)!" xml:space="preserve">
<source>Time's up for (name)!</source>
<target>Time's up for (name)!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Train horn" xml:space="preserve">
<source>Train horn</source>
<target>Train horn</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your (duration.minuteSecond) countdown is over!" xml:space="preserve">
<source>Your (duration.minuteSecond) countdown is over!</source>
<target>Your (duration.minuteSecond) countdown is over!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="LaunchWidget/en.lproj/InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
<source>LaunchWidget</source>
<target>LaunchWidget</target>
<note>Bundle display name</note>
</trans-unit>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>LaunchWidgetExtension</source>
<target>LaunchWidgetExtension</target>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
<source/>
<target/>
<note>Copyright (human-readable)</note>
</trans-unit>
</body>
</file>
<file original="LaunchWidget/en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="4:00" xml:space="preserve">
<source>4:00</source>
<target>4:00</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure me!" xml:space="preserve">
<source>Configure me!</source>
<target>Configure me!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure me!&#10;" xml:space="preserve">
<source>Configure me!
</source>
<target>Configure me!
</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="L" xml:space="preserve">
<source>L</source>
<target>L</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Launch Widget" xml:space="preserve">
<source>Launch Widget</source>
<target>Launch Widget</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Min" xml:space="preserve">
<source>Min</source>
<target>Min</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Select and launch your timers" xml:space="preserve">
<source>Select and launch your timers</source>
<target>Select and launch your timers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
<source>Stop</source>
<target>Stop</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="T" xml:space="preserve">
<source>T</source>
<target>T</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="LeCountdown/en.lproj/InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>LeCountdown</source>
<target>LeCountdown</target>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSAppleMusicUsageDescription" xml:space="preserve">
<source>NSAppleMusicUsageDescription</source>
<target>NSAppleMusicUsageDescription</target>
<note>Privacy - Media Library Usage Description</note>
</trans-unit>
</body>
</file>
<file original="LeCountdown/en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="%d" xml:space="preserve">
<source>%d</source>
<target>%d</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=".create countdown" xml:space="preserve">
<source>.create countdown</source>
<target>.create countdown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=".create stopwatch" xml:space="preserve">
<source>.create stopwatch</source>
<target>.create stopwatch</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Activities" xml:space="preserve">
<source>Activities</source>
<target>Activities</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All" xml:space="preserve">
<source>All</source>
<target>All</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Average duration" xml:space="preserve">
<source>Average duration</source>
<target>Average duration</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Background" xml:space="preserve">
<source>Background</source>
<target>Background</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black tea" xml:space="preserve">
<source>Black tea</source>
<target>Black tea</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Cancel</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancelled" xml:space="preserve">
<source>Cancelled</source>
<target>Cancelled</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chill" xml:space="preserve">
<source>Chill</source>
<target>Chill</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cooking" xml:space="preserve">
<source>Cooking</source>
<target>Cooking</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Coundown" xml:space="preserve">
<source>Coundown</source>
<target>Coundown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Count" xml:space="preserve">
<source>Count</source>
<target>Count</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve">
<source>Create</source>
<target>Create</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create a new countdown" xml:space="preserve">
<source>Create a new countdown</source>
<target>Create a new countdown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create countdown" xml:space="preserve">
<source>Create countdown</source>
<target>Create countdown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Custom" xml:space="preserve">
<source>Custom</source>
<target>Custom</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Démarre" xml:space="preserve">
<source>Démarre</source>
<target>Démarre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do you really want to delete?" xml:space="preserve">
<source>Do you really want to delete?</source>
<target>Do you really want to delete?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do you wish to rename or create a new activity" xml:space="preserve">
<source>Do you wish to rename or create a new activity</source>
<target>Do you wish to rename or create a new activity</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Don't show this again" xml:space="preserve">
<source>Don't show this again</source>
<target>Don't show this again</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
<target>Done</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Duration" xml:space="preserve">
<source>Duration</source>
<target>Duration</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit" xml:space="preserve">
<source>Edit</source>
<target>Edit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit alarm" xml:space="preserve">
<source>Edit alarm</source>
<target>Edit alarm</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit countdown" xml:space="preserve">
<source>Edit countdown</source>
<target>Edit countdown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit stopwatch" xml:space="preserve">
<source>Edit stopwatch</source>
<target>Edit stopwatch</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forest stream" xml:space="preserve">
<source>Forest stream</source>
<target>Forest stream</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fun" xml:space="preserve">
<source>Fun</source>
<target>Fun</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Green tea" xml:space="preserve">
<source>Green tea</source>
<target>Green tea</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Hard boiled eggs" xml:space="preserve">
<source>Hard boiled eggs</source>
<target>Hard boiled eggs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Hello Store!" xml:space="preserve">
<source>Hello Store!</source>
<target>Hello Store!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve">
<source>Home</source>
<target>Home</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It's time!" xml:space="preserve">
<source>It's time!</source>
<target>It's time!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="L" xml:space="preserve">
<source>L</source>
<target>L</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Make sure your device is not on silent mode" xml:space="preserve">
<source>Make sure your device is not on silent mode</source>
<target>Make sure your device is not on silent mode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Meditation" xml:space="preserve">
<source>Meditation</source>
<target>Meditation</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium boiled eggs" xml:space="preserve">
<source>Medium boiled eggs</source>
<target>Medium boiled eggs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Min" xml:space="preserve">
<source>Min</source>
<target>Min</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Minutes" xml:space="preserve">
<source>Minutes</source>
<target>Minutes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Month" xml:space="preserve">
<source>Month</source>
<target>Month</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Name (activates tracking)" xml:space="preserve">
<source>Name (activates tracking)</source>
<target>Name (activates tracking)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Name for tracking the activity" xml:space="preserve">
<source>Name for tracking the activity</source>
<target>Name for tracking the activity</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nap" xml:space="preserve">
<source>Nap</source>
<target>Nap</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nature" xml:space="preserve">
<source>Nature</source>
<target>Nature</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New activity" xml:space="preserve">
<source>New activity</source>
<target>New activity</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New alarm" xml:space="preserve">
<source>New alarm</source>
<target>New alarm</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New countdown" xml:space="preserve">
<source>New countdown</source>
<target>New countdown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New stopwatch" xml:space="preserve">
<source>New stopwatch</source>
<target>New stopwatch</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<target>No</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="OK" xml:space="preserve">
<source>OK</source>
<target>OK</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<target>Other</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play sound on end" xml:space="preserve">
<source>Play sound on end</source>
<target>Play sound on end</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Presets" xml:space="preserve">
<source>Presets</source>
<target>Presets</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Properties" xml:space="preserve">
<source>Properties</source>
<target>Properties</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Rename" xml:space="preserve">
<source>Rename</source>
<target>Rename</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Repeat Count" xml:space="preserve">
<source>Repeat Count</source>
<target>Repeat Count</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Running splits" xml:space="preserve">
<source>Running splits</source>
<target>Running splits</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save" xml:space="preserve">
<source>Save</source>
<target>Save</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Seconds" xml:space="preserve">
<source>Seconds</source>
<target>Seconds</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show permissions" xml:space="preserve">
<source>Show permissions</source>
<target>Show permissions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Soft boiled eggs" xml:space="preserve">
<source>Soft boiled eggs</source>
<target>Soft boiled eggs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sound" xml:space="preserve">
<source>Sound</source>
<target>Sound</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sounds" xml:space="preserve">
<source>Sounds</source>
<target>Sounds</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stats" xml:space="preserve">
<source>Stats</source>
<target>Stats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
<source>Stop</source>
<target>Stop</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stopwatch" xml:space="preserve">
<source>Stopwatch</source>
<target>Stopwatch</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="T" xml:space="preserve">
<source>T</source>
<target>T</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tea" xml:space="preserve">
<source>Tea</source>
<target>Tea</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Time" xml:space="preserve">
<source>Time</source>
<target>Time</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Time's up for (name)!" xml:space="preserve">
<source>Time's up for (name)!</source>
<target>Time's up for (name)!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tooth brushing" xml:space="preserve">
<source>Tooth brushing</source>
<target>Tooth brushing</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Train horn" xml:space="preserve">
<source>Train horn</source>
<target>Train horn</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Workout" xml:space="preserve">
<source>Workout</source>
<target>Workout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Year" xml:space="preserve">
<source>Year</source>
<target>Year</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Yes" xml:space="preserve">
<source>Yes</source>
<target>Yes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can ask Siri to create and launch countdowns and stopwatches" xml:space="preserve">
<source>You can ask Siri to create and launch countdowns and stopwatches</source>
<target>You can ask Siri to create and launch countdowns and stopwatches</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can edit the duration, sound and label before adding" xml:space="preserve">
<source>You can edit the duration, sound and label before adding</source>
<target>You can edit the duration, sound and label before adding</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You don't have any recorded activity yet" xml:space="preserve">
<source>You don't have any recorded activity yet</source>
<target>You don't have any recorded activity yet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to accept notifications, please check your settings" xml:space="preserve">
<source>You need to accept notifications, please check your settings</source>
<target>You need to accept notifications, please check your settings</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your (duration.minuteSecond) countdown is over!" xml:space="preserve">
<source>Your (duration.minuteSecond) countdown is over!</source>
<target>Your (duration.minuteSecond) countdown is over!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="alarm" xml:space="preserve">
<source>alarm</source>
<target>alarm</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="countdown" xml:space="preserve">
<source>countdown</source>
<target>countdown</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="date" xml:space="preserve">
<source>date</source>
<target>date</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="haha" xml:space="preserve">
<source>haha</source>
<target>haha</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="missing dial view" xml:space="preserve">
<source>missing dial view</source>
<target>missing dial view</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="missing edit view" xml:space="preserve">
<source>missing edit view</source>
<target>missing edit view</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="name" xml:space="preserve">
<source>name</source>
<target>name</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sounds" xml:space="preserve">
<source>sounds</source>
<target>sounds</target>
<note>sounds</note>
</trans-unit>
<trans-unit id="stopwatch" xml:space="preserve">
<source>stopwatch</source>
<target>stopwatch</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="todo" xml:space="preserve">
<source>todo</source>
<target>todo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmanaged timer: %@" xml:space="preserve">
<source>unmanaged timer: %@</source>
<target>unmanaged timer: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="value" xml:space="preserve">
<source>value</source>
<target>value</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
</xliff>

@ -0,0 +1,6 @@
/* Bundle display name */
"CFBundleDisplayName" = "LaunchIntents";
/* Bundle name */
"CFBundleName" = "LaunchIntents";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "";

@ -0,0 +1,6 @@
/* Bundle display name */
"CFBundleDisplayName" = "LaunchWidget";
/* Bundle name */
"CFBundleName" = "LaunchWidgetExtension";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "";

@ -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";

@ -0,0 +1,4 @@
/* Bundle name */
"CFBundleName" = "LeCountdown";
/* Privacy - Media Library Usage Description */
"NSAppleMusicUsageDescription" = "NSAppleMusicUsageDescription";

@ -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"
}

@ -0,0 +1,617 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
<file original="LaunchIntents/en.lproj/InfoPlist.strings" datatype="plaintext" source-language="en" target-language="fr">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
<source>LaunchIntents</source>
<note>Bundle display name</note>
</trans-unit>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>LaunchIntents</source>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
<source/>
<note>Copyright (human-readable)</note>
</trans-unit>
</body>
</file>
<file original="LaunchIntents/en.lproj/Localizable.strings" datatype="plaintext" source-language="en" target-language="fr">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="Custom" xml:space="preserve">
<source>Custom</source>
<target>Personnalisé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Démarre" xml:space="preserve">
<source>Démarre</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forest stream" xml:space="preserve">
<source>Forest stream</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fun" xml:space="preserve">
<source>Fun</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It's time!" xml:space="preserve">
<source>It's time!</source>
<target>C'est l'heure !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nature" xml:space="preserve">
<source>Nature</source>
<target>Nature</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Time's up for (name)!" xml:space="preserve">
<source>Time's up for (name)!</source>
<target>C'est l'heure pour (name) !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Train horn" xml:space="preserve">
<source>Train horn</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your (duration.minuteSecond) countdown is over!" xml:space="preserve">
<source>Your (duration.minuteSecond) countdown is over!</source>
<target>Votre minuteur de (duration.minuteSecond) est terminé !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="LaunchWidget/en.lproj/InfoPlist.strings" datatype="plaintext" source-language="en" target-language="fr">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
<source>LaunchWidget</source>
<note>Bundle display name</note>
</trans-unit>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>LaunchWidgetExtension</source>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
<source/>
<note>Copyright (human-readable)</note>
</trans-unit>
</body>
</file>
<file original="LaunchWidget/en.lproj/Localizable.strings" datatype="plaintext" source-language="en" target-language="fr">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="4:00" xml:space="preserve">
<source>4:00</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure me!" xml:space="preserve">
<source>Configure me!</source>
<target>Configurez moi !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure me!&#10;" xml:space="preserve">
<source>Configure me!
</source>
<target>Configurez moi !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="L" xml:space="preserve">
<source>L</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Launch Widget" xml:space="preserve">
<source>Launch Widget</source>
<target>Widget de lancement</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Min" xml:space="preserve">
<source>Min</source>
<target>Min</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Select and launch your timers" xml:space="preserve">
<source>Select and launch your timers</source>
<target>Sélectionnez et lancer vos minuteurs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
<source>Stop</source>
<target>Arrêter</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="T" xml:space="preserve">
<source>T</source>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="LeCountdown/en.lproj/InfoPlist.strings" datatype="plaintext" source-language="en" target-language="fr">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="CFBundleName" xml:space="preserve">
<source>LeCountdown</source>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSAppleMusicUsageDescription" xml:space="preserve">
<source>NSAppleMusicUsageDescription</source>
<note>Privacy - Media Library Usage Description</note>
</trans-unit>
</body>
</file>
<file original="LeCountdown/en.lproj/Localizable.strings" datatype="plaintext" source-language="en" target-language="fr">
<header>
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="%d" xml:space="preserve">
<source>%d</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=".create countdown" xml:space="preserve">
<source>.create countdown</source>
<target>.Créer minuteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=".create stopwatch" xml:space="preserve">
<source>.create stopwatch</source>
<target>.Créer un chrono</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Activities" xml:space="preserve">
<source>Activities</source>
<target>Activités</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All" xml:space="preserve">
<source>All</source>
<target>Tout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Average duration" xml:space="preserve">
<source>Average duration</source>
<target>Durée moyenne</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Background" xml:space="preserve">
<source>Background</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black tea" xml:space="preserve">
<source>Black tea</source>
<target>Thé noir</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Annuler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancelled" xml:space="preserve">
<source>Cancelled</source>
<target>Annulé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chill" xml:space="preserve">
<source>Chill</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cooking" xml:space="preserve">
<source>Cooking</source>
<target>Cuisine</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Coundown" xml:space="preserve">
<source>Coundown</source>
<target>Minuteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Count" xml:space="preserve">
<source>Count</source>
<target>Nombre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve">
<source>Create</source>
<target>Nouveau</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create a new countdown" xml:space="preserve">
<source>Create a new countdown</source>
<target>Créer un nouveau minuteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create countdown" xml:space="preserve">
<source>Create countdown</source>
<target>Créer un minuteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Custom" xml:space="preserve">
<source>Custom</source>
<target>Personnalisé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Démarre" xml:space="preserve">
<source>Démarre</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do you really want to delete?" xml:space="preserve">
<source>Do you really want to delete?</source>
<target>Voulez-vous vraiment effacer ?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do you wish to rename or create a new activity" xml:space="preserve">
<source>Do you wish to rename or create a new activity</source>
<target>Souhaitez-vous renommer ou créer une nouvelle activité ?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Don't show this again" xml:space="preserve">
<source>Don't show this again</source>
<target>Ne plus montrer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Done" xml:space="preserve">
<source>Done</source>
<target>Sauver</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Duration" xml:space="preserve">
<source>Duration</source>
<target>Durée</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit" xml:space="preserve">
<source>Edit</source>
<target>Editer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit alarm" xml:space="preserve">
<source>Edit alarm</source>
<target>Éditer alarme</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit countdown" xml:space="preserve">
<source>Edit countdown</source>
<target>Éditer minuteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Edit stopwatch" xml:space="preserve">
<source>Edit stopwatch</source>
<target>Éditer chrono</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forest stream" xml:space="preserve">
<source>Forest stream</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fun" xml:space="preserve">
<source>Fun</source>
<target>Fun</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Green tea" xml:space="preserve">
<source>Green tea</source>
<target>Thé vert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Hard boiled eggs" xml:space="preserve">
<source>Hard boiled eggs</source>
<target>Oeufs dur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Hello Store!" xml:space="preserve">
<source>Hello Store!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Home" xml:space="preserve">
<source>Home</source>
<target>Kikai</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It's time!" xml:space="preserve">
<source>It's time!</source>
<target>C'est l'heure !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="L" xml:space="preserve">
<source>L</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Make sure your device is not on silent mode" xml:space="preserve">
<source>Make sure your device is not on silent mode</source>
<target>Vérifiez que votre appareil n'est pas en mode silencieux</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Meditation" xml:space="preserve">
<source>Meditation</source>
<target>Méditation</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium boiled eggs" xml:space="preserve">
<source>Medium boiled eggs</source>
<target>Oeufs mollets</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Min" xml:space="preserve">
<source>Min</source>
<target>Min</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Minutes" xml:space="preserve">
<source>Minutes</source>
<target>Minutes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Month" xml:space="preserve">
<source>Month</source>
<target>Mois</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Name (activates tracking)" xml:space="preserve">
<source>Name (activates tracking)</source>
<target>Nom (active le suivi)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Name for tracking the activity" xml:space="preserve">
<source>Name for tracking the activity</source>
<target>Nom pour suivre l'activité</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nap" xml:space="preserve">
<source>Nap</source>
<target>Sieste</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Nature" xml:space="preserve">
<source>Nature</source>
<target>Nature</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New activity" xml:space="preserve">
<source>New activity</source>
<target>Nouvelle activité</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New alarm" xml:space="preserve">
<source>New alarm</source>
<target>Nouvelle alarme</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New countdown" xml:space="preserve">
<source>New countdown</source>
<target>Nouveau minuteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New stopwatch" xml:space="preserve">
<source>New stopwatch</source>
<target>Nouveau chrono</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<target>Non</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="OK" xml:space="preserve">
<source>OK</source>
<target>OK</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<target>Autre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play sound on end" xml:space="preserve">
<source>Play sound on end</source>
<target>Jouer un son de fin</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Presets" xml:space="preserve">
<source>Presets</source>
<target>Préselection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Properties" xml:space="preserve">
<source>Properties</source>
<target>Propriétes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Rename" xml:space="preserve">
<source>Rename</source>
<target>Renommer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Repeat Count" xml:space="preserve">
<source>Repeat Count</source>
<target>Nombre de répétitions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Running splits" xml:space="preserve">
<source>Running splits</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save" xml:space="preserve">
<source>Save</source>
<target>Sauvegarder</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Seconds" xml:space="preserve">
<source>Seconds</source>
<target>Secondes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show permissions" xml:space="preserve">
<source>Show permissions</source>
<target>Voir permissions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Soft boiled eggs" xml:space="preserve">
<source>Soft boiled eggs</source>
<target>Oeufs coque</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sound" xml:space="preserve">
<source>Sound</source>
<target>Son</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sounds" xml:space="preserve">
<source>Sounds</source>
<target>Sons</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stats" xml:space="preserve">
<source>Stats</source>
<target>Statistiques</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
<source>Stop</source>
<target>Arrêter</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stopwatch" xml:space="preserve">
<source>Stopwatch</source>
<target>Chrono</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="T" xml:space="preserve">
<source>T</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tea" xml:space="preserve">
<source>Tea</source>
<target>Thé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Time" xml:space="preserve">
<source>Time</source>
<target>Temps</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Time's up for (name)!" xml:space="preserve">
<source>Time's up for (name)!</source>
<target>C'est l'heure pour (name) !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tooth brushing" xml:space="preserve">
<source>Tooth brushing</source>
<target>Brossage de dent</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Train horn" xml:space="preserve">
<source>Train horn</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Workout" xml:space="preserve">
<source>Workout</source>
<target>Exercice</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Year" xml:space="preserve">
<source>Year</source>
<target>Année</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Yes" xml:space="preserve">
<source>Yes</source>
<target>Oui</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can ask Siri to create and launch countdowns and stopwatches" xml:space="preserve">
<source>You can ask Siri to create and launch countdowns and stopwatches</source>
<target>Vous pouvez demander à Siri de vous lancer les minuteurs et les chronos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can edit the duration, sound and label before adding" xml:space="preserve">
<source>You can edit the duration, sound and label before adding</source>
<target>Vous pouvez changer la durée, le son et le label avant l'ajout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You don't have any recorded activity yet" xml:space="preserve">
<source>You don't have any recorded activity yet</source>
<target>Vous n'avez pas encore d'activités enregistrées</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to accept notifications, please check your settings" xml:space="preserve">
<source>You need to accept notifications, please check your settings</source>
<target>Vous devez accepter les notifications, veuillez vérifier vos réglages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your (duration.minuteSecond) countdown is over!" xml:space="preserve">
<source>Your (duration.minuteSecond) countdown is over!</source>
<target>Votre minuteur de (duration.minuteSecond) est terminé !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="alarm" xml:space="preserve">
<source>alarm</source>
<target>Alarme</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="countdown" xml:space="preserve">
<source>countdown</source>
<target>compteur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="date" xml:space="preserve">
<source>date</source>
<target>Date</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="haha" xml:space="preserve">
<source>haha</source>
<target/>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="missing dial view" xml:space="preserve">
<source>missing dial view</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="missing edit view" xml:space="preserve">
<source>missing edit view</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="name" xml:space="preserve">
<source>name</source>
<target>nom</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sounds" xml:space="preserve">
<source>sounds</source>
<target>sons</target>
<note>sounds</note>
</trans-unit>
<trans-unit id="stopwatch" xml:space="preserve">
<source>stopwatch</source>
<target>chrono</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="todo" xml:space="preserve">
<source>todo</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmanaged timer: %@" xml:space="preserve">
<source>unmanaged timer: %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="value" xml:space="preserve">
<source>value</source>
<target>valeur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
</xliff>

@ -0,0 +1,6 @@
/* Bundle display name */
"CFBundleDisplayName" = "LaunchIntents";
/* Bundle name */
"CFBundleName" = "LaunchIntents";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "";

@ -0,0 +1,6 @@
/* Bundle display name */
"CFBundleDisplayName" = "LaunchWidget";
/* Bundle name */
"CFBundleName" = "LaunchWidgetExtension";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "";

@ -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";

@ -0,0 +1,4 @@
/* Bundle name */
"CFBundleName" = "LeCountdown";
/* Privacy - Media Library Usage Description */
"NSAppleMusicUsageDescription" = "NSAppleMusicUsageDescription";

@ -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"
}

@ -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 {

@ -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
}
}

@ -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<Bool, Error>) -> Void) {
static func performAction(timer: AbstractTimer) async throws -> Bool {
return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Bool, Error>) 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, Error>) -> 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<Bool, Error>) -> Void) {
fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result<Void, Error>) -> 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<Bool, Error>) -> Void) {
fileprivate static func _scheduleAlarm(_ alarm: Alarm, handler: @escaping (Result<Void, Error>) -> Void) {
}
fileprivate static func _startStopwatch(_ stopwatch: Stopwatch, handler: @escaping (Result<Bool, Error>) -> Void) {
fileprivate static func _startStopwatch(_ stopwatch: Stopwatch, handler: @escaping (Result<Void, Error>) -> Void) {
Conductor.maestro.startStopwatch(stopwatch)
handler(.success(Void()))
}
fileprivate static func _stopStopwatch(_ stopwatch: Stopwatch, end: Date? = nil) {

@ -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<T : AbstractTimer>: View {
@StateObject var boringContext: BoringContext = BoringContext()
@ -60,11 +53,6 @@ struct ContentView<T : AbstractTimer>: View {
animation: .default)
private var timers: FetchedResults<T>
// 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<T : AbstractTimer>: 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<T : AbstractTimer>: View {
fileprivate func _columns() -> [GridItem] {
return (0..<self._columnCount()).map { _ in GridItem(spacing: 10.0) }
}
// fileprivate func _buildItemsList() {
//
// var spots: [TimerSpot] = []
//
// let more: Int = self.isEditing ? 20 : 0 // add 20 empty spots when editing
//
// let count = max(self.timers.count, Int(self.timers.last?.order ?? 0) + 1) + more
//
// for i in 0..<count {
// let timer = self.timers.first(where: { $0.order == i })
// let spot = TimerSpot(order: Int16(i), timer: timer)
// spots.append(spot)
// }
// self.model.spots = spots
// }
// fileprivate func _saveOrder() {
// for (i, spot) in self.model.spots.enumerated() {
// spot.setOrder(order: Int16(i))
// }
// do {
// try viewContext.save()
// } catch {
// self.boringContext.error = error
// }
// }
// MARK: - Subviews
// @ViewBuilder
// fileprivate func _newView(isPresented: Binding<Bool>) -> 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<Int>#>)
// default:
// NewDataView(isPresented: isPresented, tabSelection: <#Binding<Int>#>)
// }
//
// }
// 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<T : AbstractTimer>: 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<T : AbstractTimer>: 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)
// }
// }
}

@ -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() {

@ -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

@ -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<AbstractTimer>()
.environment(\.managedObjectContext, viewContext)
.environmentObject(Conductor.maestro)
.tabItem { Label("Home", systemImage: "clock.fill") }
ContentView<AbstractTimer>()
.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
}
}
}

@ -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() {

@ -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
}

@ -19,46 +19,28 @@
<dict>
<key>INIntentCategory</key>
<string>start</string>
<key>INIntentConfigurable</key>
<true/>
<key>INIntentDescription</key>
<string>Launch timers and stopwatches</string>
<key>INIntentDescriptionID</key>
<string>NdKydA</string>
<key>INIntentInput</key>
<string>timer</string>
<key>INIntentKeyParameter</key>
<string>timer</string>
<key>INIntentLastParameterTag</key>
<integer>2</integer>
<key>INIntentManagedParameterCombinations</key>
<dict>
<key>timer</key>
<dict>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Launch your timer</string>
<key>INIntentParameterCombinationTitleID</key>
<string>PE02Dd</string>
<key>INIntentParameterCombinationUpdatesLinked</key>
<true/>
</dict>
</dict>
<key>INIntentName</key>
<string>LaunchTimer</string>
<key>INIntentParameterCombinations</key>
<dict>
<key>timer</key>
<dict>
<key>INIntentParameterCombinationIsLinked</key>
<true/>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string>Starts immediately </string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>r4Ikzx</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Launch your timer</string>
<string>Launch ${timer}</string>
<key>INIntentParameterCombinationTitleID</key>
<string>JfqtH6</string>
</dict>
@ -77,7 +59,7 @@
<key>INIntentParameterName</key>
<string>timer</string>
<key>INIntentParameterObjectType</key>
<string>Type</string>
<string>TimerIdentifier</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>ggxqDx</string>
<key>INIntentParameterPromptDialogs</key>
@ -142,7 +124,7 @@
<key>INIntentResponseParameterName</key>
<string>timer</string>
<key>INIntentResponseParameterObjectType</key>
<string>Type</string>
<string>TimerIdentifier</string>
<key>INIntentResponseParameterObjectTypeNamespace</key>
<string>ggxqDx</string>
<key>INIntentResponseParameterTag</key>
@ -166,13 +148,13 @@
<array>
<dict>
<key>INTypeDisplayName</key>
<string>Timer</string>
<string>Timer Identifier</string>
<key>INTypeDisplayNameID</key>
<string>02RXTq</string>
<key>INTypeLastPropertyTag</key>
<integer>99</integer>
<key>INTypeName</key>
<string>Type</string>
<string>TimerIdentifier</string>
<key>INTypeProperties</key>
<array>
<dict>

@ -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}";

@ -0,0 +1,214 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>ggxqDx</string>
<key>INIntentDefinitionSystemVersion</key>
<string>22A400</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>14C18</string>
<key>INIntentDefinitionToolsVersion</key>
<string>14.2</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>start</string>
<key>INIntentDescription</key>
<string>Minuteurs et chronos</string>
<key>INIntentDescriptionID</key>
<string>NdKydA</string>
<key>INIntentLastParameterTag</key>
<integer>2</integer>
<key>INIntentName</key>
<string>LaunchTimer</string>
<key>INIntentParameterCombinations</key>
<dict>
<key>timer</key>
<dict>
<key>INIntentParameterCombinationIsPrimary</key>
<true/>
<key>INIntentParameterCombinationSubtitle</key>
<string>Lancement immédiat</string>
<key>INIntentParameterCombinationSubtitleID</key>
<string>r4Ikzx</string>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationTitle</key>
<string>Démarrer ${timer}</string>
<key>INIntentParameterCombinationTitleID</key>
<string>JfqtH6</string>
</dict>
</dict>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>Compteur</string>
<key>INIntentParameterDisplayNameID</key>
<string>wU1mYs</string>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>timer</string>
<key>INIntentParameterObjectType</key>
<string>TimerIdentifier</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>ggxqDx</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>This is the mandatory prompt for your ${timer}</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>6ZcaR8</string>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
</array>
<key>INIntentParameterSupportsResolution</key>
<true/>
<key>INIntentParameterTag</key>
<integer>2</integer>
<key>INIntentParameterType</key>
<string>Object</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeFormatString</key>
<string>Your ${timer} has been launched</string>
<key>INIntentResponseCodeFormatStringID</key>
<string>E3Sz5n</string>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
<key>INIntentResponseLastParameterTag</key>
<integer>2</integer>
<key>INIntentResponseOutput</key>
<string>timer</string>
<key>INIntentResponseParameters</key>
<array>
<dict>
<key>INIntentResponseParameterDisplayName</key>
<string>Timer</string>
<key>INIntentResponseParameterDisplayNameID</key>
<string>Vfpf1t</string>
<key>INIntentResponseParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentResponseParameterName</key>
<string>timer</string>
<key>INIntentResponseParameterObjectType</key>
<string>TimerIdentifier</string>
<key>INIntentResponseParameterObjectTypeNamespace</key>
<string>ggxqDx</string>
<key>INIntentResponseParameterTag</key>
<integer>2</integer>
<key>INIntentResponseParameterType</key>
<string>Object</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>Démarrer vos compteurs</string>
<key>INIntentTitleID</key>
<string>nrTIGB</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>Start</string>
</dict>
</array>
<key>INTypes</key>
<array>
<dict>
<key>INTypeDisplayName</key>
<string>Timer Identifier</string>
<key>INTypeDisplayNameID</key>
<string>02RXTq</string>
<key>INTypeLastPropertyTag</key>
<integer>99</integer>
<key>INTypeName</key>
<string>TimerIdentifier</string>
<key>INTypeProperties</key>
<array>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>1</integer>
<key>INTypePropertyName</key>
<string>identifier</string>
<key>INTypePropertyTag</key>
<integer>1</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>2</integer>
<key>INTypePropertyName</key>
<string>displayString</string>
<key>INTypePropertyTag</key>
<integer>2</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>3</integer>
<key>INTypePropertyName</key>
<string>pronunciationHint</string>
<key>INTypePropertyTag</key>
<integer>3</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>4</integer>
<key>INTypePropertyName</key>
<string>alternativeSpeakableMatches</string>
<key>INTypePropertySupportsMultipleValues</key>
<true/>
<key>INTypePropertyTag</key>
<integer>4</integer>
<key>INTypePropertyType</key>
<string>SpeakableString</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>
Loading…
Cancel
Save