Skip to content

Commit

Permalink
Add function for seeking time in iOS control center and lockscreen
Browse files Browse the repository at this point in the history
  • Loading branch information
Buddy Reno committed Jun 23, 2017
1 parent 56b5a72 commit 451a57f
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 58 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ MusicControls.destroy(onSuccess, onError);
- Subscribe events to the media controller:
```javascript
function events(action) {
switch(action) {

const message = JSON.parse(action).message;
switch(message) {
case 'music-controls-next':
// Do something
break;
Expand All @@ -69,6 +71,14 @@ function events(action) {
case 'music-controls-toggle-play-pause' :
// Do something
break;
case 'music-controls-seek-to':
const seekToInSeconds = JSON.parse(action).position;
MusicControls.updateElapsed({
elapsed: seekToInSeconds,
isPlaying: true //optional
});
// Do something
break;

// Headset events (Android only)
// All media button events are listed below
Expand Down Expand Up @@ -99,6 +109,16 @@ MusicControls.listen();
MusicControls.updateIsPlaying(true); // toggle the play/pause notification button
MusicControls.updateDismissable(true);
```

- iOS Specific Events:
Allows you to listen for iOS events fired from the scrubber in control center.
```javascript
MusicControls.updateElapsed({
elapsed: 208, // seconds
isPlaying: true // optional argument.
});
```

- List of media button events (Android only):
```javascript
'music-controls-media-button-next', 'music-controls-media-button-pause', 'music-controls-media-button-play',
Expand Down
50 changes: 25 additions & 25 deletions src/android/MusicControlsBroadcastReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void setCallback(CallbackContext cb){

public void stopListening(){
if (this.cb != null){
this.cb.success("music-controls-stop-listening");
this.cb.success("{\"message\": \"music-controls-stop-listening\" }");
this.cb = null;
}
}
Expand All @@ -42,12 +42,12 @@ public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra("state", -1);
switch (state) {
case 0:
this.cb.success("music-controls-headset-unplugged");
this.cb.success("{\"message\": \"music-controls-headset-unplugged\"}");
this.cb = null;
this.musicControls.unregisterMediaButtonEvent();
break;
case 1:
this.cb.success("music-controls-headset-plugged");
this.cb.success("{\"message\": \"music-controls-headset-plugged\"}");
this.cb = null;
this.musicControls.registerMediaButtonEvent();
break;
Expand All @@ -62,75 +62,75 @@ public void onReceive(Context context, Intent intent) {
int keyCode = event.getKeyCode();
switch (keyCode) {
case KeyEvent.KEYCODE_MEDIA_NEXT:
this.cb.success("music-controls-media-button-next");
this.cb.success("{\"message\": \"music-controls-media-button-next\"}");
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
this.cb.success("music-controls-media-button-pause");
this.cb.success("{\"message\": \"music-controls-media-button-pause\"}");
break;
case KeyEvent.KEYCODE_MEDIA_PLAY:
this.cb.success("music-controls-media-button-play");
this.cb.success("{\"message\": \"music-controls-media-button-play\"}");
break;
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
this.cb.success("music-controls-media-button-play-pause");
this.cb.success("{\"message\": \"music-controls-media-button-play-pause\"}");
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
this.cb.success("music-controls-media-button-previous");
this.cb.success("{\"message\": \"music-controls-media-button-previous\"}");
break;
case KeyEvent.KEYCODE_MEDIA_STOP:
this.cb.success("music-controls-media-button-stop");
this.cb.success("{\"message\": \"music-controls-media-button-stop\"}");
break;
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
this.cb.success("music-controls-media-button-fast-forward");
this.cb.success("{\"message\": \"music-controls-media-button-fast-forward\"}");
break;
case KeyEvent.KEYCODE_MEDIA_REWIND:
this.cb.success("music-controls-media-button-rewind");
this.cb.success("{\"message\": \"music-controls-media-button-rewind\"}");
break;
case KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD:
this.cb.success("music-controls-media-button-skip-backward");
this.cb.success("{\"message\": \"music-controls-media-button-skip-backward\"}");
break;
case KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD:
this.cb.success("music-controls-media-button-skip-forward");
this.cb.success("{\"message\": \"music-controls-media-button-skip-forward\"}");
break;
case KeyEvent.KEYCODE_MEDIA_STEP_BACKWARD:
this.cb.success("music-controls-media-button-step-backward");
this.cb.success("{\"message\": \"music-controls-media-button-step-backward\"}");
break;
case KeyEvent.KEYCODE_MEDIA_STEP_FORWARD:
this.cb.success("music-controls-media-button-step-forward");
this.cb.success("{\"message\": \"music-controls-media-button-step-forward\"}");
break;
case KeyEvent.KEYCODE_META_LEFT:
this.cb.success("music-controls-media-button-meta-left");
this.cb.success("{\"message\": \"music-controls-media-button-meta-left\"}");
break;
case KeyEvent.KEYCODE_META_RIGHT:
this.cb.success("music-controls-media-button-meta-right");
this.cb.success("{\"message\": \"music-controls-media-button-meta-right\"}");
break;
case KeyEvent.KEYCODE_MUSIC:
this.cb.success("music-controls-media-button-music");
this.cb.success("{\"message\": \"music-controls-media-button-music\"}");
break;
case KeyEvent.KEYCODE_VOLUME_UP:
this.cb.success("music-controls-media-button-volume-up");
this.cb.success("{\"message\": \"music-controls-media-button-volume-up\"}");
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
this.cb.success("music-controls-media-button-volume-down");
this.cb.success("{\"message\": \"music-controls-media-button-volume-down\"}");
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
this.cb.success("music-controls-media-button-volume-mute");
this.cb.success("{\"message\": \"music-controls-media-button-volume-mute\"}");
break;
case KeyEvent.KEYCODE_HEADSETHOOK:
this.cb.success("music-controls-media-button-headset-hook");
this.cb.success("{\"message\": \"music-controls-media-button-headset-hook\"}");
break;
default:
this.cb.success(message);
this.cb.success("{\"message\": \"" + message + "\"}");
break;
}
this.cb = null;
}
} else if (message.equals("music-controls-destroy")){
// Close Button
this.cb.success("music-controls-destroy");
this.cb.success("{\"message\": \"music-controls-destroy\"}");
this.cb = null;
this.musicControls.destroyPlayerNotification();
} else {
this.cb.success(message);
this.cb.success("{\"message\": \"" + message + "\"}");
this.cb = null;
}

Expand Down
1 change: 1 addition & 0 deletions src/ios/MusicControls.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

- (void) create: (CDVInvokedUrlCommand *) command;
- (void) updateIsPlaying: (CDVInvokedUrlCommand *) command;
- (void) updateElapsed: (CDVInvokedUrlCommand *) command;
- (void) destroy: (CDVInvokedUrlCommand *) command;
- (void) watch: (CDVInvokedUrlCommand *) command;
- (MPMediaItemArtwork *) createCoverArtwork: (NSString *) coverUri;
Expand Down
90 changes: 64 additions & 26 deletions src/ios/MusicControls.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// MusicControls.m
//
//
//
// Created by Juan Gonzalez on 12/16/16.
//
Expand All @@ -14,32 +14,32 @@ @implementation MusicControls
- (void) create: (CDVInvokedUrlCommand *) command {
NSDictionary * musicControlsInfoDict = [command.arguments objectAtIndex:0];
MusicControlsInfo * musicControlsInfo = [[MusicControlsInfo alloc] initWithDictionary:musicControlsInfoDict];

if (!NSClassFromString(@"MPNowPlayingInfoCenter")) {
return;
}

[self.commandDelegate runInBackground:^{
MPNowPlayingInfoCenter * nowPlayingInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
NSDictionary * nowPlayingInfo = nowPlayingInfoCenter.nowPlayingInfo;
NSMutableDictionary * updatedNowPlayingInfo = [NSMutableDictionary dictionaryWithDictionary:nowPlayingInfo];

MPMediaItemArtwork * mediaItemArtwork = [self createCoverArtwork:[musicControlsInfo cover]];
NSNumber * duration = [NSNumber numberWithInt:[musicControlsInfo duration]];
NSNumber * elapsed = [NSNumber numberWithInt:[musicControlsInfo elapsed]];
NSNumber * playbackRate = [NSNumber numberWithBool:[musicControlsInfo isPlaying]];

if (mediaItemArtwork != nil) {
[updatedNowPlayingInfo setObject:mediaItemArtwork forKey:MPMediaItemPropertyArtwork];
}

[updatedNowPlayingInfo setObject:[musicControlsInfo artist] forKey:MPMediaItemPropertyArtist];
[updatedNowPlayingInfo setObject:[musicControlsInfo track] forKey:MPMediaItemPropertyTitle];
[updatedNowPlayingInfo setObject:[musicControlsInfo album] forKey:MPMediaItemPropertyAlbumTitle];
[updatedNowPlayingInfo setObject:duration forKey:MPMediaItemPropertyPlaybackDuration];
[updatedNowPlayingInfo setObject:elapsed forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
[updatedNowPlayingInfo setObject:playbackRate forKey:MPNowPlayingInfoPropertyPlaybackRate];

nowPlayingInfoCenter.nowPlayingInfo = updatedNowPlayingInfo;
}];
}
Expand All @@ -48,18 +48,34 @@ - (void) updateIsPlaying: (CDVInvokedUrlCommand *) command {
NSDictionary * musicControlsInfoDict = [command.arguments objectAtIndex:0];
MusicControlsInfo * musicControlsInfo = [[MusicControlsInfo alloc] initWithDictionary:musicControlsInfoDict];
NSNumber * playbackRate = [NSNumber numberWithBool:[musicControlsInfo isPlaying]];

if (!NSClassFromString(@"MPNowPlayingInfoCenter")) {
return;
}

MPNowPlayingInfoCenter * nowPlayingCenter = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary * updatedNowPlayingInfo = [NSMutableDictionary dictionaryWithDictionary:nowPlayingCenter.nowPlayingInfo];

[updatedNowPlayingInfo setObject:playbackRate forKey:MPNowPlayingInfoPropertyPlaybackRate];
nowPlayingCenter.nowPlayingInfo = updatedNowPlayingInfo;
}

- (void) updateElapsed: (CDVInvokedUrlCommand *) command {
NSDictionary * musicControlsInfoDict = [command.arguments objectAtIndex:0];
MusicControlsInfo * musicControlsInfo = [[MusicControlsInfo alloc] initWithDictionary:musicControlsInfoDict];
NSNumber * elapsed = [NSNumber numberWithDouble:[musicControlsInfo elapsed]];

if (!NSClassFromString(@"MPNowPlayingInfoCenter")) {
return;
}

MPNowPlayingInfoCenter * nowPlayingCenter = [MPNowPlayingInfoCenter defaultCenter];
NSMutableDictionary * updatedNowPlayingInfo = [NSMutableDictionary dictionaryWithDictionary:nowPlayingCenter.nowPlayingInfo];

[updatedNowPlayingInfo setObject:elapsed forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
nowPlayingCenter.nowPlayingInfo = updatedNowPlayingInfo;
}

- (void) destroy: (CDVInvokedUrlCommand *) command {
[self deregisterMusicControlsEventListener];
}
Expand All @@ -71,36 +87,36 @@ - (void) watch: (CDVInvokedUrlCommand *) command {

- (MPMediaItemArtwork *) createCoverArtwork: (NSString *) coverUri {
UIImage * coverImage = nil;

if (coverUri == nil) {
return nil;
}

if ([coverUri hasPrefix:@"http://"] || [coverUri hasPrefix:@"https://"]) {
NSURL * coverImageUrl = [NSURL URLWithString:coverUri];
NSData * coverImageData = [NSData dataWithContentsOfURL: coverImageUrl];

coverImage = [UIImage imageWithData: coverImageData];
}
else if ([coverUri hasPrefix:@"file://"]) {
NSString * fullCoverImagePath = [coverUri stringByReplacingOccurrencesOfString:@"file://" withString:@""];

if ([[NSFileManager defaultManager] fileExistsAtPath: fullCoverImagePath]) {
coverImage = [[UIImage alloc] initWithContentsOfFile: fullCoverImagePath];
}
}
else if (![coverUri isEqual:@""]) {
NSString * baseCoverImagePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString * fullCoverImagePath = [NSString stringWithFormat:@"%@%@", baseCoverImagePath, coverUri];

if ([[NSFileManager defaultManager] fileExistsAtPath:fullCoverImagePath]) {
coverImage = [UIImage imageNamed:fullCoverImagePath];
}
}
else {
coverImage = [UIImage imageNamed:@"none"];
}

return [self isCoverImageValid:coverImage] ? [[MPMediaItemArtwork alloc] initWithImage:coverImage] : nil;
}

Expand All @@ -110,57 +126,79 @@ - (bool) isCoverImageValid: (UIImage *) coverImage {

- (void) handleMusicControlsNotification: (NSNotification *) notification {
UIEvent * receivedEvent = notification.object;

if ([self latestEventCallbackId] == nil) {
return;
}

if (receivedEvent.type == UIEventTypeRemoteControl) {
NSString * action;

switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
action = @"music-controls-toggle-play-pause";
break;

case UIEventSubtypeRemoteControlPlay:
action = @"music-controls-play";
break;

case UIEventSubtypeRemoteControlPause:
action = @"music-controls-pause";
break;

case UIEventSubtypeRemoteControlPreviousTrack:
action = @"music-controls-previous";
break;

case UIEventSubtypeRemoteControlNextTrack:
action = @"music-controls-next";
break;

case UIEventSubtypeRemoteControlStop:
action = @"music-controls-destroy";
break;

default:
break;
}

CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:action];

NSString * jsonAction = [NSString stringWithFormat:@"{\"message\":\"%@\"}", action];
CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:jsonAction];
[self.commandDelegate sendPluginResult:pluginResult callbackId:[self latestEventCallbackId]];
}
}

- (void) registerMusicControlsEventListener {
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMusicControlsNotification:) name:@"musicControlsEventNotification" object:nil];

if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_0) {
//only available in iOS 9.1 and up.
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.changePlaybackPositionCommand setEnabled:true];
[commandCenter.changePlaybackPositionCommand addTarget:self action:@selector(changedThumbSliderOnLockScreen:)];
}
}

- (MPRemoteCommandHandlerStatus)changedThumbSliderOnLockScreen:(MPChangePlaybackPositionCommandEvent *)event {
NSString * seekTo = [NSString stringWithFormat:@"{\"message\":\"music-controls-seek-to\",\"position\":\"%f\"}", event.positionTime];
CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:seekTo];
pluginResult.associatedObject = @{@"position":[NSNumber numberWithDouble: event.positionTime]};
[self.commandDelegate sendPluginResult:pluginResult callbackId:[self latestEventCallbackId]];
return MPRemoteCommandHandlerStatusSuccess;
}

- (void) deregisterMusicControlsEventListener {
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"receivedEvent" object:nil];
[self setLatestEventCallbackId:nil];

if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_0) {
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.changePlaybackPositionCommand setEnabled:false];
[commandCenter.changePlaybackPositionCommand removeTarget:self action:NULL];
}
}

- (void) dealloc {
Expand Down
Loading

0 comments on commit 451a57f

Please sign in to comment.