Show volume view when starting a countdown

main
Laurent 3 years ago
parent b99fb2a59a
commit 6d49965b18
  1. 4
      LeCountdown.xcodeproj/project.pbxproj
  2. 20
      LeCountdown/Conductor.swift
  3. 4
      LeCountdown/CountdownScheduler.swift
  4. 25
      LeCountdown/Sound/DelaySoundPlayer.swift
  5. 8
      LeCountdown/Sound/SoundPlayer.swift
  6. 9
      LeCountdown/Views/ContentView.swift
  7. 30
      LeCountdown/Views/Reusable/VolumeView.swift

@ -163,6 +163,7 @@
C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; }; C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67729B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; }; C4E5D67729B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67829B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; }; C4E5D67829B88BB5008E7465 /* DelaySoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */; };
C4E5D67A29B8C5A1008E7465 /* VolumeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67929B8C5A1008E7465 /* VolumeView.swift */; };
C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; }; C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; };
C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; }; C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; };
C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; }; C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; };
@ -375,6 +376,7 @@
C4E5D66E29B753D7008E7465 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AppShortcuts.strings; sourceTree = "<group>"; }; C4E5D66E29B753D7008E7465 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AppShortcuts.strings; sourceTree = "<group>"; };
C4E5D67029B753DC008E7465 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppShortcuts.strings; sourceTree = "<group>"; }; C4E5D67029B753DC008E7465 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppShortcuts.strings; sourceTree = "<group>"; };
C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelaySoundPlayer.swift; sourceTree = "<group>"; }; C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelaySoundPlayer.swift; sourceTree = "<group>"; };
C4E5D67929B8C5A1008E7465 /* VolumeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeView.swift; sourceTree = "<group>"; };
C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = "<group>"; }; C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = "<group>"; };
C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = "<group>"; }; C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = "<group>"; };
C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = "<group>"; }; C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = "<group>"; };
@ -769,6 +771,7 @@
C473C33829ACDBD70056B38A /* TipView.swift */, C473C33829ACDBD70056B38A /* TipView.swift */,
C4BA2B2E299E69A000CB4FBA /* View+Extension.swift */, C4BA2B2E299E69A000CB4FBA /* View+Extension.swift */,
C4742B5E2984205000D5D950 /* ViewModifiers.swift */, C4742B5E2984205000D5D950 /* ViewModifiers.swift */,
C4E5D67929B8C5A1008E7465 /* VolumeView.swift */,
); );
path = Reusable; path = Reusable;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1005,6 +1008,7 @@
C4F8B1BF298ACA0B005C86A5 /* StopwatchDialView.swift in Sources */, C4F8B1BF298ACA0B005C86A5 /* StopwatchDialView.swift in Sources */,
C438C807298195E600BF3EF9 /* Model+Extensions.swift in Sources */, C438C807298195E600BF3EF9 /* Model+Extensions.swift in Sources */,
C4BA2B6829A3C4AC00CB4FBA /* Context+Calculations.swift in Sources */, C4BA2B6829A3C4AC00CB4FBA /* Context+Calculations.swift in Sources */,
C4E5D67A29B8C5A1008E7465 /* VolumeView.swift in Sources */,
C4BA2B04299A42EF00CB4FBA /* NewDataView.swift in Sources */, C4BA2B04299A42EF00CB4FBA /* NewDataView.swift in Sources */,
C4BA2B4C299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */, C4BA2B4C299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */, C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */,

@ -21,7 +21,8 @@ class Conductor: ObservableObject {
static let maestro: Conductor = Conductor() static let maestro: Conductor = Conductor()
@Published var soundPlayer: SoundPlayer? = nil @Published var soundPlayer: SoundPlayer? = nil
@Published var delaySoundPlayer: DelaySoundPlayer? = nil
var delaySoundPlayer: DelaySoundPlayer? = nil
@UserDefault(PreferenceKey.countdowns.rawValue, defaultValue: [:]) static var savedCountdowns: [String : DateInterval] @UserDefault(PreferenceKey.countdowns.rawValue, defaultValue: [:]) static var savedCountdowns: [String : DateInterval]
@UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : Date] @UserDefault(PreferenceKey.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : Date]
@ -38,7 +39,7 @@ class Conductor: ObservableObject {
@Published var currentCountdowns: [String : DateInterval] = [:] { @Published var currentCountdowns: [String : DateInterval] = [:] {
didSet { didSet {
Conductor.savedCountdowns = currentCountdowns Conductor.savedCountdowns = currentCountdowns
Logger.log("**** currentCountdowns didSet, count = \(currentCountdowns.count)") // Logger.log("**** currentCountdowns didSet, count = \(currentCountdowns.count)")
withAnimation { withAnimation {
self._buildLiveTimers() self._buildLiveTimers()
} }
@ -92,7 +93,7 @@ class Conductor: ObservableObject {
} }
func notifyUser(countdownId: String) { func notifyUser(countdownId: String) {
self._playSound(timerId: countdownId) // self._playSound(timerId: countdownId)
self._endCountdown(countdownId: countdownId, cancel: false) self._endCountdown(countdownId: countdownId, cancel: false)
} }
@ -123,12 +124,12 @@ class Conductor: ObservableObject {
let dateInterval = DateInterval(start: Date(), end: date) let dateInterval = DateInterval(start: Date(), end: date)
self.currentCountdowns[countdown.stringId] = dateInterval self.currentCountdowns[countdown.stringId] = dateInterval
// self._launchLiveActivity(countdown: countdown, endDate: date) // self._launchLiveActivity(countdown: countdown, endDate: date)
// self._createTimerIntent(countdown) // self._createTimerIntent(countdown)
Logger.log("countdowns count = \(self.currentCountdowns.count)") // Logger.log("countdowns count = \(self.currentCountdowns.count)")
// } // }
} }
@ -156,13 +157,18 @@ class Conductor: ObservableObject {
do { do {
let date = Date(timeIntervalSinceNow: countdown.duration) let date = Date(timeIntervalSinceNow: countdown.duration)
let soundFile = try SoundFile(fullName: countdown.soundName) let soundFile = try SoundFile(fullName: countdown.soundName)
self.delaySoundPlayer = DelaySoundPlayer(soundFile: soundFile)
try self.delaySoundPlayer?.start(in: countdown.duration, self.playSound(countdown.someSound)
let soundPlayer = DelaySoundPlayer(soundFile: soundFile)
self.delaySoundPlayer = soundPlayer
try soundPlayer.start(in: countdown.duration,
repeatCount: Int(countdown.repeatCount)) repeatCount: Int(countdown.repeatCount))
self.startCountdown(date, countdown: countdown) self.startCountdown(date, countdown: countdown)
handler(.success(date)) handler(.success(date))
} catch { } catch {
Logger.error(error)
handler(.failure(error)) handler(.failure(error))
} }
} }

@ -41,8 +41,8 @@ class CountdownScheduler {
print("Selected sound = \(sound)") print("Selected sound = \(sound)")
// content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0) // content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0)
// content.interruptionLevel = .critical content.interruptionLevel = .critical
// content.relevanceScore = 1.0 content.relevanceScore = 1.0
// let notificationCount = 1 + countdown.repeatCount // let notificationCount = 1 + countdown.repeatCount

@ -25,18 +25,20 @@ import AVFoundation
} }
let audioSession: AVAudioSession = AVAudioSession.sharedInstance() let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback) try audioSession.setCategory(.playback, options: .duckOthers)
try audioSession.setActive(true) try audioSession.setActive(true)
let player = try AVAudioPlayer(contentsOf: url) // MPMusicPlayerController.applicationMusicPlayer.volume = 1.0
_player = player
_player?.prepareToPlay() let player = try AVAudioPlayer(contentsOf: url)
_player?.volume = 1.0 player.prepareToPlay()
_player?.delegate = self player.volume = 1.0
player.delegate = self
player.numberOfLoops = repeatCount player.numberOfLoops = repeatCount
self._player = player
_player?.play(atTime: player.deviceCurrentTime + duration) player.play(atTime: player.deviceCurrentTime + duration)
} }
func stop() { func stop() {
@ -46,6 +48,15 @@ import AVFoundation
// MARK: - Delegate // MARK: - Delegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
do {
self.stop()
try audioSession.setActive(false)
} catch {
Logger.error(error)
}
Logger.log("audioPlayerDidFinishPlaying: successfully = \(flag)") Logger.log("audioPlayerDidFinishPlaying: successfully = \(flag)")
} }

@ -53,15 +53,9 @@ enum SoundPlayerError : Error {
_player?.volume = 1.0 _player?.volume = 1.0
_player?.delegate = self _player?.delegate = self
// do { Logger.log("Plays \(url) on player: \(String(describing: self._player))")
// try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth, .defaultToSpeaker])
// } catch {
// print("audioSession error = \(error)")
// }
_player?.play() _player?.play()
} }
func stop() { func stop() {

@ -99,10 +99,19 @@ struct ContentView<T : AbstractTimer>: View {
// }.padding() // }.padding()
// } // }
Spacer()
SiriTipView(intent: StartTimerIntent(), isVisible: self.$siriTipShown) SiriTipView(intent: StartTimerIntent(), isVisible: self.$siriTipShown)
.siriTipViewStyle(SiriTipViewStyle.dark).padding() .siriTipViewStyle(SiriTipViewStyle.dark).padding()
if !conductor.liveTimers.isEmpty { if !conductor.liveTimers.isEmpty {
HStack(alignment: .center) {
VolumeView().padding(12.0)
}.frame(width: 300.0, height: 40.0)
.background(Color(white: 0.9))
.cornerRadius(16.0)
LiveTimerListView() LiveTimerListView()
.environment(\.managedObjectContext, viewContext) .environment(\.managedObjectContext, viewContext)
.environmentObject(conductor) .environmentObject(conductor)

@ -0,0 +1,30 @@
//
// VolumeView.swift
// LeCountdown
//
// Created by Laurent Morvillier on 08/03/2023.
//
import SwiftUI
import MediaPlayer
import UIKit
struct VolumeView: UIViewRepresentable {
func makeUIView(context: Context) -> MPVolumeView {
let volumeView = MPVolumeView(frame: .zero)
volumeView.showsLargeContentViewer = false
return volumeView
}
func updateUIView(_ view: MPVolumeView, context: Context) {
Logger.log("update volume view")
}
}
struct VolumeView_Previews: PreviewProvider {
static var previews: some View {
VolumeView()
}
}
Loading…
Cancel
Save