feat: add support for os media control

This commit is contained in:
2024-07-29 12:56:17 +01:00
parent b02c6a9415
commit 6db6a8375c
4 changed files with 82 additions and 0 deletions

View File

@@ -24,6 +24,7 @@
F12B73482C55C0C40064A230 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = SOURCE_ROOT; };
F12B734A2C56AEB00064A230 /* LiveStatusManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveStatusManager.swift; sourceTree = "<group>"; };
F12B734C2C5702EC0064A230 /* LiveListenerCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveListenerCounter.swift; sourceTree = "<group>"; };
F12B734E2C57B0230064A230 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
F138396C2C51BABD00B4814F /* InfinifiIOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InfinifiIOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
F138396F2C51BABD00B4814F /* InfinifiIOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfinifiIOSApp.swift; sourceTree = "<group>"; };
F13839712C51BABD00B4814F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -61,6 +62,7 @@
F138396E2C51BABD00B4814F /* InfinifiIOS */ = {
isa = PBXGroup;
children = (
F12B734E2C57B0230064A230 /* Info.plist */,
F138396F2C51BABD00B4814F /* InfinifiIOSApp.swift */,
F13839712C51BABD00B4814F /* ContentView.swift */,
F13839732C51BABE00B4814F /* Assets.xcassets */,
@@ -295,6 +297,7 @@
DEVELOPMENT_TEAM = 5FG7YZ49ZA;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = InfinifiIOS/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = infinifi;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -326,6 +329,7 @@
DEVELOPMENT_TEAM = 5FG7YZ49ZA;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = InfinifiIOS/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = infinifi;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;

View File

@@ -10,6 +10,12 @@ struct ContentView: View {
playbackManager.stop()
case .paused:
playbackManager.nextTrack()
Task {
// sleep for 100ms before configuring audio session and control
// to prevent UI stutter
try await Task.sleep(nanoseconds: 100_000_000)
playbackManager.confiugureAudioSessionAndControls()
}
default:
break
}

10
InfinifiIOS/Info.plist Normal file
View File

@@ -0,0 +1,10 @@
<?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>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</dict>
</plist>

View File

@@ -1,5 +1,6 @@
import AVKit
import Foundation
import MediaPlayer
enum PlaybackState {
case playing
@@ -21,6 +22,35 @@ class PlaybackManager: ObservableObject {
Task { try await initialize() }
}
func confiugureAudioSessionAndControls() {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playback, mode: .default)
try audioSession.setActive(true)
} catch {}
NotificationCenter.default.addObserver(self, selector: #selector(onAudioInterrupted), name: AVAudioSession.interruptionNotification, object: audioSession)
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle: "infinite lo-fi music",
MPMediaItemPropertyArtist: "infinifi"
]
let cmdCenter = MPRemoteCommandCenter.shared()
cmdCenter.playCommand.isEnabled = true
cmdCenter.playCommand.addTarget { _ in
self.nextTrack()
return .success
}
cmdCenter.pauseCommand.isEnabled = true
cmdCenter.pauseCommand.addTarget { _ in
self.stop()
return .success
}
}
func nextTrack() {
playbackState = .loading
Task {
@@ -82,6 +112,38 @@ class PlaybackManager: ObservableObject {
}
}
@objc
private func onAudioInterrupted(notification: Notification) {
guard let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let interruptionType = AVAudioSession.InterruptionType(rawValue: typeValue)
else {
return
}
switch interruptionType {
case .began:
DispatchQueue.main.async {
self.stop()
}
case .ended:
guard let optionValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSession.InterruptionOptions(rawValue: optionValue)
if options.contains(.shouldResume) {
DispatchQueue.main.async {
self.nextTrack()
}
}
@unknown default:
break
}
}
@objc
private func playbackFinished() {
NotificationCenter.default.removeObserver(self, name: AVPlayerItem.didPlayToEndTimeNotification, object: audioPlayer.currentItem)