For Freezer 0.5.0
This commit is contained in:
parent
73fce9905f
commit
b268066d26
@ -402,6 +402,7 @@ public class AudioService extends MediaBrowserServiceCompat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (displayTitle != null)
|
if (displayTitle != null)
|
||||||
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle);
|
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle);
|
||||||
if (displaySubtitle != null)
|
if (displaySubtitle != null)
|
||||||
@ -553,6 +554,7 @@ public class AudioService extends MediaBrowserServiceCompat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class MediaSessionCallback extends MediaSessionCompat.Callback {
|
public class MediaSessionCallback extends MediaSessionCompat.Callback {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAddQueueItem(MediaDescriptionCompat description) {
|
public void onAddQueueItem(MediaDescriptionCompat description) {
|
||||||
if (listener == null) return;
|
if (listener == null) return;
|
||||||
|
@ -60,6 +60,7 @@ import io.flutter.embedding.engine.dart.DartExecutor;
|
|||||||
import io.flutter.embedding.engine.dart.DartExecutor.DartCallback;
|
import io.flutter.embedding.engine.dart.DartExecutor.DartCallback;
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import io.flutter.view.FlutterNativeView;
|
import io.flutter.view.FlutterNativeView;
|
||||||
import io.flutter.view.FlutterRunArguments;
|
import io.flutter.view.FlutterRunArguments;
|
||||||
|
BIN
android/src/main/res/drawable-hdpi/ic_favorite.png
Normal file
BIN
android/src/main/res/drawable-hdpi/ic_favorite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 803 B |
BIN
android/src/main/res/drawable-mdpi/ic_favorite.png
Normal file
BIN
android/src/main/res/drawable-mdpi/ic_favorite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 597 B |
BIN
android/src/main/res/drawable-xhdpi/ic_favorite.png
Normal file
BIN
android/src/main/res/drawable-xhdpi/ic_favorite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
android/src/main/res/drawable-xxhdpi/ic_favorite.png
Normal file
BIN
android/src/main/res/drawable-xxhdpi/ic_favorite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
android/src/main/res/drawable-xxxhdpi/ic_favorite.png
Normal file
BIN
android/src/main/res/drawable-xxxhdpi/ic_favorite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
@ -1,617 +0,0 @@
|
|||||||
#import "AudioServicePlugin.h"
|
|
||||||
#import <AVFoundation/AVFoundation.h>
|
|
||||||
#import <MediaPlayer/MediaPlayer.h>
|
|
||||||
|
|
||||||
// If you'd like to help, please see the TODO comments below, then open a
|
|
||||||
// GitHub issue to announce your intention to work on a particular feature, and
|
|
||||||
// submit a pull request. We have an open discussion over at issue #10 about
|
|
||||||
// all things iOS if you'd like to discuss approaches or ask for input. Thank
|
|
||||||
// you for your support!
|
|
||||||
|
|
||||||
@implementation AudioServicePlugin
|
|
||||||
|
|
||||||
static FlutterMethodChannel *channel = nil;
|
|
||||||
static FlutterMethodChannel *backgroundChannel = nil;
|
|
||||||
static BOOL _running = NO;
|
|
||||||
static FlutterResult startResult = nil;
|
|
||||||
static MPRemoteCommandCenter *commandCenter = nil;
|
|
||||||
static NSArray *queue = nil;
|
|
||||||
static NSMutableDictionary *mediaItem = nil;
|
|
||||||
static long actionBits;
|
|
||||||
static NSArray *commands;
|
|
||||||
static BOOL _controlsUpdated = NO;
|
|
||||||
static enum AudioProcessingState processingState = none;
|
|
||||||
static BOOL playing = NO;
|
|
||||||
static NSNumber *position = nil;
|
|
||||||
static NSNumber *bufferedPosition = nil;
|
|
||||||
static NSNumber *updateTime = nil;
|
|
||||||
static NSNumber *speed = nil;
|
|
||||||
static NSNumber *repeatMode = nil;
|
|
||||||
static NSNumber *shuffleMode = nil;
|
|
||||||
static NSNumber *fastForwardInterval = nil;
|
|
||||||
static NSNumber *rewindInterval = nil;
|
|
||||||
static NSMutableDictionary *params = nil;
|
|
||||||
static MPMediaItemArtwork* artwork = nil;
|
|
||||||
|
|
||||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
|
||||||
@synchronized(self) {
|
|
||||||
// TODO: Need a reliable way to detect whether this is the client
|
|
||||||
// or background.
|
|
||||||
// TODO: Handle multiple clients.
|
|
||||||
// As no separate isolate is used on macOS, add both handlers to the one registrar.
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
if (channel == nil) {
|
|
||||||
#endif
|
|
||||||
AudioServicePlugin *instance = [[AudioServicePlugin alloc] init:registrar];
|
|
||||||
channel = [FlutterMethodChannel
|
|
||||||
methodChannelWithName:@"ryanheise.com/audioService"
|
|
||||||
binaryMessenger:[registrar messenger]];
|
|
||||||
[registrar addMethodCallDelegate:instance channel:channel];
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
} else {
|
|
||||||
AudioServicePlugin *instance = [[AudioServicePlugin alloc] init:registrar];
|
|
||||||
#endif
|
|
||||||
backgroundChannel = [FlutterMethodChannel
|
|
||||||
methodChannelWithName:@"ryanheise.com/audioServiceBackground"
|
|
||||||
binaryMessenger:[registrar messenger]];
|
|
||||||
[registrar addMethodCallDelegate:instance channel:backgroundChannel];
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar {
|
|
||||||
self = [super init];
|
|
||||||
NSAssert(self, @"super init cannot be nil");
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)broadcastPlaybackState {
|
|
||||||
[channel invokeMethod:@"onPlaybackStateChanged" arguments:@[
|
|
||||||
// processingState
|
|
||||||
@(processingState),
|
|
||||||
// playing
|
|
||||||
@(playing),
|
|
||||||
// actions
|
|
||||||
@(actionBits),
|
|
||||||
// position
|
|
||||||
position,
|
|
||||||
// bufferedPosition
|
|
||||||
bufferedPosition,
|
|
||||||
// playback speed
|
|
||||||
speed,
|
|
||||||
// update time since epoch
|
|
||||||
updateTime,
|
|
||||||
// repeat mode
|
|
||||||
repeatMode,
|
|
||||||
// shuffle mode
|
|
||||||
shuffleMode,
|
|
||||||
]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
|
|
||||||
// TODO:
|
|
||||||
// - Restructure this so that we have a separate method call delegate
|
|
||||||
// for the client instance and the background instance so that methods
|
|
||||||
// can't be called on the wrong instance.
|
|
||||||
if ([@"connect" isEqualToString:call.method]) {
|
|
||||||
long long msSinceEpoch = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
|
||||||
if (position == nil) {
|
|
||||||
position = @(0);
|
|
||||||
bufferedPosition = @(0);
|
|
||||||
updateTime = [NSNumber numberWithLongLong: msSinceEpoch];
|
|
||||||
speed = [NSNumber numberWithDouble: 1.0];
|
|
||||||
repeatMode = @(0);
|
|
||||||
shuffleMode = @(0);
|
|
||||||
}
|
|
||||||
// Notify client of state on subscribing.
|
|
||||||
[self broadcastPlaybackState];
|
|
||||||
[channel invokeMethod:@"onMediaChanged" arguments:@[mediaItem ? mediaItem : [NSNull null]]];
|
|
||||||
[channel invokeMethod:@"onQueueChanged" arguments:@[queue ? queue : [NSNull null]]];
|
|
||||||
|
|
||||||
result(nil);
|
|
||||||
} else if ([@"disconnect" isEqualToString:call.method]) {
|
|
||||||
result(nil);
|
|
||||||
} else if ([@"start" isEqualToString:call.method]) {
|
|
||||||
if (_running) {
|
|
||||||
result(@NO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_running = YES;
|
|
||||||
// The result will be sent after the background task actually starts.
|
|
||||||
// See the "ready" case below.
|
|
||||||
startResult = result;
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
[AVAudioSession sharedInstance];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set callbacks on MPRemoteCommandCenter
|
|
||||||
fastForwardInterval = [call.arguments objectForKey:@"fastForwardInterval"];
|
|
||||||
rewindInterval = [call.arguments objectForKey:@"rewindInterval"];
|
|
||||||
commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
|
|
||||||
commands = @[
|
|
||||||
commandCenter.stopCommand,
|
|
||||||
commandCenter.pauseCommand,
|
|
||||||
commandCenter.playCommand,
|
|
||||||
commandCenter.skipBackwardCommand,
|
|
||||||
commandCenter.previousTrackCommand,
|
|
||||||
commandCenter.nextTrackCommand,
|
|
||||||
commandCenter.skipForwardCommand,
|
|
||||||
[NSNull null],
|
|
||||||
commandCenter.changePlaybackPositionCommand,
|
|
||||||
commandCenter.togglePlayPauseCommand,
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
commandCenter.changeRepeatModeCommand,
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
commandCenter.changeShuffleModeCommand,
|
|
||||||
commandCenter.seekBackwardCommand,
|
|
||||||
commandCenter.seekForwardCommand,
|
|
||||||
];
|
|
||||||
[commandCenter.changePlaybackRateCommand setEnabled:YES];
|
|
||||||
[commandCenter.togglePlayPauseCommand setEnabled:YES];
|
|
||||||
[commandCenter.togglePlayPauseCommand addTarget:self action:@selector(togglePlayPause:)];
|
|
||||||
// TODO: enable more commands
|
|
||||||
// Language options
|
|
||||||
if (@available(iOS 9.0, macOS 10.12.2, *)) {
|
|
||||||
[commandCenter.enableLanguageOptionCommand setEnabled:NO];
|
|
||||||
[commandCenter.disableLanguageOptionCommand setEnabled:NO];
|
|
||||||
}
|
|
||||||
// Rating
|
|
||||||
[commandCenter.ratingCommand setEnabled:NO];
|
|
||||||
// Feedback
|
|
||||||
[commandCenter.likeCommand setEnabled:NO];
|
|
||||||
[commandCenter.dislikeCommand setEnabled:NO];
|
|
||||||
[commandCenter.bookmarkCommand setEnabled:NO];
|
|
||||||
[self updateControls];
|
|
||||||
|
|
||||||
// Params
|
|
||||||
params = [call.arguments objectForKey:@"params"];
|
|
||||||
|
|
||||||
#if TARGET_OS_OSX
|
|
||||||
// No isolate can be used for macOS until https://github.com/flutter/flutter/issues/65222 is resolved.
|
|
||||||
// We send a result here, and then the Dart code continues in the main isolate.
|
|
||||||
result(@YES);
|
|
||||||
#endif
|
|
||||||
} else if ([@"ready" isEqualToString:call.method]) {
|
|
||||||
NSMutableDictionary *startParams = [NSMutableDictionary new];
|
|
||||||
startParams[@"fastForwardInterval"] = fastForwardInterval;
|
|
||||||
startParams[@"rewindInterval"] = rewindInterval;
|
|
||||||
startParams[@"params"] = params;
|
|
||||||
result(startParams);
|
|
||||||
} else if ([@"started" isEqualToString:call.method]) {
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
if (startResult) {
|
|
||||||
startResult(@YES);
|
|
||||||
startResult = nil;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"stopped" isEqualToString:call.method]) {
|
|
||||||
_running = NO;
|
|
||||||
[channel invokeMethod:@"onStopped" arguments:nil];
|
|
||||||
[commandCenter.changePlaybackRateCommand setEnabled:NO];
|
|
||||||
[commandCenter.togglePlayPauseCommand setEnabled:NO];
|
|
||||||
[commandCenter.togglePlayPauseCommand removeTarget:nil];
|
|
||||||
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nil;
|
|
||||||
processingState = none;
|
|
||||||
playing = NO;
|
|
||||||
position = nil;
|
|
||||||
bufferedPosition = nil;
|
|
||||||
updateTime = nil;
|
|
||||||
speed = nil;
|
|
||||||
artwork = nil;
|
|
||||||
mediaItem = nil;
|
|
||||||
repeatMode = @(0);
|
|
||||||
shuffleMode = @(0);
|
|
||||||
actionBits = 0;
|
|
||||||
[self updateControls];
|
|
||||||
_controlsUpdated = NO;
|
|
||||||
queue = nil;
|
|
||||||
startResult = nil;
|
|
||||||
fastForwardInterval = nil;
|
|
||||||
rewindInterval = nil;
|
|
||||||
params = nil;
|
|
||||||
commandCenter = nil;
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"isRunning" isEqualToString:call.method]) {
|
|
||||||
if (_running) {
|
|
||||||
result(@YES);
|
|
||||||
} else {
|
|
||||||
result(@NO);
|
|
||||||
}
|
|
||||||
} else if ([@"setBrowseMediaParent" isEqualToString:call.method]) {
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"addQueueItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onAddQueueItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"addQueueItemAt" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onAddQueueItemAt" arguments:call.arguments result: result];
|
|
||||||
} else if ([@"removeQueueItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onRemoveQueueItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"updateQueue" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onUpdateQueue" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"updateMediaItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onUpdateMediaItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"click" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onClick" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"prepare" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPrepare" arguments:nil result: result];
|
|
||||||
} else if ([@"prepareFromMediaId" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPrepareFromMediaId" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"play" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPlay" arguments:nil result: result];
|
|
||||||
} else if ([@"playFromMediaId" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPlayFromMediaId" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"playMediaItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPlayMediaItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"skipToQueueItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToQueueItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"pause" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPause" arguments:nil result: result];
|
|
||||||
} else if ([@"stop" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onStop" arguments:nil result: result];
|
|
||||||
} else if ([@"seekTo" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekTo" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"skipToNext" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToNext" arguments:nil result: result];
|
|
||||||
} else if ([@"skipToPrevious" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToPrevious" arguments:nil result: result];
|
|
||||||
} else if ([@"fastForward" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onFastForward" arguments:nil result: result];
|
|
||||||
} else if ([@"rewind" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onRewind" arguments:nil result: result];
|
|
||||||
} else if ([@"setRepeatMode" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetRepeatMode" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"setShuffleMode" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetShuffleMode" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"setRating" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetRating" arguments:@[call.arguments[@"rating"], call.arguments[@"extras"]] result: result];
|
|
||||||
} else if ([@"setSpeed" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetSpeed" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"seekForward" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekForward" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"seekBackward" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekBackward" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"setState" isEqualToString:call.method]) {
|
|
||||||
long long msSinceEpoch;
|
|
||||||
if (call.arguments[7] != [NSNull null]) {
|
|
||||||
msSinceEpoch = [call.arguments[7] longLongValue];
|
|
||||||
} else {
|
|
||||||
msSinceEpoch = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
|
||||||
}
|
|
||||||
actionBits = 0;
|
|
||||||
NSArray *controlsArray = call.arguments[0];
|
|
||||||
for (int i = 0; i < controlsArray.count; i++) {
|
|
||||||
NSDictionary *control = (NSDictionary *)controlsArray[i];
|
|
||||||
NSNumber *actionIndex = (NSNumber *)control[@"action"];
|
|
||||||
int actionCode = 1 << [actionIndex intValue];
|
|
||||||
actionBits |= actionCode;
|
|
||||||
}
|
|
||||||
NSArray *systemActionsArray = call.arguments[1];
|
|
||||||
for (int i = 0; i < systemActionsArray.count; i++) {
|
|
||||||
NSNumber *actionIndex = (NSNumber *)systemActionsArray[i];
|
|
||||||
int actionCode = 1 << [actionIndex intValue];
|
|
||||||
actionBits |= actionCode;
|
|
||||||
}
|
|
||||||
processingState = [call.arguments[2] intValue];
|
|
||||||
playing = [call.arguments[3] boolValue];
|
|
||||||
position = call.arguments[4];
|
|
||||||
bufferedPosition = call.arguments[5];
|
|
||||||
speed = call.arguments[6];
|
|
||||||
repeatMode = call.arguments[9];
|
|
||||||
shuffleMode = call.arguments[10];
|
|
||||||
updateTime = [NSNumber numberWithLongLong: msSinceEpoch];
|
|
||||||
[self broadcastPlaybackState];
|
|
||||||
[self updateControls];
|
|
||||||
[self updateNowPlayingInfo];
|
|
||||||
result(@(YES));
|
|
||||||
} else if ([@"setQueue" isEqualToString:call.method]) {
|
|
||||||
queue = call.arguments;
|
|
||||||
[channel invokeMethod:@"onQueueChanged" arguments:@[queue]];
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"setMediaItem" isEqualToString:call.method]) {
|
|
||||||
mediaItem = call.arguments;
|
|
||||||
NSString* artUri = mediaItem[@"artUri"];
|
|
||||||
artwork = nil;
|
|
||||||
if (![artUri isEqual: [NSNull null]]) {
|
|
||||||
NSString* artCacheFilePath = [NSNull null];
|
|
||||||
NSDictionary* extras = mediaItem[@"extras"];
|
|
||||||
if (![extras isEqual: [NSNull null]]) {
|
|
||||||
artCacheFilePath = extras[@"artCacheFile"];
|
|
||||||
}
|
|
||||||
if (![artCacheFilePath isEqual: [NSNull null]]) {
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
UIImage* artImage = [UIImage imageWithContentsOfFile:artCacheFilePath];
|
|
||||||
#else
|
|
||||||
NSImage* artImage = [[NSImage alloc] initWithContentsOfFile:artCacheFilePath];
|
|
||||||
#endif
|
|
||||||
if (artImage != nil) {
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
artwork = [[MPMediaItemArtwork alloc] initWithImage: artImage];
|
|
||||||
#else
|
|
||||||
artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artImage.size requestHandler:^NSImage* _Nonnull(CGSize aSize) {
|
|
||||||
return artImage;
|
|
||||||
}];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[self updateNowPlayingInfo];
|
|
||||||
[channel invokeMethod:@"onMediaChanged" arguments:@[call.arguments]];
|
|
||||||
result(@(YES));
|
|
||||||
} else if ([@"notifyChildrenChanged" isEqualToString:call.method]) {
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"androidForceEnableMediaButtons" isEqualToString:call.method]) {
|
|
||||||
result(@YES);
|
|
||||||
} else {
|
|
||||||
// TODO: Check if this implementation is correct.
|
|
||||||
// Can I just pass on the result as the last argument?
|
|
||||||
[backgroundChannel invokeMethod:call.method arguments:call.arguments result: result];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) play: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"play");
|
|
||||||
[backgroundChannel invokeMethod:@"onPlay" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) pause: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"pause");
|
|
||||||
[backgroundChannel invokeMethod:@"onPause" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateNowPlayingInfo {
|
|
||||||
NSMutableDictionary *nowPlayingInfo = [NSMutableDictionary new];
|
|
||||||
if (mediaItem) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyTitle] = mediaItem[@"title"];
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = mediaItem[@"album"];
|
|
||||||
if (mediaItem[@"artist"] != [NSNull null]) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtist] = mediaItem[@"artist"];
|
|
||||||
}
|
|
||||||
if (mediaItem[@"duration"] != [NSNull null]) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = [NSNumber numberWithLongLong: ([mediaItem[@"duration"] longLongValue] / 1000)];
|
|
||||||
}
|
|
||||||
if (@available(iOS 3.0, macOS 10.13.2, *)) {
|
|
||||||
if (artwork) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = [NSNumber numberWithInt:([position intValue] / 1000)];
|
|
||||||
}
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = [NSNumber numberWithDouble: playing ? 1.0 : 0.0];
|
|
||||||
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateControls {
|
|
||||||
for (enum MediaAction action = AStop; action <= ASeekForward; action++) {
|
|
||||||
[self updateControl:action];
|
|
||||||
}
|
|
||||||
_controlsUpdated = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateControl:(enum MediaAction)action {
|
|
||||||
MPRemoteCommand *command = commands[action];
|
|
||||||
if (command == [NSNull null]) return;
|
|
||||||
// Shift the actionBits right until the least significant bit is the tested action bit, and AND that with a 1 at the same position.
|
|
||||||
// All bytes become 0, other than the tested action bit, which will be 0 or 1 according to its status in the actionBits long.
|
|
||||||
BOOL enable = ((actionBits >> action) & 1);
|
|
||||||
if (_controlsUpdated && enable == command.enabled) return;
|
|
||||||
[command setEnabled:enable];
|
|
||||||
switch (action) {
|
|
||||||
case AStop:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.stopCommand addTarget:self action:@selector(stop:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.stopCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case APause:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.pauseCommand addTarget:self action:@selector(pause:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.pauseCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case APlay:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.playCommand addTarget:self action:@selector(play:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.playCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ARewind:
|
|
||||||
if (rewindInterval.integerValue > 0) {
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.skipBackwardCommand addTarget: self action:@selector(skipBackward:)];
|
|
||||||
int rewindIntervalInSeconds = [rewindInterval intValue]/1000;
|
|
||||||
NSNumber *rewindIntervalInSec = [NSNumber numberWithInt: rewindIntervalInSeconds];
|
|
||||||
commandCenter.skipBackwardCommand.preferredIntervals = @[rewindIntervalInSec];
|
|
||||||
} else {
|
|
||||||
[commandCenter.skipBackwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASkipToPrevious:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.previousTrackCommand addTarget:self action:@selector(previousTrack:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.previousTrackCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASkipToNext:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.nextTrackCommand addTarget:self action:@selector(nextTrack:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.nextTrackCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AFastForward:
|
|
||||||
if (fastForwardInterval.integerValue > 0) {
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.skipForwardCommand addTarget: self action:@selector(skipForward:)];
|
|
||||||
int fastForwardIntervalInSeconds = [fastForwardInterval intValue]/1000;
|
|
||||||
NSNumber *fastForwardIntervalInSec = [NSNumber numberWithInt: fastForwardIntervalInSeconds];
|
|
||||||
commandCenter.skipForwardCommand.preferredIntervals = @[fastForwardIntervalInSec];
|
|
||||||
} else {
|
|
||||||
[commandCenter.skipForwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASetRating:
|
|
||||||
// TODO:
|
|
||||||
// commandCenter.ratingCommand
|
|
||||||
// commandCenter.dislikeCommand
|
|
||||||
// commandCenter.bookmarkCommand
|
|
||||||
break;
|
|
||||||
case ASeekTo:
|
|
||||||
if (@available(iOS 9.1, macOS 10.12.2, *)) {
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.changePlaybackPositionCommand addTarget:self action:@selector(changePlaybackPosition:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.changePlaybackPositionCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case APlayPause:
|
|
||||||
// Automatically enabled.
|
|
||||||
break;
|
|
||||||
case ASetRepeatMode:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.changeRepeatModeCommand addTarget:self action:@selector(changeRepeatMode:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.changeRepeatModeCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASetShuffleMode:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.changeShuffleModeCommand addTarget:self action:@selector(changeShuffleMode:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.changeShuffleModeCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASeekBackward:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.seekBackwardCommand addTarget:self action:@selector(seekBackward:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.seekBackwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASeekForward:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.seekForwardCommand addTarget:self action:@selector(seekForward:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.seekForwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) togglePlayPause: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"togglePlayPause");
|
|
||||||
[backgroundChannel invokeMethod:@"onClick" arguments:@[@(0)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) stop: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"stop");
|
|
||||||
[backgroundChannel invokeMethod:@"onStop" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) nextTrack: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"nextTrack");
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToNext" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) previousTrack: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"previousTrack");
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToPrevious" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) changePlaybackPosition: (MPChangePlaybackPositionCommandEvent *) event {
|
|
||||||
NSLog(@"changePlaybackPosition");
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekTo" arguments: @[@((long long) (event.positionTime * 1000))]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) skipForward: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"skipForward");
|
|
||||||
[backgroundChannel invokeMethod:@"onFastForward" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) skipBackward: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"skipBackward");
|
|
||||||
[backgroundChannel invokeMethod:@"onRewind" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) seekForward: (MPSeekCommandEvent *) event {
|
|
||||||
NSLog(@"seekForward");
|
|
||||||
BOOL begin = event.type == MPSeekCommandEventTypeBeginSeeking;
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekForward" arguments:@[@(begin)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) seekBackward: (MPSeekCommandEvent *) event {
|
|
||||||
NSLog(@"seekBackward");
|
|
||||||
BOOL begin = event.type == MPSeekCommandEventTypeBeginSeeking;
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekBackward" arguments:@[@(begin)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) changeRepeatMode: (MPChangeRepeatModeCommandEvent *) event {
|
|
||||||
NSLog(@"changeRepeatMode");
|
|
||||||
int modeIndex;
|
|
||||||
switch (event.repeatType) {
|
|
||||||
case MPRepeatTypeOff:
|
|
||||||
modeIndex = 0;
|
|
||||||
break;
|
|
||||||
case MPRepeatTypeOne:
|
|
||||||
modeIndex = 1;
|
|
||||||
break;
|
|
||||||
// MPRepeatTypeAll
|
|
||||||
default:
|
|
||||||
modeIndex = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[backgroundChannel invokeMethod:@"onSetRepeatMode" arguments:@[@(modeIndex)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) changeShuffleMode: (MPChangeShuffleModeCommandEvent *) event {
|
|
||||||
NSLog(@"changeShuffleMode");
|
|
||||||
int modeIndex;
|
|
||||||
switch (event.shuffleType) {
|
|
||||||
case MPShuffleTypeOff:
|
|
||||||
modeIndex = 0;
|
|
||||||
break;
|
|
||||||
case MPShuffleTypeItems:
|
|
||||||
modeIndex = 1;
|
|
||||||
break;
|
|
||||||
// MPShuffleTypeCollections
|
|
||||||
default:
|
|
||||||
modeIndex = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[backgroundChannel invokeMethod:@"onSetShuffleMode" arguments:@[@(modeIndex)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) dealloc {
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
1
ios/Classes/AudioServicePlugin.m
Symbolic link
1
ios/Classes/AudioServicePlugin.m
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../darwin/Classes/AudioServicePlugin.m
|
@ -1,617 +0,0 @@
|
|||||||
#import "AudioServicePlugin.h"
|
|
||||||
#import <AVFoundation/AVFoundation.h>
|
|
||||||
#import <MediaPlayer/MediaPlayer.h>
|
|
||||||
|
|
||||||
// If you'd like to help, please see the TODO comments below, then open a
|
|
||||||
// GitHub issue to announce your intention to work on a particular feature, and
|
|
||||||
// submit a pull request. We have an open discussion over at issue #10 about
|
|
||||||
// all things iOS if you'd like to discuss approaches or ask for input. Thank
|
|
||||||
// you for your support!
|
|
||||||
|
|
||||||
@implementation AudioServicePlugin
|
|
||||||
|
|
||||||
static FlutterMethodChannel *channel = nil;
|
|
||||||
static FlutterMethodChannel *backgroundChannel = nil;
|
|
||||||
static BOOL _running = NO;
|
|
||||||
static FlutterResult startResult = nil;
|
|
||||||
static MPRemoteCommandCenter *commandCenter = nil;
|
|
||||||
static NSArray *queue = nil;
|
|
||||||
static NSMutableDictionary *mediaItem = nil;
|
|
||||||
static long actionBits;
|
|
||||||
static NSArray *commands;
|
|
||||||
static BOOL _controlsUpdated = NO;
|
|
||||||
static enum AudioProcessingState processingState = none;
|
|
||||||
static BOOL playing = NO;
|
|
||||||
static NSNumber *position = nil;
|
|
||||||
static NSNumber *bufferedPosition = nil;
|
|
||||||
static NSNumber *updateTime = nil;
|
|
||||||
static NSNumber *speed = nil;
|
|
||||||
static NSNumber *repeatMode = nil;
|
|
||||||
static NSNumber *shuffleMode = nil;
|
|
||||||
static NSNumber *fastForwardInterval = nil;
|
|
||||||
static NSNumber *rewindInterval = nil;
|
|
||||||
static NSMutableDictionary *params = nil;
|
|
||||||
static MPMediaItemArtwork* artwork = nil;
|
|
||||||
|
|
||||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
|
||||||
@synchronized(self) {
|
|
||||||
// TODO: Need a reliable way to detect whether this is the client
|
|
||||||
// or background.
|
|
||||||
// TODO: Handle multiple clients.
|
|
||||||
// As no separate isolate is used on macOS, add both handlers to the one registrar.
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
if (channel == nil) {
|
|
||||||
#endif
|
|
||||||
AudioServicePlugin *instance = [[AudioServicePlugin alloc] init:registrar];
|
|
||||||
channel = [FlutterMethodChannel
|
|
||||||
methodChannelWithName:@"ryanheise.com/audioService"
|
|
||||||
binaryMessenger:[registrar messenger]];
|
|
||||||
[registrar addMethodCallDelegate:instance channel:channel];
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
} else {
|
|
||||||
AudioServicePlugin *instance = [[AudioServicePlugin alloc] init:registrar];
|
|
||||||
#endif
|
|
||||||
backgroundChannel = [FlutterMethodChannel
|
|
||||||
methodChannelWithName:@"ryanheise.com/audioServiceBackground"
|
|
||||||
binaryMessenger:[registrar messenger]];
|
|
||||||
[registrar addMethodCallDelegate:instance channel:backgroundChannel];
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)init:(NSObject<FlutterPluginRegistrar> *)registrar {
|
|
||||||
self = [super init];
|
|
||||||
NSAssert(self, @"super init cannot be nil");
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)broadcastPlaybackState {
|
|
||||||
[channel invokeMethod:@"onPlaybackStateChanged" arguments:@[
|
|
||||||
// processingState
|
|
||||||
@(processingState),
|
|
||||||
// playing
|
|
||||||
@(playing),
|
|
||||||
// actions
|
|
||||||
@(actionBits),
|
|
||||||
// position
|
|
||||||
position,
|
|
||||||
// bufferedPosition
|
|
||||||
bufferedPosition,
|
|
||||||
// playback speed
|
|
||||||
speed,
|
|
||||||
// update time since epoch
|
|
||||||
updateTime,
|
|
||||||
// repeat mode
|
|
||||||
repeatMode,
|
|
||||||
// shuffle mode
|
|
||||||
shuffleMode,
|
|
||||||
]];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
|
|
||||||
// TODO:
|
|
||||||
// - Restructure this so that we have a separate method call delegate
|
|
||||||
// for the client instance and the background instance so that methods
|
|
||||||
// can't be called on the wrong instance.
|
|
||||||
if ([@"connect" isEqualToString:call.method]) {
|
|
||||||
long long msSinceEpoch = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
|
||||||
if (position == nil) {
|
|
||||||
position = @(0);
|
|
||||||
bufferedPosition = @(0);
|
|
||||||
updateTime = [NSNumber numberWithLongLong: msSinceEpoch];
|
|
||||||
speed = [NSNumber numberWithDouble: 1.0];
|
|
||||||
repeatMode = @(0);
|
|
||||||
shuffleMode = @(0);
|
|
||||||
}
|
|
||||||
// Notify client of state on subscribing.
|
|
||||||
[self broadcastPlaybackState];
|
|
||||||
[channel invokeMethod:@"onMediaChanged" arguments:@[mediaItem ? mediaItem : [NSNull null]]];
|
|
||||||
[channel invokeMethod:@"onQueueChanged" arguments:@[queue ? queue : [NSNull null]]];
|
|
||||||
|
|
||||||
result(nil);
|
|
||||||
} else if ([@"disconnect" isEqualToString:call.method]) {
|
|
||||||
result(nil);
|
|
||||||
} else if ([@"start" isEqualToString:call.method]) {
|
|
||||||
if (_running) {
|
|
||||||
result(@NO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_running = YES;
|
|
||||||
// The result will be sent after the background task actually starts.
|
|
||||||
// See the "ready" case below.
|
|
||||||
startResult = result;
|
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
[AVAudioSession sharedInstance];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set callbacks on MPRemoteCommandCenter
|
|
||||||
fastForwardInterval = [call.arguments objectForKey:@"fastForwardInterval"];
|
|
||||||
rewindInterval = [call.arguments objectForKey:@"rewindInterval"];
|
|
||||||
commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
|
|
||||||
commands = @[
|
|
||||||
commandCenter.stopCommand,
|
|
||||||
commandCenter.pauseCommand,
|
|
||||||
commandCenter.playCommand,
|
|
||||||
commandCenter.skipBackwardCommand,
|
|
||||||
commandCenter.previousTrackCommand,
|
|
||||||
commandCenter.nextTrackCommand,
|
|
||||||
commandCenter.skipForwardCommand,
|
|
||||||
[NSNull null],
|
|
||||||
commandCenter.changePlaybackPositionCommand,
|
|
||||||
commandCenter.togglePlayPauseCommand,
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
commandCenter.changeRepeatModeCommand,
|
|
||||||
[NSNull null],
|
|
||||||
[NSNull null],
|
|
||||||
commandCenter.changeShuffleModeCommand,
|
|
||||||
commandCenter.seekBackwardCommand,
|
|
||||||
commandCenter.seekForwardCommand,
|
|
||||||
];
|
|
||||||
[commandCenter.changePlaybackRateCommand setEnabled:YES];
|
|
||||||
[commandCenter.togglePlayPauseCommand setEnabled:YES];
|
|
||||||
[commandCenter.togglePlayPauseCommand addTarget:self action:@selector(togglePlayPause:)];
|
|
||||||
// TODO: enable more commands
|
|
||||||
// Language options
|
|
||||||
if (@available(iOS 9.0, macOS 10.12.2, *)) {
|
|
||||||
[commandCenter.enableLanguageOptionCommand setEnabled:NO];
|
|
||||||
[commandCenter.disableLanguageOptionCommand setEnabled:NO];
|
|
||||||
}
|
|
||||||
// Rating
|
|
||||||
[commandCenter.ratingCommand setEnabled:NO];
|
|
||||||
// Feedback
|
|
||||||
[commandCenter.likeCommand setEnabled:NO];
|
|
||||||
[commandCenter.dislikeCommand setEnabled:NO];
|
|
||||||
[commandCenter.bookmarkCommand setEnabled:NO];
|
|
||||||
[self updateControls];
|
|
||||||
|
|
||||||
// Params
|
|
||||||
params = [call.arguments objectForKey:@"params"];
|
|
||||||
|
|
||||||
#if TARGET_OS_OSX
|
|
||||||
// No isolate can be used for macOS until https://github.com/flutter/flutter/issues/65222 is resolved.
|
|
||||||
// We send a result here, and then the Dart code continues in the main isolate.
|
|
||||||
result(@YES);
|
|
||||||
#endif
|
|
||||||
} else if ([@"ready" isEqualToString:call.method]) {
|
|
||||||
NSMutableDictionary *startParams = [NSMutableDictionary new];
|
|
||||||
startParams[@"fastForwardInterval"] = fastForwardInterval;
|
|
||||||
startParams[@"rewindInterval"] = rewindInterval;
|
|
||||||
startParams[@"params"] = params;
|
|
||||||
result(startParams);
|
|
||||||
} else if ([@"started" isEqualToString:call.method]) {
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
if (startResult) {
|
|
||||||
startResult(@YES);
|
|
||||||
startResult = nil;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"stopped" isEqualToString:call.method]) {
|
|
||||||
_running = NO;
|
|
||||||
[channel invokeMethod:@"onStopped" arguments:nil];
|
|
||||||
[commandCenter.changePlaybackRateCommand setEnabled:NO];
|
|
||||||
[commandCenter.togglePlayPauseCommand setEnabled:NO];
|
|
||||||
[commandCenter.togglePlayPauseCommand removeTarget:nil];
|
|
||||||
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nil;
|
|
||||||
processingState = none;
|
|
||||||
playing = NO;
|
|
||||||
position = nil;
|
|
||||||
bufferedPosition = nil;
|
|
||||||
updateTime = nil;
|
|
||||||
speed = nil;
|
|
||||||
artwork = nil;
|
|
||||||
mediaItem = nil;
|
|
||||||
repeatMode = @(0);
|
|
||||||
shuffleMode = @(0);
|
|
||||||
actionBits = 0;
|
|
||||||
[self updateControls];
|
|
||||||
_controlsUpdated = NO;
|
|
||||||
queue = nil;
|
|
||||||
startResult = nil;
|
|
||||||
fastForwardInterval = nil;
|
|
||||||
rewindInterval = nil;
|
|
||||||
params = nil;
|
|
||||||
commandCenter = nil;
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"isRunning" isEqualToString:call.method]) {
|
|
||||||
if (_running) {
|
|
||||||
result(@YES);
|
|
||||||
} else {
|
|
||||||
result(@NO);
|
|
||||||
}
|
|
||||||
} else if ([@"setBrowseMediaParent" isEqualToString:call.method]) {
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"addQueueItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onAddQueueItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"addQueueItemAt" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onAddQueueItemAt" arguments:call.arguments result: result];
|
|
||||||
} else if ([@"removeQueueItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onRemoveQueueItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"updateQueue" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onUpdateQueue" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"updateMediaItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onUpdateMediaItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"click" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onClick" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"prepare" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPrepare" arguments:nil result: result];
|
|
||||||
} else if ([@"prepareFromMediaId" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPrepareFromMediaId" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"play" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPlay" arguments:nil result: result];
|
|
||||||
} else if ([@"playFromMediaId" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPlayFromMediaId" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"playMediaItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPlayMediaItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"skipToQueueItem" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToQueueItem" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"pause" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onPause" arguments:nil result: result];
|
|
||||||
} else if ([@"stop" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onStop" arguments:nil result: result];
|
|
||||||
} else if ([@"seekTo" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekTo" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"skipToNext" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToNext" arguments:nil result: result];
|
|
||||||
} else if ([@"skipToPrevious" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToPrevious" arguments:nil result: result];
|
|
||||||
} else if ([@"fastForward" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onFastForward" arguments:nil result: result];
|
|
||||||
} else if ([@"rewind" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onRewind" arguments:nil result: result];
|
|
||||||
} else if ([@"setRepeatMode" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetRepeatMode" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"setShuffleMode" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetShuffleMode" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"setRating" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetRating" arguments:@[call.arguments[@"rating"], call.arguments[@"extras"]] result: result];
|
|
||||||
} else if ([@"setSpeed" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSetSpeed" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"seekForward" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekForward" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"seekBackward" isEqualToString:call.method]) {
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekBackward" arguments:@[call.arguments] result: result];
|
|
||||||
} else if ([@"setState" isEqualToString:call.method]) {
|
|
||||||
long long msSinceEpoch;
|
|
||||||
if (call.arguments[7] != [NSNull null]) {
|
|
||||||
msSinceEpoch = [call.arguments[7] longLongValue];
|
|
||||||
} else {
|
|
||||||
msSinceEpoch = (long long)([[NSDate date] timeIntervalSince1970] * 1000.0);
|
|
||||||
}
|
|
||||||
actionBits = 0;
|
|
||||||
NSArray *controlsArray = call.arguments[0];
|
|
||||||
for (int i = 0; i < controlsArray.count; i++) {
|
|
||||||
NSDictionary *control = (NSDictionary *)controlsArray[i];
|
|
||||||
NSNumber *actionIndex = (NSNumber *)control[@"action"];
|
|
||||||
int actionCode = 1 << [actionIndex intValue];
|
|
||||||
actionBits |= actionCode;
|
|
||||||
}
|
|
||||||
NSArray *systemActionsArray = call.arguments[1];
|
|
||||||
for (int i = 0; i < systemActionsArray.count; i++) {
|
|
||||||
NSNumber *actionIndex = (NSNumber *)systemActionsArray[i];
|
|
||||||
int actionCode = 1 << [actionIndex intValue];
|
|
||||||
actionBits |= actionCode;
|
|
||||||
}
|
|
||||||
processingState = [call.arguments[2] intValue];
|
|
||||||
playing = [call.arguments[3] boolValue];
|
|
||||||
position = call.arguments[4];
|
|
||||||
bufferedPosition = call.arguments[5];
|
|
||||||
speed = call.arguments[6];
|
|
||||||
repeatMode = call.arguments[9];
|
|
||||||
shuffleMode = call.arguments[10];
|
|
||||||
updateTime = [NSNumber numberWithLongLong: msSinceEpoch];
|
|
||||||
[self broadcastPlaybackState];
|
|
||||||
[self updateControls];
|
|
||||||
[self updateNowPlayingInfo];
|
|
||||||
result(@(YES));
|
|
||||||
} else if ([@"setQueue" isEqualToString:call.method]) {
|
|
||||||
queue = call.arguments;
|
|
||||||
[channel invokeMethod:@"onQueueChanged" arguments:@[queue]];
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"setMediaItem" isEqualToString:call.method]) {
|
|
||||||
mediaItem = call.arguments;
|
|
||||||
NSString* artUri = mediaItem[@"artUri"];
|
|
||||||
artwork = nil;
|
|
||||||
if (![artUri isEqual: [NSNull null]]) {
|
|
||||||
NSString* artCacheFilePath = [NSNull null];
|
|
||||||
NSDictionary* extras = mediaItem[@"extras"];
|
|
||||||
if (![extras isEqual: [NSNull null]]) {
|
|
||||||
artCacheFilePath = extras[@"artCacheFile"];
|
|
||||||
}
|
|
||||||
if (![artCacheFilePath isEqual: [NSNull null]]) {
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
UIImage* artImage = [UIImage imageWithContentsOfFile:artCacheFilePath];
|
|
||||||
#else
|
|
||||||
NSImage* artImage = [[NSImage alloc] initWithContentsOfFile:artCacheFilePath];
|
|
||||||
#endif
|
|
||||||
if (artImage != nil) {
|
|
||||||
#if TARGET_OS_IPHONE
|
|
||||||
artwork = [[MPMediaItemArtwork alloc] initWithImage: artImage];
|
|
||||||
#else
|
|
||||||
artwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artImage.size requestHandler:^NSImage* _Nonnull(CGSize aSize) {
|
|
||||||
return artImage;
|
|
||||||
}];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[self updateNowPlayingInfo];
|
|
||||||
[channel invokeMethod:@"onMediaChanged" arguments:@[call.arguments]];
|
|
||||||
result(@(YES));
|
|
||||||
} else if ([@"notifyChildrenChanged" isEqualToString:call.method]) {
|
|
||||||
result(@YES);
|
|
||||||
} else if ([@"androidForceEnableMediaButtons" isEqualToString:call.method]) {
|
|
||||||
result(@YES);
|
|
||||||
} else {
|
|
||||||
// TODO: Check if this implementation is correct.
|
|
||||||
// Can I just pass on the result as the last argument?
|
|
||||||
[backgroundChannel invokeMethod:call.method arguments:call.arguments result: result];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) play: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"play");
|
|
||||||
[backgroundChannel invokeMethod:@"onPlay" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) pause: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"pause");
|
|
||||||
[backgroundChannel invokeMethod:@"onPause" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateNowPlayingInfo {
|
|
||||||
NSMutableDictionary *nowPlayingInfo = [NSMutableDictionary new];
|
|
||||||
if (mediaItem) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyTitle] = mediaItem[@"title"];
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = mediaItem[@"album"];
|
|
||||||
if (mediaItem[@"artist"] != [NSNull null]) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtist] = mediaItem[@"artist"];
|
|
||||||
}
|
|
||||||
if (mediaItem[@"duration"] != [NSNull null]) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = [NSNumber numberWithLongLong: ([mediaItem[@"duration"] longLongValue] / 1000)];
|
|
||||||
}
|
|
||||||
if (@available(iOS 3.0, macOS 10.13.2, *)) {
|
|
||||||
if (artwork) {
|
|
||||||
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = [NSNumber numberWithInt:([position intValue] / 1000)];
|
|
||||||
}
|
|
||||||
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = [NSNumber numberWithDouble: playing ? 1.0 : 0.0];
|
|
||||||
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateControls {
|
|
||||||
for (enum MediaAction action = AStop; action <= ASeekForward; action++) {
|
|
||||||
[self updateControl:action];
|
|
||||||
}
|
|
||||||
_controlsUpdated = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) updateControl:(enum MediaAction)action {
|
|
||||||
MPRemoteCommand *command = commands[action];
|
|
||||||
if (command == [NSNull null]) return;
|
|
||||||
// Shift the actionBits right until the least significant bit is the tested action bit, and AND that with a 1 at the same position.
|
|
||||||
// All bytes become 0, other than the tested action bit, which will be 0 or 1 according to its status in the actionBits long.
|
|
||||||
BOOL enable = ((actionBits >> action) & 1);
|
|
||||||
if (_controlsUpdated && enable == command.enabled) return;
|
|
||||||
[command setEnabled:enable];
|
|
||||||
switch (action) {
|
|
||||||
case AStop:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.stopCommand addTarget:self action:@selector(stop:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.stopCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case APause:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.pauseCommand addTarget:self action:@selector(pause:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.pauseCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case APlay:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.playCommand addTarget:self action:@selector(play:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.playCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ARewind:
|
|
||||||
if (rewindInterval.integerValue > 0) {
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.skipBackwardCommand addTarget: self action:@selector(skipBackward:)];
|
|
||||||
int rewindIntervalInSeconds = [rewindInterval intValue]/1000;
|
|
||||||
NSNumber *rewindIntervalInSec = [NSNumber numberWithInt: rewindIntervalInSeconds];
|
|
||||||
commandCenter.skipBackwardCommand.preferredIntervals = @[rewindIntervalInSec];
|
|
||||||
} else {
|
|
||||||
[commandCenter.skipBackwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASkipToPrevious:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.previousTrackCommand addTarget:self action:@selector(previousTrack:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.previousTrackCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASkipToNext:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.nextTrackCommand addTarget:self action:@selector(nextTrack:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.nextTrackCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AFastForward:
|
|
||||||
if (fastForwardInterval.integerValue > 0) {
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.skipForwardCommand addTarget: self action:@selector(skipForward:)];
|
|
||||||
int fastForwardIntervalInSeconds = [fastForwardInterval intValue]/1000;
|
|
||||||
NSNumber *fastForwardIntervalInSec = [NSNumber numberWithInt: fastForwardIntervalInSeconds];
|
|
||||||
commandCenter.skipForwardCommand.preferredIntervals = @[fastForwardIntervalInSec];
|
|
||||||
} else {
|
|
||||||
[commandCenter.skipForwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASetRating:
|
|
||||||
// TODO:
|
|
||||||
// commandCenter.ratingCommand
|
|
||||||
// commandCenter.dislikeCommand
|
|
||||||
// commandCenter.bookmarkCommand
|
|
||||||
break;
|
|
||||||
case ASeekTo:
|
|
||||||
if (@available(iOS 9.1, macOS 10.12.2, *)) {
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.changePlaybackPositionCommand addTarget:self action:@selector(changePlaybackPosition:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.changePlaybackPositionCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case APlayPause:
|
|
||||||
// Automatically enabled.
|
|
||||||
break;
|
|
||||||
case ASetRepeatMode:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.changeRepeatModeCommand addTarget:self action:@selector(changeRepeatMode:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.changeRepeatModeCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASetShuffleMode:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.changeShuffleModeCommand addTarget:self action:@selector(changeShuffleMode:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.changeShuffleModeCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASeekBackward:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.seekBackwardCommand addTarget:self action:@selector(seekBackward:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.seekBackwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ASeekForward:
|
|
||||||
if (enable) {
|
|
||||||
[commandCenter.seekForwardCommand addTarget:self action:@selector(seekForward:)];
|
|
||||||
} else {
|
|
||||||
[commandCenter.seekForwardCommand removeTarget:nil];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) togglePlayPause: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"togglePlayPause");
|
|
||||||
[backgroundChannel invokeMethod:@"onClick" arguments:@[@(0)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) stop: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"stop");
|
|
||||||
[backgroundChannel invokeMethod:@"onStop" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) nextTrack: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"nextTrack");
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToNext" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) previousTrack: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"previousTrack");
|
|
||||||
[backgroundChannel invokeMethod:@"onSkipToPrevious" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) changePlaybackPosition: (MPChangePlaybackPositionCommandEvent *) event {
|
|
||||||
NSLog(@"changePlaybackPosition");
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekTo" arguments: @[@((long long) (event.positionTime * 1000))]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) skipForward: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"skipForward");
|
|
||||||
[backgroundChannel invokeMethod:@"onFastForward" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) skipBackward: (MPRemoteCommandEvent *) event {
|
|
||||||
NSLog(@"skipBackward");
|
|
||||||
[backgroundChannel invokeMethod:@"onRewind" arguments:nil];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) seekForward: (MPSeekCommandEvent *) event {
|
|
||||||
NSLog(@"seekForward");
|
|
||||||
BOOL begin = event.type == MPSeekCommandEventTypeBeginSeeking;
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekForward" arguments:@[@(begin)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) seekBackward: (MPSeekCommandEvent *) event {
|
|
||||||
NSLog(@"seekBackward");
|
|
||||||
BOOL begin = event.type == MPSeekCommandEventTypeBeginSeeking;
|
|
||||||
[backgroundChannel invokeMethod:@"onSeekBackward" arguments:@[@(begin)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) changeRepeatMode: (MPChangeRepeatModeCommandEvent *) event {
|
|
||||||
NSLog(@"changeRepeatMode");
|
|
||||||
int modeIndex;
|
|
||||||
switch (event.repeatType) {
|
|
||||||
case MPRepeatTypeOff:
|
|
||||||
modeIndex = 0;
|
|
||||||
break;
|
|
||||||
case MPRepeatTypeOne:
|
|
||||||
modeIndex = 1;
|
|
||||||
break;
|
|
||||||
// MPRepeatTypeAll
|
|
||||||
default:
|
|
||||||
modeIndex = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[backgroundChannel invokeMethod:@"onSetRepeatMode" arguments:@[@(modeIndex)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MPRemoteCommandHandlerStatus) changeShuffleMode: (MPChangeShuffleModeCommandEvent *) event {
|
|
||||||
NSLog(@"changeShuffleMode");
|
|
||||||
int modeIndex;
|
|
||||||
switch (event.shuffleType) {
|
|
||||||
case MPShuffleTypeOff:
|
|
||||||
modeIndex = 0;
|
|
||||||
break;
|
|
||||||
case MPShuffleTypeItems:
|
|
||||||
modeIndex = 1;
|
|
||||||
break;
|
|
||||||
// MPShuffleTypeCollections
|
|
||||||
default:
|
|
||||||
modeIndex = 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
[backgroundChannel invokeMethod:@"onSetShuffleMode" arguments:@[@(modeIndex)]];
|
|
||||||
return MPRemoteCommandHandlerStatusSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) dealloc {
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
1
macos/Classes/AudioServicePlugin.m
Symbolic link
1
macos/Classes/AudioServicePlugin.m
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../darwin/Classes/AudioServicePlugin.m
|
Loading…
Reference in New Issue
Block a user