Skip to content

Commit

Permalink
Merge branch 'fix/early_method_calls' into minor
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanheise committed Dec 7, 2021
2 parents 384a159 + e7378ab commit 8d33625
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 75 deletions.
1 change: 1 addition & 0 deletions audio_service/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* Guard against NPE when Android service is destroyed quickly.
* Migrate to flutter_lints.
* Queue messages from platform if init() called late.

## 0.18.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ public void onDestroy() {
notificationCreated = false;
}

public AudioServiceConfig getConfig() {
return config;
}

public void configure(AudioServiceConfig config) {
this.config = config;
notificationChannelId = (config.androidNotificationChannelId != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -93,6 +94,7 @@ public static void disposeFlutterEngine() {
private static AudioHandlerInterface audioHandlerInterface;
private static final long bootTime;
private static Result configureResult;
private static boolean flutterReady;

static {
bootTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
Expand Down Expand Up @@ -410,6 +412,7 @@ public void onMethodCall(MethodCall call, final Result result) {
if (serviceConnectionFailed) {
throw new IllegalStateException("Unable to bind to AudioService. Please ensure you have declared a <service> element as described in the README.");
}
flutterReady = true;
Map<?, ?> args = (Map<?, ?>)call.arguments;
Map<?, ?> configMap = (Map<?, ?>)args.get("config");
AudioServiceConfig config = new AudioServiceConfig(context.getApplicationContext());
Expand Down Expand Up @@ -437,10 +440,13 @@ public void onMethodCall(MethodCall call, final Result result) {
if (audioHandlerInterface == null) {
audioHandlerInterface = new AudioHandlerInterface(messenger);
AudioService.init(audioHandlerInterface);
} else if (audioHandlerInterface.messenger != messenger) {
// We've detected this is the real engine hosting the AudioHandler,
// so update AudioHandlerInterface to connect to it.
audioHandlerInterface.switchToMessenger(messenger);
} else {
if (audioHandlerInterface.messenger != messenger) {
// We've detected this is the real engine hosting the AudioHandler,
// so update AudioHandlerInterface to connect to it.
audioHandlerInterface.switchToMessenger(messenger);
}
audioHandlerInterface.invokePendingMethods();
}
if (mediaController != null) {
result.success(mapOf());
Expand All @@ -462,6 +468,7 @@ private static class AudioHandlerInterface implements MethodCallHandler, AudioSe
public MethodChannel channel;
private AudioTrack silenceAudioTrack;
private final Handler handler = new Handler(Looper.getMainLooper());
private List<MethodInvocation> methodInvocationQueue = new LinkedList<MethodInvocation>();

public AudioHandlerInterface(BinaryMessenger messenger) {
this.messenger = messenger;
Expand All @@ -476,6 +483,13 @@ public void switchToMessenger(BinaryMessenger messenger) {
channel.setMethodCallHandler(this);
}

public void invokePendingMethods() {
for (MethodInvocation mi : methodInvocationQueue) {
channel.invokeMethod(mi.method, mi.arg, mi.result);
}
methodInvocationQueue.clear();
}

@Override
public void onLoadChildren(final String parentMediaId, final MediaBrowserServiceCompat.Result<List<MediaBrowserCompat.MediaItem>> result, Bundle options) {
if (audioHandlerInterface != null) {
Expand Down Expand Up @@ -927,12 +941,16 @@ public void onMethodCall(MethodCall call, Result result) {

@UiThread
public void invokeMethod(String method, Object arg) {
channel.invokeMethod(method, arg);
invokeMethod(method, arg, null);
}

@UiThread
public void invokeMethod(String method, Object arg, final Result result) {
channel.invokeMethod(method, arg, result);
if (flutterReady) {
channel.invokeMethod(method, arg, result);
} else {
methodInvocationQueue.add(new MethodInvocation(method, arg, result));
}
}

private void destroy() {
Expand Down Expand Up @@ -1126,4 +1144,16 @@ static Map<String, Object> mapOf(Object... args) {
}
return map;
}

static class MethodInvocation {
public final String method;
public final Object arg;
public final Result result;

public MethodInvocation(String method, Object arg, Result result) {
this.method = method;
this.arg = arg;
this.result = result;
}
}
}
157 changes: 88 additions & 69 deletions audio_service/lib/audio_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -900,12 +900,14 @@ class AudioService {
assert(config.rewindInterval > Duration.zero);
WidgetsFlutterBinding.ensureInitialized();
_cacheManager = (cacheManager ??= DefaultCacheManager());
final callbacks = _HandlerCallbacks();
_platform.setHandlerCallbacks(callbacks);
await _platform.configure(ConfigureRequest(config: config._toMessage()));
_config = config;
final handler = builder();
_handler = handler;
callbacks.setHandler(handler);

_platform.setHandlerCallbacks(_HandlerCallbacks(handler));
_observeMediaItem();
_observeAndroidPlaybackInfo();
_observeQueue();
Expand Down Expand Up @@ -3680,34 +3682,39 @@ class AudioServiceBackground {
}

class _HandlerCallbacks extends AudioHandlerCallbacks {
final AudioHandler handler;
final _handlerCompleter = Completer<AudioHandler>();

Future<AudioHandler> get handlerFuture => _handlerCompleter.future;

_HandlerCallbacks(this.handler);
void setHandler(AudioHandler handler) => _handlerCompleter.complete(handler);

@override
Future<void> addQueueItem(AddQueueItemRequest request) =>
handler.addQueueItem(request.mediaItem.toPlugin());
Future<void> addQueueItem(AddQueueItemRequest request) async =>
(await handlerFuture).addQueueItem(request.mediaItem.toPlugin());

@override
Future<void> androidAdjustRemoteVolume(
AndroidAdjustRemoteVolumeRequest request) =>
handler.androidAdjustRemoteVolume(request.direction.toPlugin());
AndroidAdjustRemoteVolumeRequest request) async =>
(await handlerFuture)
.androidAdjustRemoteVolume(request.direction.toPlugin());

@override
Future<void> androidSetRemoteVolume(AndroidSetRemoteVolumeRequest request) =>
handler.androidSetRemoteVolume(request.volumeIndex);
Future<void> androidSetRemoteVolume(
AndroidSetRemoteVolumeRequest request) async =>
(await handlerFuture).androidSetRemoteVolume(request.volumeIndex);

@override
Future<void> click(ClickRequest request) {
return handler.click(request.button.toPlugin());
Future<void> click(ClickRequest request) async {
return (await handlerFuture).click(request.button.toPlugin());
}

@override
Future customAction(CustomActionRequest request) =>
handler.customAction(request.name, request.extras);
Future customAction(CustomActionRequest request) async =>
(await handlerFuture).customAction(request.name, request.extras);

@override
Future<void> fastForward(FastForwardRequest request) => handler.fastForward();
Future<void> fastForward(FastForwardRequest request) async =>
(await handlerFuture).fastForward();

@override
Future<GetChildrenResponse> getChildren(GetChildrenRequest request) async {
Expand All @@ -3720,12 +3727,14 @@ class _HandlerCallbacks extends AudioHandlerCallbacks {
@override
Future<GetMediaItemResponse> getMediaItem(GetMediaItemRequest request) async {
return GetMediaItemResponse(
mediaItem: (await handler.getMediaItem(request.mediaId))?._toMessage());
mediaItem: (await (await handlerFuture).getMediaItem(request.mediaId))
?._toMessage());
}

@override
Future<void> insertQueueItem(InsertQueueItemRequest request) =>
handler.insertQueueItem(request.index, request.mediaItem.toPlugin());
Future<void> insertQueueItem(InsertQueueItemRequest request) async =>
(await handlerFuture)
.insertQueueItem(request.index, request.mediaItem.toPlugin());

@override
Future<void> onNotificationClicked(
Expand All @@ -3734,112 +3743,122 @@ class _HandlerCallbacks extends AudioHandlerCallbacks {
}

@override
Future<void> onNotificationDeleted(OnNotificationDeletedRequest request) =>
handler.onNotificationDeleted();
Future<void> onNotificationDeleted(
OnNotificationDeletedRequest request) async =>
(await handlerFuture).onNotificationDeleted();

@override
Future<void> onTaskRemoved(OnTaskRemovedRequest request) =>
handler.onTaskRemoved();
Future<void> onTaskRemoved(OnTaskRemovedRequest request) async =>
(await handlerFuture).onTaskRemoved();

@override
Future<void> pause(PauseRequest request) => handler.pause();
Future<void> pause(PauseRequest request) async =>
(await handlerFuture).pause();

@override
Future<void> play(PlayRequest request) => handler.play();
Future<void> play(PlayRequest request) async => (await handlerFuture).play();

@override
Future<void> playFromMediaId(PlayFromMediaIdRequest request) =>
handler.playFromMediaId(request.mediaId);
Future<void> playFromMediaId(PlayFromMediaIdRequest request) async =>
(await handlerFuture).playFromMediaId(request.mediaId);

@override
Future<void> playFromSearch(PlayFromSearchRequest request) =>
handler.playFromSearch(request.query);
Future<void> playFromSearch(PlayFromSearchRequest request) async =>
(await handlerFuture).playFromSearch(request.query);

@override
Future<void> playFromUri(PlayFromUriRequest request) =>
handler.playFromUri(request.uri);
Future<void> playFromUri(PlayFromUriRequest request) async =>
(await handlerFuture).playFromUri(request.uri);

@override
Future<void> playMediaItem(PlayMediaItemRequest request) =>
handler.playMediaItem(request.mediaItem.toPlugin());
Future<void> playMediaItem(PlayMediaItemRequest request) async =>
(await handlerFuture).playMediaItem(request.mediaItem.toPlugin());

@override
Future<void> prepare(PrepareRequest request) => handler.prepare();
Future<void> prepare(PrepareRequest request) async =>
(await handlerFuture).prepare();

@override
Future<void> prepareFromMediaId(PrepareFromMediaIdRequest request) =>
handler.prepareFromMediaId(request.mediaId);
Future<void> prepareFromMediaId(PrepareFromMediaIdRequest request) async =>
(await handlerFuture).prepareFromMediaId(request.mediaId);

@override
Future<void> prepareFromSearch(PrepareFromSearchRequest request) =>
handler.prepareFromSearch(request.query);
Future<void> prepareFromSearch(PrepareFromSearchRequest request) async =>
(await handlerFuture).prepareFromSearch(request.query);

@override
Future<void> prepareFromUri(PrepareFromUriRequest request) =>
handler.prepareFromUri(request.uri);
Future<void> prepareFromUri(PrepareFromUriRequest request) async =>
(await handlerFuture).prepareFromUri(request.uri);

@override
Future<void> removeQueueItem(RemoveQueueItemRequest request) =>
handler.removeQueueItem(request.mediaItem.toPlugin());
Future<void> removeQueueItem(RemoveQueueItemRequest request) async =>
(await handlerFuture).removeQueueItem(request.mediaItem.toPlugin());

@override
Future<void> removeQueueItemAt(RemoveQueueItemAtRequest request) =>
handler.removeQueueItemAt(request.index);
Future<void> removeQueueItemAt(RemoveQueueItemAtRequest request) async =>
(await handlerFuture).removeQueueItemAt(request.index);

@override
Future<void> rewind(RewindRequest request) => handler.rewind();
Future<void> rewind(RewindRequest request) async =>
(await handlerFuture).rewind();

@override
Future<SearchResponse> search(SearchRequest request) async => SearchResponse(
mediaItems: (await handler.search(request.query, request.extras))
.map((item) => item._toMessage())
.toList());
mediaItems:
(await (await handlerFuture).search(request.query, request.extras))
.map((item) => item._toMessage())
.toList());

@override
Future<void> seek(SeekRequest request) => handler.seek(request.position);
Future<void> seek(SeekRequest request) async =>
(await handlerFuture).seek(request.position);

@override
Future<void> seekBackward(SeekBackwardRequest request) =>
handler.seekBackward(request.begin);
Future<void> seekBackward(SeekBackwardRequest request) async =>
(await handlerFuture).seekBackward(request.begin);

@override
Future<void> seekForward(SeekForwardRequest request) =>
handler.seekForward(request.begin);
Future<void> seekForward(SeekForwardRequest request) async =>
(await handlerFuture).seekForward(request.begin);

@override
Future<void> setCaptioningEnabled(SetCaptioningEnabledRequest request) =>
handler.setCaptioningEnabled(request.enabled);
Future<void> setCaptioningEnabled(
SetCaptioningEnabledRequest request) async =>
(await handlerFuture).setCaptioningEnabled(request.enabled);

@override
Future<void> setRating(SetRatingRequest request) =>
handler.setRating(request.rating.toPlugin(), request.extras);
Future<void> setRating(SetRatingRequest request) async =>
(await handlerFuture)
.setRating(request.rating.toPlugin(), request.extras);

@override
Future<void> setRepeatMode(SetRepeatModeRequest request) => handler
.setRepeatMode(AudioServiceRepeatMode.values[request.repeatMode.index]);
Future<void> setRepeatMode(SetRepeatModeRequest request) async =>
(await handlerFuture).setRepeatMode(
AudioServiceRepeatMode.values[request.repeatMode.index]);

@override
Future<void> setShuffleMode(SetShuffleModeRequest request) =>
handler.setShuffleMode(
Future<void> setShuffleMode(SetShuffleModeRequest request) async =>
(await handlerFuture).setShuffleMode(
AudioServiceShuffleMode.values[request.shuffleMode.index]);

@override
Future<void> setSpeed(SetSpeedRequest request) =>
handler.setSpeed(request.speed);
Future<void> setSpeed(SetSpeedRequest request) async =>
(await handlerFuture).setSpeed(request.speed);

@override
Future<void> skipToNext(SkipToNextRequest request) => handler.skipToNext();
Future<void> skipToNext(SkipToNextRequest request) async =>
(await handlerFuture).skipToNext();

@override
Future<void> skipToPrevious(SkipToPreviousRequest request) =>
handler.skipToPrevious();
Future<void> skipToPrevious(SkipToPreviousRequest request) async =>
(await handlerFuture).skipToPrevious();

@override
Future<void> skipToQueueItem(SkipToQueueItemRequest request) =>
handler.skipToQueueItem(request.index);
Future<void> skipToQueueItem(SkipToQueueItemRequest request) async =>
(await handlerFuture).skipToQueueItem(request.index);

@override
Future<void> stop(StopRequest request) => handler.stop();
Future<void> stop(StopRequest request) async => (await handlerFuture).stop();

final Map<String, ValueStream<Map<String, dynamic>>> _childrenSubscriptions =
{};
Expand All @@ -3849,7 +3868,7 @@ class _HandlerCallbacks extends AudioHandlerCallbacks {
var childrenSubscription = _childrenSubscriptions[parentMediaId];
if (childrenSubscription == null) {
childrenSubscription = _childrenSubscriptions[parentMediaId] =
handler.subscribeToChildren(parentMediaId);
(await handlerFuture).subscribeToChildren(parentMediaId);
childrenSubscription.listen((Map<String, dynamic>? options) {
// Notify clients that the children of [parentMediaId] have changed.
_platform.notifyChildrenChanged(NotifyChildrenChangedRequest(
Expand All @@ -3858,7 +3877,7 @@ class _HandlerCallbacks extends AudioHandlerCallbacks {
));
});
}
return await handler.getChildren(parentMediaId, options);
return await (await handlerFuture).getChildren(parentMediaId, options);
}
}

Expand Down

0 comments on commit 8d33625

Please sign in to comment.