From 6d49965b18dc6edcb0758d367ee6141bc158964e Mon Sep 17 00:00:00 2001 From: Laurent Date: Wed, 8 Mar 2023 15:29:24 +0100 Subject: [PATCH] Show volume view when starting a countdown --- LeCountdown.xcodeproj/project.pbxproj | 4 +++ LeCountdown/Conductor.swift | 20 +++++++++----- LeCountdown/CountdownScheduler.swift | 4 +-- LeCountdown/Sound/DelaySoundPlayer.swift | 25 ++++++++++++----- LeCountdown/Sound/SoundPlayer.swift | 8 +----- LeCountdown/Views/ContentView.swift | 9 +++++++ LeCountdown/Views/Reusable/VolumeView.swift | 30 +++++++++++++++++++++ 7 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 LeCountdown/Views/Reusable/VolumeView.swift diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index 5b2f1ef..586dba6 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -163,6 +163,7 @@ C4E5D67429B88734008E7465 /* 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 */; }; + C4E5D67A29B8C5A1008E7465 /* VolumeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E5D67929B8C5A1008E7465 /* VolumeView.swift */; }; C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.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 */; }; @@ -375,6 +376,7 @@ C4E5D66E29B753D7008E7465 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AppShortcuts.strings; sourceTree = ""; }; C4E5D67029B753DC008E7465 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/AppShortcuts.strings; sourceTree = ""; }; C4E5D67329B88734008E7465 /* DelaySoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DelaySoundPlayer.swift; sourceTree = ""; }; + C4E5D67929B8C5A1008E7465 /* VolumeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeView.swift; sourceTree = ""; }; C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = ""; }; C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = ""; }; C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = ""; }; @@ -769,6 +771,7 @@ C473C33829ACDBD70056B38A /* TipView.swift */, C4BA2B2E299E69A000CB4FBA /* View+Extension.swift */, C4742B5E2984205000D5D950 /* ViewModifiers.swift */, + C4E5D67929B8C5A1008E7465 /* VolumeView.swift */, ); path = Reusable; sourceTree = ""; @@ -1005,6 +1008,7 @@ C4F8B1BF298ACA0B005C86A5 /* StopwatchDialView.swift in Sources */, C438C807298195E600BF3EF9 /* Model+Extensions.swift in Sources */, C4BA2B6829A3C4AC00CB4FBA /* Context+Calculations.swift in Sources */, + C4E5D67A29B8C5A1008E7465 /* VolumeView.swift in Sources */, C4BA2B04299A42EF00CB4FBA /* NewDataView.swift in Sources */, C4BA2B4C299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */, C4E5D67429B88734008E7465 /* DelaySoundPlayer.swift in Sources */, diff --git a/LeCountdown/Conductor.swift b/LeCountdown/Conductor.swift index 0c76a8b..e9d53b6 100644 --- a/LeCountdown/Conductor.swift +++ b/LeCountdown/Conductor.swift @@ -21,7 +21,8 @@ class Conductor: ObservableObject { static let maestro: Conductor = Conductor() @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.stopwatches.rawValue, defaultValue: [:]) static var savedStopwatches: [String : Date] @@ -38,7 +39,7 @@ class Conductor: ObservableObject { @Published var currentCountdowns: [String : DateInterval] = [:] { didSet { Conductor.savedCountdowns = currentCountdowns - Logger.log("**** currentCountdowns didSet, count = \(currentCountdowns.count)") +// Logger.log("**** currentCountdowns didSet, count = \(currentCountdowns.count)") withAnimation { self._buildLiveTimers() } @@ -92,7 +93,7 @@ class Conductor: ObservableObject { } func notifyUser(countdownId: String) { - self._playSound(timerId: countdownId) +// self._playSound(timerId: countdownId) self._endCountdown(countdownId: countdownId, cancel: false) } @@ -123,12 +124,12 @@ class Conductor: ObservableObject { let dateInterval = DateInterval(start: Date(), end: date) self.currentCountdowns[countdown.stringId] = dateInterval - + // self._launchLiveActivity(countdown: countdown, endDate: date) // 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 { let date = Date(timeIntervalSinceNow: countdown.duration) 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)) self.startCountdown(date, countdown: countdown) handler(.success(date)) } catch { + Logger.error(error) handler(.failure(error)) } } diff --git a/LeCountdown/CountdownScheduler.swift b/LeCountdown/CountdownScheduler.swift index 21ecf90..ec3ce97 100644 --- a/LeCountdown/CountdownScheduler.swift +++ b/LeCountdown/CountdownScheduler.swift @@ -41,8 +41,8 @@ class CountdownScheduler { print("Selected sound = \(sound)") // content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(rawValue: sound), withAudioVolume: 1.0) -// content.interruptionLevel = .critical -// content.relevanceScore = 1.0 + content.interruptionLevel = .critical + content.relevanceScore = 1.0 // let notificationCount = 1 + countdown.repeatCount diff --git a/LeCountdown/Sound/DelaySoundPlayer.swift b/LeCountdown/Sound/DelaySoundPlayer.swift index e5e6264..f3886c8 100644 --- a/LeCountdown/Sound/DelaySoundPlayer.swift +++ b/LeCountdown/Sound/DelaySoundPlayer.swift @@ -25,18 +25,20 @@ import AVFoundation } let audioSession: AVAudioSession = AVAudioSession.sharedInstance() - try audioSession.setCategory(.playback) + try audioSession.setCategory(.playback, options: .duckOthers) try audioSession.setActive(true) - let player = try AVAudioPlayer(contentsOf: url) - _player = player +// MPMusicPlayerController.applicationMusicPlayer.volume = 1.0 - _player?.prepareToPlay() - _player?.volume = 1.0 - _player?.delegate = self + let player = try AVAudioPlayer(contentsOf: url) + player.prepareToPlay() + player.volume = 1.0 + player.delegate = self player.numberOfLoops = repeatCount + + self._player = player - _player?.play(atTime: player.deviceCurrentTime + duration) + player.play(atTime: player.deviceCurrentTime + duration) } func stop() { @@ -46,6 +48,15 @@ import AVFoundation // MARK: - Delegate 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)") } diff --git a/LeCountdown/Sound/SoundPlayer.swift b/LeCountdown/Sound/SoundPlayer.swift index 3948729..a06c5df 100644 --- a/LeCountdown/Sound/SoundPlayer.swift +++ b/LeCountdown/Sound/SoundPlayer.swift @@ -53,15 +53,9 @@ enum SoundPlayerError : Error { _player?.volume = 1.0 _player?.delegate = self -// do { -// try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth, .defaultToSpeaker]) -// } catch { -// print("audioSession error = \(error)") -// } - + Logger.log("Plays \(url) on player: \(String(describing: self._player))") _player?.play() - } func stop() { diff --git a/LeCountdown/Views/ContentView.swift b/LeCountdown/Views/ContentView.swift index 108473e..59e7050 100644 --- a/LeCountdown/Views/ContentView.swift +++ b/LeCountdown/Views/ContentView.swift @@ -99,10 +99,19 @@ struct ContentView: View { // }.padding() // } + Spacer() + SiriTipView(intent: StartTimerIntent(), isVisible: self.$siriTipShown) .siriTipViewStyle(SiriTipViewStyle.dark).padding() 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() .environment(\.managedObjectContext, viewContext) .environmentObject(conductor) diff --git a/LeCountdown/Views/Reusable/VolumeView.swift b/LeCountdown/Views/Reusable/VolumeView.swift new file mode 100644 index 0000000..0a29a79 --- /dev/null +++ b/LeCountdown/Views/Reusable/VolumeView.swift @@ -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() + } +}