summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c7
-rw-r--r--src/audio/player.c14
-rw-r--r--src/audio/player.h2
-rw-r--r--src/ios.h3
-rw-r--r--src/ios.m87
5 files changed, 111 insertions, 2 deletions
diff --git a/src/app.c b/src/app.c
index 73908a09..c2bd3da0 100644
--- a/src/app.c
+++ b/src/app.c
@@ -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
458static iPlayer *activePlayer_;
459
458iDefineTypeConstruction(Player) 460iDefineTypeConstruction(Player)
459 461
460static size_t sampleSize_Player_(const iPlayer *d) { 462static 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
662iBool isStarted_Player(const iPlayer *d) { 670iBool 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
903iPlayer *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
69uint32_t idleTimeMs_Player (const iPlayer *); 69uint32_t idleTimeMs_Player (const iPlayer *);
70iString * metadataLabel_Player (const iPlayer *); 70iString * metadataLabel_Player (const iPlayer *);
71
72iPlayer * active_Player (void);
diff --git a/src/ios.h b/src/ios.h
index 64c0fad1..a8039c1c 100644
--- a/src/ios.h
+++ b/src/ios.h
@@ -56,3 +56,6 @@ double currentTime_AVFAudioPlayer (const iAVFAudioPlayer *);
56double duration_AVFAudioPlayer (const iAVFAudioPlayer *); 56double duration_AVFAudioPlayer (const iAVFAudioPlayer *);
57iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); 57iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *);
58iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); 58iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *);
59
60void clearNowPlayingInfo_iOS (void);
61void updateNowPlayingInfo_iOS (void);
diff --git a/src/ios.m b/src/ios.m
index e7288677..b50f4ecb 100644
--- a/src/ios.m
+++ b/src/ios.m
@@ -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
39static iBool isSystemDarkMode_ = iFalse; 41static iBool isSystemDarkMode_ = iFalse;
40static iBool isPhone_ = iFalse; 42static 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
246static iBool isDarkMode_(iWindow *window) { 297static 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
389void 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
417void clearNowPlayingInfo_iOS(void) {
418 [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil];
419}
420
338void exportDownloadedFile_iOS(const iString *path) { 421void 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]];