diff --git a/MusicPlayer Watch App/ContentView.swift b/MusicPlayer Watch App/ContentView.swift
index dd2b5c1..7dbfb6e 100644
--- a/MusicPlayer Watch App/ContentView.swift
+++ b/MusicPlayer Watch App/ContentView.swift
@@ -9,13 +9,16 @@ import SwiftUI
import Network
import AVFoundation
import UIKit
+import WatchKit
+import MediaPlayer
class TrackInfo : NSObject, Identifiable, ObservableObject {
@Published var s : String = ""
- @Published var art : Image? = nil
+ @Published var art : UIImage? = nil
@Published var m : AVPlayerItem? = nil
@Published var changed = false
var cv : ContentView? = nil
+ var background = false
override init() {
super.init()
}
@@ -37,10 +40,30 @@ class TrackInfo : NSObject, Identifiable, ObservableObject {
self.changed = !self.changed
return
}
+ if (context == nil) {
+ if let change = change,
+ let keyPath = keyPath,
+ let cv = self.cv,
+ keyPath == "timeControlStatus" && self.background
+ {
+ let old = change[.oldKey] as! Int
+ let new = change[.newKey] as! Int
+ if (new == 0 && old != 0) {
+ cv.player.playImmediately(atRate: 1)
+ }
+ }
+ }
if let cv = self.cv {
if let idx = cv.music.music.firstIndex(where: { s in
change![.newKey] as? AVPlayerItem == s.m
}){
+ var nowPlayingInfo = [String: Any]()
+ nowPlayingInfo[MPMediaItemPropertyTitle] = self.s
+ nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = cv.player.currentItem?.duration
+ nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = cv.player.currentTime()
+ nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = cv.player.rate
+ //nowPlayingInfo[MPMediaItemPropertyArtwork] = self.art
+ MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
self.from(other: cv.music.music[idx])
cv.update_pbv(idx: idx)
}
@@ -74,14 +97,39 @@ class PlaybackViewProxy {
}
struct ContentView: View {
+ @Environment(\.scenePhase) private var scenePhase
@ObservedObject var music = ListViewModel()
@ObservedObject var nowplaying : TrackInfo
@State var pushState = false
@State var geo:CGSize = .zero
@State var active = false
+ var audio_session: AVAudioSession = AVAudioSession.sharedInstance()
//@State var _curr_sel_music : TrackInfo = TrackInfo()
var pbv : PlaybackViewProxy
var dir: String
+ var cc = MPRemoteCommandCenter.shared()
+ func play() {
+ do {
+ try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, policy:.longForm)
+ try AVAudioSession.sharedInstance().setActive(true)
+
+ self.player.play()
+ } catch {
+ print("Error playing audio: \(error)")
+ }
+ /*do {
+ try audio_session.setCategory(AVAudioSession.Category.ambient, options: .mixWithOthers)
+ try audio_session.setActive(true)
+ audio_session.activate { _, e in
+ if e == nil {
+ self.player.audiovisualBackgroundPlaybackPolicy = .continuesIfPossible
+ self.player.playImmediately(atRate: 1)
+ }
+ }
+ } catch {
+ print(error)
+ }*/
+ }
func update_pbv(idx: Int) {
let m = music.music[idx]
@@ -150,6 +198,18 @@ struct ContentView: View {
.toolbar(.visible, for: .navigationBar)
self.pbv.tabpbv
+ NowPlayingView().blur(radius: 0.2)
+ }
+ }.onChange(of: scenePhase) { phase in
+ switch phase {
+ case .active:
+ self.nowplaying.background = false
+ case .inactive:
+ self.nowplaying.background = true
+ case .background:
+ self.nowplaying.background = true
+ default:
+ self.nowplaying.background = true
}
}
@@ -161,7 +221,7 @@ struct ContentView: View {
let file_url = URL(filePath: dir + "/Documents/" + filename)
let asset = AVAsset(url: file_url)
let track = TrackInfo()
-
+
asset.loadMetadata(for: .iTunesMetadata) {
items, b in
if (items == nil) { return }
@@ -169,7 +229,7 @@ struct ContentView: View {
if(i.identifier == .iTunesMetadataCoverArt) {
Task{
let imageData = try await i.load(.dataValue)
- track.art = Image(uiImage: UIImage(data: imageData!)!)
+ track.art = UIImage(data: imageData!)!
/*if (track.art != nil) {
track.art!.resizable().scaledToFill().frame(width: geo.width, height: geo.height)
}*/
@@ -188,7 +248,7 @@ struct ContentView: View {
print(self.player.error!)
}
else {
- self.player.play()
+ //self.play()
}
}
init() {
@@ -200,12 +260,6 @@ struct ContentView: View {
self.dir = NSHomeDirectory()
let dir = self.dir
- do {
- let session = AVAudioSession.sharedInstance()
- try session.setCategory(AVAudioSession.Category.playback, mode: .default, policy: .longFormAudio, options: .duckOthers)
- } catch {
- print(error)
- }
self.player = AVQueuePlayer()
self.nowplaying = TrackInfo()
@@ -213,11 +267,9 @@ struct ContentView: View {
self.pbv.pbv.parent = self
self.pbv.tabpbv.parent = self
-
self.player.audiovisualBackgroundPlaybackPolicy = .continuesIfPossible
-
- self.player.addObserver(self.nowplaying, forKeyPath: "currentItem",options: [.old, .new], context: &self)
-
+ self.player.addObserver(self.nowplaying, forKeyPath: "currentItem", options: [.old, .new], context: &self)
+ self.player.addObserver(self.nowplaying, forKeyPath: "timeControlStatus", options: [.old, .new], context: nil)
session.dataTask(with: request, completionHandler:
{ [self] (data, response, error) -> Void in
if (error != nil) { return }
@@ -251,24 +303,7 @@ struct ContentView: View {
check_file(filepath)
check_file("\(dir)/Documents/\(_file)")
if (download) {
- var tries = 16
- /*
- func try_save_response (data: Data?, response: URLResponse?, error: Error?) -> Void {
- if (error == nil) {
- let fp = fopen(filepath, "wb")
- data!.withUnsafeBytes({ ptr in
- fwrite(ptr, 1, data!.count, fp)
- })
- fclose(fp)
- add_music(filename: file)
- }
- else {
- if (tries > 0) {
- tries -= 1
- session.dataTask(with: URLRequest(url: URL(string: base + "/" + _file)!, timeoutInterval: TimeInterval(100000 * (5 - tries))), completionHandler: try_save_response).resume()
- }
- }
- }*/
+ var tries = 32
var req = URLRequest(url: URL(string: base + "/" + _file)!, timeoutInterval: 65536)
func try_download (u: URL?, r: URLResponse?, e: Error?) -> Void { // use download to avoid memory overflow
@@ -289,9 +324,6 @@ struct ContentView: View {
}
}
session.downloadTask(with: req, completionHandler: try_download).resume()
-
-
- //session.dataTask(with: URLRequest(url: URL(string: base + "/" + _file)!, timeoutInterval: TimeInterval(65535)), completionHandler: try_save_response).resume()
}
}
}catch{}
@@ -303,6 +335,17 @@ struct ContentView: View {
self.pbv.pbv.update(music: self.nowplaying)
self.pbv.tabpbv.update(music: self.nowplaying)
+
+ let player = self.player
+ cc.playCommand.addTarget { _ in
+ player.play()
+ return .success
+ }
+ cc.stopCommand.addTarget { _ in
+ player.play()
+ return .success
+ }
+
}
}
diff --git a/MusicPlayer Watch App/PlaybackView.swift b/MusicPlayer Watch App/PlaybackView.swift
index ccd3b62..cd83869 100644
--- a/MusicPlayer Watch App/PlaybackView.swift
+++ b/MusicPlayer Watch App/PlaybackView.swift
@@ -7,6 +7,10 @@
import SwiftUI
import UIKit
+import WatchKit
+
+let window_width = WKInterfaceDevice.current().screenBounds.width
+let window_height = WKInterfaceDevice.current().screenBounds.height
class AppearTimer : ObservableObject {
@Published var appear = false
@@ -47,19 +51,24 @@ struct PlaybackView: View {
if trackInfo.m != nil {
GeometryReader { geo in
ZStack {
- if(trackInfo.art == nil) {
-
- Image(systemName: "music.note")
- .resizable()
- .scaledToFit()
- .foregroundColor(.white)
- .frame(width: geo.size.width*0.84, height: geo.size.height*0.84)
- .padding(.leading, geo.size.width*0.08)
- .padding(.top, geo.size.height*0.08)
- }
- else {
- trackInfo.art!.resizable().scaledToFill()
- }
+ ZStack{
+ if(trackInfo.art == nil) {
+
+ Image(systemName: "music.note")
+ .resizable()
+ .scaledToFit()
+ .foregroundColor(.white)
+ .frame(width: window_width*0.7, height: window_height*0.7)
+ //.padding(.leading, window_width*0.05)
+ //.padding(.bottom, window_height * 0.15)
+
+ //.padding(.top, window_height*0.08)
+ }
+ else {
+ Image(uiImage: trackInfo.art!).resizable().scaledToFill()
+ }
+ }.frame(width : window_width, height: window_height * 0.8)
+ .background(appearTimer.appear ? Color.gray : Color.clear).opacity(appearTimer.appear ? 0.3 : 1).blur(radius: appearTimer.appear ? 5 : 0)
if (appearTimer.appear)
{
VStack {
@@ -70,8 +79,9 @@ struct PlaybackView: View {
self.playing = false
} else {
parent!.player.audiovisualBackgroundPlaybackPolicy = .continuesIfPossible
- parent!.player.play()
+ parent!.play()
self.playing = true
+ appearTimer.appear()
}
} label: {
(
@@ -81,9 +91,9 @@ struct PlaybackView: View {
)
.resizable()
.scaledToFit()
- .frame(width: geo.size.width/5.5)
+ .frame(width: window_width/5.5)
}.background(Color(red: 0,green: 0,blue: 0,opacity: 0.2))
- .frame(width: geo.size.width/2.5)
+ .frame(width: window_width/2.5)
.cornerRadius(90, antialiased: true)
.foregroundColor(.white)
.opacity(1)
@@ -94,25 +104,27 @@ struct PlaybackView: View {
curr!.seek(to: .zero)
parent!.player.play()
self.playing = true
+ appearTimer.appear()
} label : {
Image(systemName: "chevron.forward")
.resizable()
.scaledToFit()
- .frame(width: geo.size.width/7, height: geo.size.height/7)
+ .frame(width: window_width/7, height: window_height/7)
}.background(Color.clear)
.clipShape(Circle())
.foregroundColor(.white)
- .frame(width: geo.size.width/4, height: geo.size.height/4)
+ .frame(width: window_width/4, height: window_height/4)
.padding(0)
.opacity(1)
.buttonStyle(.plain)
}
- }
+ }.zIndex(5)
}
}.onTapGesture {
appearTimer.appear()
}
}.navigationBarBackButtonHidden(false)
+ .navigationTitle(trackInfo.s)
.toolbar(.visible, for: .navigationBar)
.onAppear() {
appearTimer.appear(time: 3, _appear: true)
diff --git a/MusicPlayer-Watch-App-Info.plist b/MusicPlayer-Watch-App-Info.plist
index f753731..e095854 100644
--- a/MusicPlayer-Watch-App-Info.plist
+++ b/MusicPlayer-Watch-App-Info.plist
@@ -2,9 +2,21 @@
+ NSSupportsAutomaticTermination
+
+ NSSupportsSuddenTermination
+
+ UIApplicationExitsOnSuspend
+
UIBackgroundModes
audio
+ fetch
+ processing
+ WKBackgroundModes
+
+ WKSupportsAlwaysOnDisplay
+
diff --git a/MusicPlayer.xcodeproj/project.pbxproj b/MusicPlayer.xcodeproj/project.pbxproj
index ea02f31..2c29c80 100644
--- a/MusicPlayer.xcodeproj/project.pbxproj
+++ b/MusicPlayer.xcodeproj/project.pbxproj
@@ -20,7 +20,7 @@
isa = PBXContainerItemProxy;
containerPortal = A95C117329C531C000737618 /* Project object */;
proxyType = 1;
- remoteGlobalTrackInfoing = A95C117E29C531C100737618;
+ remoteGlobalIDString = A95C117E29C531C100737618;
remoteInfo = "MusicPlayer Watch App";
};
/* End PBXContainerItemProxy section */
@@ -338,6 +338,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "MusicPlayer-Watch-App-Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = MusicPlayer;
+ INFOPLIST_KEY_UIStatusBarHidden = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
INFOPLIST_KEY_WKWatchOnly = YES;
LD_RUNPATH_SEARCH_PATHS = (
@@ -352,7 +353,7 @@
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
- WATCHOS_DEPLOYMENT_TARGET = 9.1;
+ WATCHOS_DEPLOYMENT_TARGET = 9.3;
};
name = Debug;
};
@@ -369,6 +370,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "MusicPlayer-Watch-App-Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = MusicPlayer;
+ INFOPLIST_KEY_UIStatusBarHidden = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
INFOPLIST_KEY_WKWatchOnly = YES;
LD_RUNPATH_SEARCH_PATHS = (
@@ -384,7 +386,7 @@
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
VALIDATE_PRODUCT = YES;
- WATCHOS_DEPLOYMENT_TARGET = 9.1;
+ WATCHOS_DEPLOYMENT_TARGET = 9.3;
};
name = Release;
};
diff --git a/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
index c920a37..7e92f65 100644
--- a/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
+++ b/MusicPlayer.xcodeproj/xcuserdata/bill.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -14,10 +14,57 @@
filePath = "MusicPlayer Watch App/ContentView.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
- startingLineNumber = "284"
- endingLineNumber = "284"
+ startingLineNumber = "319"
+ endingLineNumber = "319"
landmarkName = "try_download(u:r:e:)"
landmarkType = "9">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+