diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 7 | ||||
-rw-r--r-- | src/audio/player.c | 14 | ||||
-rw-r--r-- | src/audio/player.h | 2 | ||||
-rw-r--r-- | src/ios.h | 3 | ||||
-rw-r--r-- | src/ios.m | 87 |
5 files changed, 111 insertions, 2 deletions
@@ -1089,6 +1089,13 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1089 | postRefresh_App(); | 1089 | postRefresh_App(); |
1090 | break; | 1090 | break; |
1091 | case SDL_APP_WILLENTERBACKGROUND: | 1091 | case SDL_APP_WILLENTERBACKGROUND: |
1092 | #if defined (iPlatformAppleMobile) | ||
1093 | updateNowPlayingInfo_iOS(); | ||
1094 | #endif | ||
1095 | setFreezeDraw_Window(d->window, iTrue); | ||
1096 | savePrefs_App_(d); | ||
1097 | saveState_App_(d); | ||
1098 | break; | ||
1092 | case SDL_APP_TERMINATING: | 1099 | case SDL_APP_TERMINATING: |
1093 | setFreezeDraw_Window(d->window, iTrue); | 1100 | setFreezeDraw_Window(d->window, iTrue); |
1094 | savePrefs_App_(d); | 1101 | savePrefs_App_(d); |
diff --git a/src/audio/player.c b/src/audio/player.c index a2a3955b..94bcd065 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -455,6 +455,8 @@ struct Impl_Player { | |||
455 | iAVFAudioPlayer * avfPlayer; /* iOS */ | 455 | iAVFAudioPlayer * avfPlayer; /* iOS */ |
456 | }; | 456 | }; |
457 | 457 | ||
458 | static iPlayer *activePlayer_; | ||
459 | |||
458 | iDefineTypeConstruction(Player) | 460 | iDefineTypeConstruction(Player) |
459 | 461 | ||
460 | static size_t sampleSize_Player_(const iPlayer *d) { | 462 | static size_t sampleSize_Player_(const iPlayer *d) { |
@@ -655,8 +657,14 @@ void deinit_Player(iPlayer *d) { | |||
655 | #if defined (iPlatformAppleMobile) | 657 | #if defined (iPlatformAppleMobile) |
656 | if (d->avfPlayer) { | 658 | if (d->avfPlayer) { |
657 | delete_AVFAudioPlayer(d->avfPlayer); | 659 | delete_AVFAudioPlayer(d->avfPlayer); |
660 | if (activePlayer_ == d) { | ||
661 | clearNowPlayingInfo_iOS(); | ||
662 | } | ||
658 | } | 663 | } |
659 | #endif | 664 | #endif |
665 | if (activePlayer_ == d) { | ||
666 | activePlayer_ = NULL; | ||
667 | } | ||
660 | } | 668 | } |
661 | 669 | ||
662 | iBool isStarted_Player(const iPlayer *d) { | 670 | iBool isStarted_Player(const iPlayer *d) { |
@@ -739,6 +747,7 @@ iBool start_Player(iPlayer *d) { | |||
739 | if (d->avfPlayer) { | 747 | if (d->avfPlayer) { |
740 | play_AVFAudioPlayer(d->avfPlayer); | 748 | play_AVFAudioPlayer(d->avfPlayer); |
741 | setNotIdle_Player(d); | 749 | setNotIdle_Player(d); |
750 | activePlayer_ = d; | ||
742 | return iTrue; | 751 | return iTrue; |
743 | } | 752 | } |
744 | #endif | 753 | #endif |
@@ -756,6 +765,7 @@ iBool start_Player(iPlayer *d) { | |||
756 | d->decoder->gain = d->volume; | 765 | d->decoder->gain = d->volume; |
757 | SDL_PauseAudioDevice(d->device, SDL_FALSE); | 766 | SDL_PauseAudioDevice(d->device, SDL_FALSE); |
758 | setNotIdle_Player(d); | 767 | setNotIdle_Player(d); |
768 | activePlayer_ = d; | ||
759 | return iTrue; | 769 | return iTrue; |
760 | } | 770 | } |
761 | 771 | ||
@@ -889,3 +899,7 @@ iString *metadataLabel_Player(const iPlayer *d) { | |||
889 | } | 899 | } |
890 | return meta; | 900 | return meta; |
891 | } | 901 | } |
902 | |||
903 | iPlayer *active_Player(void) { | ||
904 | return activePlayer_; | ||
905 | } | ||
diff --git a/src/audio/player.h b/src/audio/player.h index b131838d..ca307dc4 100644 --- a/src/audio/player.h +++ b/src/audio/player.h | |||
@@ -68,3 +68,5 @@ float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ | |||
68 | 68 | ||
69 | uint32_t idleTimeMs_Player (const iPlayer *); | 69 | uint32_t idleTimeMs_Player (const iPlayer *); |
70 | iString * metadataLabel_Player (const iPlayer *); | 70 | iString * metadataLabel_Player (const iPlayer *); |
71 | |||
72 | iPlayer * active_Player (void); | ||
@@ -56,3 +56,6 @@ double currentTime_AVFAudioPlayer (const iAVFAudioPlayer *); | |||
56 | double duration_AVFAudioPlayer (const iAVFAudioPlayer *); | 56 | double duration_AVFAudioPlayer (const iAVFAudioPlayer *); |
57 | iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); | 57 | iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); |
58 | iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); | 58 | iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); |
59 | |||
60 | void clearNowPlayingInfo_iOS (void); | ||
61 | void updateNowPlayingInfo_iOS (void); | ||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "ios.h" | 23 | #include "ios.h" |
24 | #include "app.h" | 24 | #include "app.h" |
25 | #include "audio/player.h" | ||
25 | #include "ui/command.h" | 26 | #include "ui/command.h" |
26 | #include "ui/window.h" | 27 | #include "ui/window.h" |
27 | 28 | ||
@@ -32,9 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
32 | #include <SDL_syswm.h> | 33 | #include <SDL_syswm.h> |
33 | #include <SDL_timer.h> | 34 | #include <SDL_timer.h> |
34 | 35 | ||
35 | #import <UIKit/UIKit.h> | ||
36 | #import <CoreHaptics/CoreHaptics.h> | ||
37 | #import <AVFAudio/AVFAudio.h> | 36 | #import <AVFAudio/AVFAudio.h> |
37 | #import <CoreHaptics/CoreHaptics.h> | ||
38 | #import <UIKit/UIKit.h> | ||
39 | #import <MediaPlayer/MediaPlayer.h> | ||
38 | 40 | ||
39 | static iBool isSystemDarkMode_ = iFalse; | 41 | static iBool isSystemDarkMode_ = iFalse; |
40 | static iBool isPhone_ = iFalse; | 42 | static iBool isPhone_ = iFalse; |
@@ -241,6 +243,55 @@ void setupApplication_iOS(void) { | |||
241 | name:UIKeyboardWillHideNotification | 243 | name:UIKeyboardWillHideNotification |
242 | object:nil]; | 244 | object:nil]; |
243 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; | 245 | [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; |
246 | /* Media player remote controls. */ | ||
247 | MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; | ||
248 | [[commandCenter pauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
249 | iPlayer *player = active_Player(); | ||
250 | if (player) { | ||
251 | setPaused_Player(player, iTrue); | ||
252 | return MPRemoteCommandHandlerStatusSuccess; | ||
253 | } | ||
254 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
255 | }]; | ||
256 | [[commandCenter playCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
257 | iPlayer *player = active_Player(); | ||
258 | if (player) { | ||
259 | if (isPaused_Player(player)) { | ||
260 | setPaused_Player(player, iFalse); | ||
261 | } | ||
262 | else { | ||
263 | start_Player(player); | ||
264 | } | ||
265 | return MPRemoteCommandHandlerStatusSuccess; | ||
266 | } | ||
267 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
268 | }]; | ||
269 | [[commandCenter stopCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
270 | iPlayer *player = active_Player(); | ||
271 | if (player) { | ||
272 | stop_Player(player); | ||
273 | return MPRemoteCommandHandlerStatusSuccess; | ||
274 | } | ||
275 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
276 | }]; | ||
277 | [[commandCenter togglePlayPauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { | ||
278 | iPlayer *player = active_Player(); | ||
279 | if (player) { | ||
280 | setPaused_Player(player, !isPaused_Player(player)); | ||
281 | return MPRemoteCommandHandlerStatusSuccess; | ||
282 | } | ||
283 | return MPRemoteCommandHandlerStatusCommandFailed; | ||
284 | }]; | ||
285 | [[commandCenter nextTrackCommand] setEnabled:NO]; | ||
286 | [[commandCenter previousTrackCommand] setEnabled:NO]; | ||
287 | [[commandCenter changeRepeatModeCommand] setEnabled:NO]; | ||
288 | [[commandCenter changeShuffleModeCommand] setEnabled:NO]; | ||
289 | [[commandCenter changePlaybackRateCommand] setEnabled:NO]; | ||
290 | [[commandCenter seekForwardCommand] setEnabled:NO]; | ||
291 | [[commandCenter seekBackwardCommand] setEnabled:NO]; | ||
292 | [[commandCenter skipForwardCommand] setEnabled:NO]; | ||
293 | [[commandCenter skipBackwardCommand] setEnabled:NO]; | ||
294 | [[commandCenter changePlaybackPositionCommand] setEnabled:NO]; | ||
244 | } | 295 | } |
245 | 296 | ||
246 | static iBool isDarkMode_(iWindow *window) { | 297 | static iBool isDarkMode_(iWindow *window) { |
@@ -335,6 +386,38 @@ iBool processEvent_iOS(const SDL_Event *ev) { | |||
335 | return iFalse; /* allow normal processing */ | 386 | return iFalse; /* allow normal processing */ |
336 | } | 387 | } |
337 | 388 | ||
389 | void updateNowPlayingInfo_iOS(void) { | ||
390 | const iPlayer *player = active_Player(); | ||
391 | if (!player) { | ||
392 | clearNowPlayingInfo_iOS(); | ||
393 | return; | ||
394 | } | ||
395 | NSMutableDictionary<NSString *, id> *info = [[NSMutableDictionary<NSString *, id> alloc] init]; | ||
396 | [info setObject:[NSNumber numberWithDouble:time_Player(player)] | ||
397 | forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; | ||
398 | [info setObject:[NSNumber numberWithInt:MPNowPlayingInfoMediaTypeAudio] | ||
399 | forKey:MPNowPlayingInfoPropertyMediaType]; | ||
400 | [info setObject:[NSNumber numberWithDouble:duration_Player(player)] | ||
401 | forKey:MPMediaItemPropertyPlaybackDuration]; | ||
402 | const iString *title = tag_Player(player, title_PlayerTag); | ||
403 | const iString *artist = tag_Player(player, artist_PlayerTag); | ||
404 | if (isEmpty_String(title)) { | ||
405 | title = collectNewCStr_String("Audio"); /* TODO: Use link label or URL file name */ | ||
406 | } | ||
407 | if (isEmpty_String(artist)) { | ||
408 | artist = collectNewCStr_String("Lagrange"); /* TODO: Use domain or base URL */ | ||
409 | } | ||
410 | [info setObject:[NSString stringWithUTF8String:cstr_String(title)] | ||
411 | forKey:MPMediaItemPropertyTitle]; | ||
412 | [info setObject:[NSString stringWithUTF8String:cstr_String(artist)] | ||
413 | forKey:MPMediaItemPropertyArtist]; | ||
414 | [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:info]; | ||
415 | } | ||
416 | |||
417 | void clearNowPlayingInfo_iOS(void) { | ||
418 | [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil]; | ||
419 | } | ||
420 | |||
338 | void exportDownloadedFile_iOS(const iString *path) { | 421 | void exportDownloadedFile_iOS(const iString *path) { |
339 | NSURL *url = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:cstr_String(path) | 422 | NSURL *url = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:cstr_String(path) |
340 | encoding:NSUTF8StringEncoding]]; | 423 | encoding:NSUTF8StringEncoding]]; |