From b268066d26c1cc28183bfc3f0f4ab60d31ebf1f7 Mon Sep 17 00:00:00 2001 From: exttex Date: Fri, 9 Oct 2020 20:46:36 +0200 Subject: [PATCH] For Freezer 0.5.0 --- .../ryanheise/audioservice/AudioService.java | 2 + .../audioservice/AudioServicePlugin.java | 1 + .../main/res/drawable-hdpi/ic_favorite.png | Bin 0 -> 803 bytes .../main/res/drawable-mdpi/ic_favorite.png | Bin 0 -> 597 bytes .../main/res/drawable-xhdpi/ic_favorite.png | Bin 0 -> 1207 bytes .../main/res/drawable-xxhdpi/ic_favorite.png | Bin 0 -> 1529 bytes .../main/res/drawable-xxxhdpi/ic_favorite.png | Bin 0 -> 2424 bytes ios/Classes/AudioServicePlugin.m | 618 +----------------- macos/Classes/AudioServicePlugin.m | 618 +----------------- 9 files changed, 5 insertions(+), 1234 deletions(-) create mode 100644 android/src/main/res/drawable-hdpi/ic_favorite.png create mode 100644 android/src/main/res/drawable-mdpi/ic_favorite.png create mode 100644 android/src/main/res/drawable-xhdpi/ic_favorite.png create mode 100644 android/src/main/res/drawable-xxhdpi/ic_favorite.png create mode 100644 android/src/main/res/drawable-xxxhdpi/ic_favorite.png mode change 100644 => 120000 ios/Classes/AudioServicePlugin.m mode change 100644 => 120000 macos/Classes/AudioServicePlugin.m diff --git a/android/src/main/java/com/ryanheise/audioservice/AudioService.java b/android/src/main/java/com/ryanheise/audioservice/AudioService.java index adb051a..53681fa 100644 --- a/android/src/main/java/com/ryanheise/audioservice/AudioService.java +++ b/android/src/main/java/com/ryanheise/audioservice/AudioService.java @@ -402,6 +402,7 @@ public class AudioService extends MediaBrowserServiceCompat { } } } + if (displayTitle != null) builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle); if (displaySubtitle != null) @@ -553,6 +554,7 @@ public class AudioService extends MediaBrowserServiceCompat { } public class MediaSessionCallback extends MediaSessionCompat.Callback { + @Override public void onAddQueueItem(MediaDescriptionCompat description) { if (listener == null) return; diff --git a/android/src/main/java/com/ryanheise/audioservice/AudioServicePlugin.java b/android/src/main/java/com/ryanheise/audioservice/AudioServicePlugin.java index eb04bd5..64922a8 100644 --- a/android/src/main/java/com/ryanheise/audioservice/AudioServicePlugin.java +++ b/android/src/main/java/com/ryanheise/audioservice/AudioServicePlugin.java @@ -60,6 +60,7 @@ import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.dart.DartExecutor.DartCallback; import android.content.res.AssetManager; +import android.util.Log; import io.flutter.view.FlutterNativeView; import io.flutter.view.FlutterRunArguments; diff --git a/android/src/main/res/drawable-hdpi/ic_favorite.png b/android/src/main/res/drawable-hdpi/ic_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..dceaa5d9d09eaba6ed57ca0f6f87071bbbed1813 GIT binary patch literal 803 zcmV+;1Kj+HP)j{0008&NklAN1_%*Dg$^X_|ZZW7;jKWEPO+&6R2yZ6kQQ&CY- zQBhG*(FDxHN^Hbd^kWM)U@7JX#_d24)~E6M(T7b~g#|^sWJ&Hre1rdRAI~t33B14) z+{M4xj}Kam+lk#chdXJU7irw_^m`uQ22NmE*{=1;eS~k*q_S`oLs--#ZZ{6&LdLky zFpQ7O7XJjJ8Ix<|EP5N_F2wJs<%nBDe_7%?aXY8@6xXmZjk^lx3yOOm`-&FdsyOi} zp5RN|D>&{$Y%fZ@NB&2>8IvW&eU8;d$bVN-f@Sk_Uh&1sTlKOT!;1HD)u$K<74O2$ z*xBb~6iY(n*Wyv++ds#K5czK0iky62Zlf!+d|H4rv9r&~u^{nO{Kj2H^F%e@U^%{T|Z%d+r{e&a7dg(k6)rm*U5KE?%&7TDE=cB#&&;=^7T^#b7c4y~xjKtChtn$9AsH7bN_;4`Q_Dyusruv} zXc)sjbjEhdjO60uQlR)lNgvS>+anms%TU%+;HT(|oqHijh7|v3QT)oR5uXCR_*3(4 zu!hUn6g$^az@}Dk`QeuK=|#mH)8#h6Vrt002ovPDHLkV1iecg46&2 literal 0 HcmV?d00001 diff --git a/android/src/main/res/drawable-mdpi/ic_favorite.png b/android/src/main/res/drawable-mdpi/ic_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..7770fd533e48dda7b662b478a2688646866abe25 GIT binary patch literal 597 zcmV-b0;>IqP){aYQzQR ziZ#3Pqo%Oz$IHceR;TqH=h^q{ds5%`%{g_>r_ZbBJe}t`r=g*qdg`fdP)0X);Q;#3 zgXRpiP1u0lI2exWv(&~SkKrN4@EKpjaU4%^4y_gHN;rj~aPR!u$KiToHMmA};=XKo zgDnA9LN8ustQ`s?90v}KcHjx7Gd6=!$p{_CyNJ43yucomW1#DB55FUt%{Bal58*r} z(|ClQtjMxt&i#yRlt(6UO@;Yh3AzHq1t9107HhLWH;I32d1qf1=$V3%a5)R~M$t;# z`Cp&|S)gZ&R^nn7=r(aL`MfiUomrr(@V-FkFxF&&w%}Gl$QfM4vVTGYcDrO5taV;v zYbIoXw&J$KE>!BNt=}+m1{4k^^=kAUktKrm!8juKH;( zfUYG$rm$@|EqQdYnUTCJ+m{qM4}2KkBal<*uNg1}ZIXNze+(nFP-Z1R9Qx5#b7;Ue z(0%|(0u j8myLOhh9%T^%Rpowm0*oE~>8C00000NkvXXu0mjf_8JwK literal 0 HcmV?d00001 diff --git a/android/src/main/res/drawable-xhdpi/ic_favorite.png b/android/src/main/res/drawable-xhdpi/ic_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..a0bcb2e84baaa72f0087e36cf42a196b7ae18a1b GIT binary patch literal 1207 zcmV;o1W5adP)33}+E zhiFldq8^emf+Px2vkf&ZHD?^>etP)*2X0_9cgFuVbI%tB=3$s~&-b47cYf!L8kI;S z5{X12kw_#GiNx>&V=)Vt;|AP;dyb+;NzTD{&L<%*tGz{eCkRVGcU; zDz`8&4NLJdKE&79g?%`PLpXq6@EtZ{Jyzlzj2S|i9hjYoTZd1u6+h$8tjt5%@4N9W zKE~_Wmpc<3MfeypS&LkmNjiXIXlRW_CVMAd!rVcWI~`Bsr|f_9wpga@Z~w**ctnmp zWyoL*F2a^URAeCbWJE*!+7qGV1JYsNCVTwUnJ zM>w+xp-UAz3DuQ-SS8Q#6jQ}6q|7m_E;R4~&d5{f1^BVrdR5AASdy2_4$@OC&C|BMof8)I5cb z#pCFzv})C`7x(2Rvq|a_aah()H1HW_7a?>aUe#W^rY@|?U1pQidD?4o`Wz<}CNz`! znD&U?cjLlBWoAOhU{l4FEt(#zEnH@k)P+X&1hq*K6Hz}x$KhU)ST)kvfm@ZG)+lEE zXN|1ja~GabRk}D%7b(~yWs>?7Gb@qVBz2xx{0?`vV_~H-`@qHcs$wB0!X8{-wa5P; zX8z$A**}7N)bJ)q2d)))K*NQG$i|*fwZG#c7X7R6hx!MkyT!$b)2jB?FXG5#HU6qp z*TB3Zk5kMtzy#4!k5udIf7yVus`fM}L{NH#`bk}f$yNIq5*UvM#TCG+$J8FYsqSOu zK#1=DfNJBYhw+LcE+15Z*!SNrF4mS!L*x~V44Hk1?!Q#IE%hkYD)X9QnTW;zRbt;< zG+kn-ss7q_kz`V@5drEVqErp>*uZKNG?o8&ys_R)QqNQFTvfA=u zr^rESkCu5n#G4=Q<{)#c_=56D=o-#o2HrCC#s4=rzv_C_3XzcZhIqlIReHo}!Q!ev ze=QNO+&rbV_e6T}9u`#nxoeHMB(X;S7bUv!n&A?Ds~QngUFP`D2pz@~^32z42SoJ$ zcJU-fBm3De9<8e9kwx7R8-Uxyl4!dKu$=f6>vjiM<3?$XnMfoOi9{liNF)-8#J_^c VfJ7ts9^C){002ovPDHLkV1ht(OP~M% literal 0 HcmV?d00001 diff --git a/android/src/main/res/drawable-xxhdpi/ic_favorite.png b/android/src/main/res/drawable-xxhdpi/ic_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..5baaadcda011c7999f6f1aa9a61520a6a10333ae GIT binary patch literal 1529 zcmVCn*A_Ds*r{`Tzs{T|QL`o8~} zJ$u$(d#z36~2e#FeiGv zold8Ng*Xpa;5OXT8}r?`rT6#m;%Mv~J>GEG4U4c84`ZFdOyL<^gT*+Yf-%p>H+uh= zPvFA=$NXQc#7}Sn_8Rq=YXukenlMZoaX$_m!Fcyou0R`xJmwE^xh7u;us42z)#whn zfty>eTtQA6!k8D}KINZulw*DyH)6jbw7MQJfm5(%gpHd{SNYC8cn8M3AFedou&47r zmSFc$WKk*HWbp68Vd>&6EFNII$Kws6dE9*7A3 z+32d1)m4W+M_K8TVtuNDwT7DqzbLxm)OUWB!T%__+SIqK%HVI0t~T{OUuE!*Mpv8q z-mNnDzeHD?`X*KNR*|v_HPyqUs+MEp)#z$d-z0XcD)^kdsgL(m^$HgL6kToVd!>@l zd%@ojU2W>SzslghA6;$gTUurCM@Lti`i`nH_+8{8j5=6XS?HUE|9o`S$*PFpZ;7rt zxjG{FbEB(nxh_tK2>#IM%3E$VcB(x1UieQ%SKm@yTpSX5FZh#;&)cR=vepjl?K8~a z=g9{)5q>Lri!^0}!Bm~~I3hIkUhsP-oY zS(#u+v-dH+9i8cnVGleR+_WaREjr7ADVzKcdfZ6*%o>sN@w1_XpAT^c_Yn??&Tm$f zJ>b(|E|1P_C~&R(kivDIWsKE^zJx)=XY^^bl(~QU&r5r1D%N}<%H@zqI=dDSctb` zbKRyX{4}~xjf6wgbIbIPumrnA_o%UO5LT+s3!4AId7?HVY7=EMhL=O*{>+Kd{b&{C zY}@65f&UZ7NB5zXFo9pIc9yS;zhPl?{jJsu|Edaue+^%bt~>SMvI;_fR6a|uADoH} zBM5w>at_bj=o-^jD0}e?dlBbNa^AiEaGbL3=WKn9>!RyP15jr9-kfFdm&n<;@q;7r z+ztf39_K`lKh2WC4nbavBcsQhh5%nw-dfq-{Qj$05Ix>BM9LKJt)`#l>nevX91=aw zGy=+6z8_B${9UR#iaW(nmT+I7{MMhsm2$;Qdnl)9T_K;Nr<77kDW#NBN-3q3Qc5YM flu}A5rP=sD97J~dw_WjE00000NkvXXu0mjfMz{}C literal 0 HcmV?d00001 diff --git a/android/src/main/res/drawable-xxxhdpi/ic_favorite.png b/android/src/main/res/drawable-xxxhdpi/ic_favorite.png new file mode 100644 index 0000000000000000000000000000000000000000..b635760be3c9607c1232e27c890d23c445edce1b GIT binary patch literal 2424 zcmai$`#%$k8^?7xNJgR@*R&{?gTdEJ9+{1q{VMV@KtfSMNJt1^`-c_ku&e)# zxacA8ULm~_5;}^owX$%IE?Jw4sVlWUDRid*rFrXku8Na*IaNzD3HVF~3?$ZNXlAIX zr`fssi1^rNDrpm+*c?}Rm z7oMrNa&Q}iTuuOmFAW{;lw{6}JX6z1b(%p@3zdgUyf<6@xcad+Nhr zo|A-KvJPg5NUNKFdGLS;dH}tny8G_FSFVjL0*cL+nh5`GWdW@lAOh+);f}eIj)iy} z5oD~tJuf?B7k3HvVBMF9Y-!q|MJ*+=tC|CCHpFlrdUnYK;up~`t|>HjP%^TqrHRJ; zVGKF#h|L~?NOI(GNnZ-eUotZKnUrW?y#mM3C2pw$pcCoKRgz|S-=jbjkjk0q(Wkm; zQ2mIckaz+=2XYI99Zk+kd)ZbY`4N}#0)ng?p24`{SjxtUySzk1#`XP&ibq{XH!-Xs{H3IErNc8Dy|J-G>wtl4*?6k3m*)QStn(q|r|S&xxhL5<(- z9AQb8`WjH)0HBuW+u5w_T|hDcQu!vs+3jJn*vb4sA-y%jOkXM|)V%>lM)DfQ@!tb1 zdmH~4tGO;fn*pKwo)4eE^+tK5hf6o=3RF^kdz;y-<_h-V8}nRZV4oIfu) zX>aiUy%`9%W9eVw%5%9DgQcjye8utphvT#@=-9o-u=S}&+8=;~z|cQaA7M;~QF3ly zCAj6(Wd>1&tfeu~FL|?g+u!2!>sFcJ}G4 z7RgkxMSAb1Q&x+d4>k5mr2A;9gSFN=>N5scs#5qZy(M9l>K@bM|JS4IdSrauHIMI_ z2TU;!Q|QHI*`_DR8~FceG%Gm{W)E7YUA7Y=Nd+j(5T;F_s`6*%r#f_Bhue{5DCQr* zhl|9*bmxOTDHZoVwni>SJ^g|`%;5b1y~Yj;BS`zx7oIehSN;=rHr{oAODrfhO-Qe1E;UR>)z#b)jAxjprEx!ht?S5-ci9@ zMFOjIv9A5H*%u^G*;M6FL}^m#_2<`S034{R4z$cn>iwOJ=wHiUF~xb~l>%OAO4i1) zydS8K{-Ve?6I{ruBE_VQxThLY!Dz6Fl-v=ybZ&Rd4k{VUwr&lT0j;d8?xU232YOfr z@59R#2c;*puH}#PZF601&(^R#@?hfrI2TR?E9{|7$L4EdbWt6cF~@$=rr_TJ@;*IV zY^LmQ0SfbpbO&d=)hT%l)7LOwWKLF2WfYs2Og*hh0?!yt>qAu)kLs`+gL1%(5z!bY zuxh#4Age~)tsb5k70B7i^6VSnXMH@omMjxCnjrVwFDE`2trM|AmS6g;<7$K-zI7S) zEk2&d-|4*VJIAGj>f&nW(W?c=D4x5&wW!X&Fy4Z$0qgSN&G8m~h&95!n&kI>BYmW4 z2;NN!I;B3n*H5sJK1&HL4Q`^mbx}%J zga0v7$%z1X=D|aSt(`BJ{*qJcJ4TUQ(8d+PJ%w#&n-E8GNGTQ|05Ow)^SFPnozKE{ zKoA7X)c9)~BT$pA6~H&mL+!x>$*aYUZnR4)yr6S%uVUiRE=Zs@d-)v^mT>UvS`3;i#m$|&O-i*YDrE*Fu)i5i zYqfXV{gTIz1M`?!J^hM3_0BfqXxYmZVw~NHJ8_g4;Mu@@?!Fd4%?`oKKwxdYRAV;) z5qI5ArL0+*Zq@0iH}=1dowU^^riR!MUQJaYLA24{Ay)iVMP$h*uezvp2s|x+yHD_y zWMY*y-gx(5VkI)q&!fqdK|_bwPjx|7pE7U^Q}rNCr@6oc5lye(X5Q zEBZVfBXMJBU%lr7Z{OD&?!I5(p7vmk( zV>-~{Q|TyJ6WwhqO}BcdA8*Ob8ZB}0366-W1}DFLhp?2(sAhZox1=0;%(%jV@Ve6c V;{7?#{X?S?vb9E9k$*=g{R1%*%ai~B literal 0 HcmV?d00001 diff --git a/ios/Classes/AudioServicePlugin.m b/ios/Classes/AudioServicePlugin.m deleted file mode 100644 index 48e8463..0000000 --- a/ios/Classes/AudioServicePlugin.m +++ /dev/null @@ -1,617 +0,0 @@ -#import "AudioServicePlugin.h" -#import -#import - -// 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*)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 *)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 diff --git a/ios/Classes/AudioServicePlugin.m b/ios/Classes/AudioServicePlugin.m new file mode 120000 index 0000000..ce7a5fe --- /dev/null +++ b/ios/Classes/AudioServicePlugin.m @@ -0,0 +1 @@ +../../darwin/Classes/AudioServicePlugin.m \ No newline at end of file diff --git a/macos/Classes/AudioServicePlugin.m b/macos/Classes/AudioServicePlugin.m deleted file mode 100644 index 48e8463..0000000 --- a/macos/Classes/AudioServicePlugin.m +++ /dev/null @@ -1,617 +0,0 @@ -#import "AudioServicePlugin.h" -#import -#import - -// 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*)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 *)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 diff --git a/macos/Classes/AudioServicePlugin.m b/macos/Classes/AudioServicePlugin.m new file mode 120000 index 0000000..ce7a5fe --- /dev/null +++ b/macos/Classes/AudioServicePlugin.m @@ -0,0 +1 @@ +../../darwin/Classes/AudioServicePlugin.m \ No newline at end of file