Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I can'tget songs from storage using other lib #167

Closed
riskikukuh opened this issue Feb 3, 2020 · 11 comments
Closed

I can'tget songs from storage using other lib #167

riskikukuh opened this issue Feb 3, 2020 · 11 comments

Comments

@riskikukuh
Copy link

Describe the bug
I want to use other lib for fetch music from storage, i use flutter_audio_query and fetch same like the documentation. Why i get this error ? but if i remove lib audio_service application work well.

Error messages

E/flutter (26935): #0      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:319:7)
E/flutter (26935): <asynchronous suspension>
E/flutter (26935): #1      FlutterAudioQuery.getSongs (package:flutter_audio_query/src/flutter_audio_query.dart:179:44)
E/flutter (26935): #2      _HomepageState.initData (package:audio_service_example/homepage.dart:23:30)
E/flutter (26935): #3      _HomepageState.initState (package:audio_service_example/homepage.dart:29:5)
E/flutter (26935): #4      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4355:58)
E/flutter (26935): #5      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4201:5)
E/flutter (26935): #6      Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #7      Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #8      SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5445:14)
E/flutter (26935): #9      Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #10     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #11     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4243:16)
E/flutter (26935): #12     Element.rebuild (package:flutter/src/widgets/framework.dart:3947:5)
E/flutter (26935): #13     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4206:5)
E/flutter (26935): #14     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4201:5)
E/flutter (26935): #15     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #16     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #17     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5445:14)
E/flutter (26935): #18     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #19     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #20     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5445:14)
E/flutter (26935): #21     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #22     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #23     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4243:16)
E/flutter (26935): #24     Element.rebuild (package:flutter/src/widgets/framework.dart:3947:5)
E/flutter (26935): #25     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4206:5)
E/flutter (26935): #26     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4381:11)
E/flutter (26935): #27     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4201:5)
E/flutter (26935): #28     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #29     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #30     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5445:14)
E/flutter (26935): #31     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #32     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #33     SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:5445:14)
E/flutter (26935): #34     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #35     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #36     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4243:16)
E/flutter (26935): #37     Element.rebuild (package:flutter/src/widgets/framework.dart:3947:5)
E/flutter (26935): #38     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4206:5)
E/flutter (26935): #39     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4381:11)
E/flutter (26935): #40     ComponentElement.mount (package:flutter/src/widgets/framework.dart:4201:5)
E/flutter (26935): #41     Element.inflateWidget (package:flutter/src/widgets/framework.dart:3194:14)
E/flutter (26935): #42     Element.updateChild (package:flutter/src/widgets/framework.dart:2988:12)
E/flutter (26935): #43     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4243:16)
E/flutter (26935): #44     Element.rebuild (package:flutter/src/widge

Flutter SDK version

[✓] Flutter (Channel stable, v1.12.13+hotfix.7, on Linux, locale
    en_US.UTF-8)
    • Flutter version 1.12.13+hotfix.7 at
      /home/zeism/Development/Software/flutter
    • Framework revision 9f5ff2306b (7 days ago), 2020-01-26
      22:38:26 -0800
    • Engine revision a67792536c
    • Dart version 2.7.0

 
[✓] Android toolchain - develop for Android devices (Android SDK 
    version 29.0.2)
    • Android SDK at /home/zeism/Android/Sdk/
    • Android NDK location not configured (optional; useful for
      native profiling support)
    • Platform android-29, build-tools 29.0.2
    • ANDROID_HOME = /home/zeism/Android/Sdk/
    • Java binary at:
      /home/zeism/Development/Android/android-studio/jre/bin/java
    • Java version openjdk version "1.8.0_202-release"
    • All Android licenses accepted.

[✓] Android Studio (version 3.5)
    • Android Studio at
      /home/zeism/Development/Android/android-studio
    • Flutter plugin version 41.0.2
    • Dart plugin version 191.8593
    • Java version openjdk version "1.8.0_202-release"

[✓] IntelliJ IDEA Community Edition (version 2019.2)
    • IntelliJ at /opt/idea
    • Flutter plugin version 38.2.4
    • Dart plugin version 192.6262.13

[✓] VS Code (version 1.41.1)
    • VS Code at /usr/share/code
    • Flutter extension version 3.8.0

[✓] Connected device (1 available)
    • Redmi 3S • 34b971177d63 • android-arm64 • Android 9 (API 28)

• No issues found!
@ryanheise
Copy link
Owner

I think you may have cut off the top part of the stack trace which is important.

@ryanheise ryanheise self-assigned this Feb 3, 2020
@riskikukuh
Copy link
Author

Ohh sorry i forget that

E/flutter (30593): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method getSongs on channel boaventura.com.devel.br.flutteraudioquery)
Syncing files to device Redmi 3S...                                 
E/flutter (30593): #0      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:319:7)

@ryanheise
Copy link
Owner

ryanheise commented Feb 3, 2020

What does your main activity class look like? And also if you have a custom application class (which you might not), it would be helpful to also copy and paste that here.

Edit: and also did you migrate your project to Flutter 1.12 (including manifest changes)?

@riskikukuh
Copy link
Author

FYI, i use example project from your lib.

MainActivity

package com.ryanheise.audioserviceexample;

import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
import android.content.Context;
import io.flutter.embedding.android.FlutterActivity;

public class MainActivity extends FlutterActivity {
	/** This is a temporary workaround to avoid a memory leak in the Flutter framework */
	@Override
	public FlutterEngine provideFlutterEngine(Context context) {
		// Instantiate a FlutterEngine.
		FlutterEngine flutterEngine = new FlutterEngine(context.getApplicationContext());

		// Start executing Dart code to pre-warm the FlutterEngine.
		flutterEngine.getDartExecutor().executeDartEntrypoint(
			DartExecutor.DartEntrypoint.createDefault()
		);

		return flutterEngine;
	}
}

AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ryanheise.audioserviceexample">

    <!-- The INTERNET permission is required for development. Specifically,
         flutter needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:label="Audio Service Demo"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name="com.ryanheise.audioservice.AudioService">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>

        <receiver android:name="androidx.media.session.MediaButtonReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver> 

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

    </application>
</manifest>

what do you mean about including manifest changes ? sorry, my english it's so bad. Yesterday , i was upgrade flutter just using flutter upgrade

@ryanheise
Copy link
Owner

Can you provide the other parts of the bug report?

Minimal reproduction project
If the example project exhibits the bug, please mention that here, otherwise fork this project and modify the example to reproduce the bug. Provide the link to your repository here.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Runtime Environment (please complete the following information if relevant):

  • Device: [e.g. Samsung Galaxy Note 8]
  • Android version: [e.g. 8.0.0]
  • iOS version: [e.g. 13.3]

Additional context
Add any other context about the problem here.

@riskikukuh
Copy link
Author

Minimal reproduction project
Idk this is bug or not, but i assume this is a bug, i just add lib flutter_audio_query and then call getSongs function, and the error appears.

To Reproduce
Steps to reproduce the behavior:

  1. clone your repository
  2. i just try example and no error found
  3. i try to add package for get songs from storage, i use flutter_audio_query
  4. i add function for get song from storage in initState
  5. Run app, initState called, error appear.

Expected behavior
Idk, what is going on, but from the error, package flutter_audio_query not registered in app, but if i remove your package audio_service function getSongs called and no error found.

Runtime Environment (please complete the following information if relevant):

  • Device: Xiaomi Redmi 3s Custom ROM HAVOC-OS
  • Android version: 9.0

@ryanheise
Copy link
Owner

I'm sorry but to investigate this, I'll need an actual project that I can run. You can create a fork of this repository and make the changes and then share the URL. This will make it very easy for me to clone your repo and run it.

@riskikukuh
Copy link
Author

If you follow that steps for reproduce the behavior and copy this main.dart and add flutter_audio_query in pubspec.yaml, i am using your example.

main.dart

import 'dart:math';

import 'package:flutter/material.dart';
import 'dart:async';

import 'package:audio_service/audio_service.dart';
import 'package:flutter_audio_query/flutter_audio_query.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:just_audio/just_audio.dart';
import 'package:rxdart/rxdart.dart';

MediaControl playControl = MediaControl(
  androidIcon: 'drawable/ic_action_play_arrow',
  label: 'Play',
  action: MediaAction.play,
);
MediaControl pauseControl = MediaControl(
  androidIcon: 'drawable/ic_action_pause',
  label: 'Pause',
  action: MediaAction.pause,
);
MediaControl skipToNextControl = MediaControl(
  androidIcon: 'drawable/ic_action_skip_next',
  label: 'Next',
  action: MediaAction.skipToNext,
);
MediaControl skipToPreviousControl = MediaControl(
  androidIcon: 'drawable/ic_action_skip_previous',
  label: 'Previous',
  action: MediaAction.skipToPrevious,
);
MediaControl stopControl = MediaControl(
  androidIcon: 'drawable/ic_action_stop',
  label: 'Stop',
  action: MediaAction.stop,
);

void main() => runApp(new MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  final BehaviorSubject<double> _dragPositionSubject =
      BehaviorSubject.seeded(null);

  List<SongInfo> data = [];
  FlutterAudioQuery _audioQuery = FlutterAudioQuery();

  @override
  void initState() {
    super.initState();
    _getMusic();
    WidgetsBinding.instance.addObserver(this);
    connect();
  }

  @override
  void dispose() {
    disconnect();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.resumed:
        connect();
        break;
      case AppLifecycleState.paused:
        disconnect();
        break;
      default:
        break;
    }
  }

  void _getMusic() async {
    data = await _audioQuery.getSongs();
  }

  void connect() async {
    await AudioService.connect();
  }

  void disconnect() {
    AudioService.disconnect();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: WillPopScope(
        onWillPop: () {
          disconnect();
          return Future.value(true);
        },
        child: new Scaffold(
          appBar: new AppBar(
            title: const Text('Audio Service Demo'),
          ),
          body: new Center(
            child: StreamBuilder<ScreenState>(
              stream: Rx.combineLatest3<List<MediaItem>, MediaItem,
                      PlaybackState, ScreenState>(
                  AudioService.queueStream,
                  AudioService.currentMediaItemStream,
                  AudioService.playbackStateStream,
                  (queue, mediaItem, playbackState) =>
                      ScreenState(queue, mediaItem, playbackState)),
              builder: (context, snapshot) {
                final screenState = snapshot.data;
                final queue = screenState?.queue;
                final mediaItem = screenState?.mediaItem;
                final state = screenState?.playbackState;
                final basicState = state?.basicState ?? BasicPlaybackState.none;
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    if (queue != null && queue.isNotEmpty)
                      Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          IconButton(
                            icon: Icon(Icons.skip_previous),
                            iconSize: 64.0,
                            onPressed: mediaItem == queue.first
                                ? null
                                : AudioService.skipToPrevious,
                          ),
                          IconButton(
                            icon: Icon(Icons.skip_next),
                            iconSize: 64.0,
                            onPressed: mediaItem == queue.last
                                ? null
                                : AudioService.skipToNext,
                          ),
                        ],
                      ),
                    if (mediaItem?.title != null) Text(mediaItem.title),
                    if (basicState == BasicPlaybackState.none) ...[
                      audioPlayerButton(),
                      textToSpeechButton(),
                    ] else
                      Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          if (basicState == BasicPlaybackState.playing)
                            pauseButton()
                          else if (basicState == BasicPlaybackState.paused)
                            playButton()
                          else if (basicState == BasicPlaybackState.buffering ||
                              basicState == BasicPlaybackState.skippingToNext ||
                              basicState ==
                                  BasicPlaybackState.skippingToPrevious)
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: SizedBox(
                                width: 64.0,
                                height: 64.0,
                                child: CircularProgressIndicator(),
                              ),
                            ),
                          stopButton(),
                        ],
                      ),
                    if (basicState != BasicPlaybackState.none &&
                        basicState != BasicPlaybackState.stopped) ...[
                      positionIndicator(mediaItem, state),
                      Text("State: " +
                          "$basicState".replaceAll(RegExp(r'^.*\.'), '')),
                    ]
                  ],
                );
              },
            ),
          ),
        ),
      ),
    );
  }

  RaisedButton audioPlayerButton() => startButton(
        'AudioPlayer',
        () {
          AudioService.start(
            backgroundTaskEntrypoint: _audioPlayerTaskEntrypoint,
            androidNotificationChannelName: 'Audio Service Demo',
            notificationColor: 0xFF2196f3,
            androidNotificationIcon: 'mipmap/ic_launcher',
            enableQueue: true,
          );
        },
      );

  RaisedButton textToSpeechButton() => startButton(
        'TextToSpeech',
        () {
          AudioService.start(
            backgroundTaskEntrypoint: _textToSpeechTaskEntrypoint,
            androidNotificationChannelName: 'Audio Service Demo',
            notificationColor: 0xFF2196f3,
            androidNotificationIcon: 'mipmap/ic_launcher',
          );
        },
      );

  RaisedButton startButton(String label, VoidCallback onPressed) =>
      RaisedButton(
        child: Text(label),
        onPressed: onPressed,
      );

  IconButton playButton() => IconButton(
        icon: Icon(Icons.play_arrow),
        iconSize: 64.0,
        onPressed: AudioService.play,
      );

  IconButton pauseButton() => IconButton(
        icon: Icon(Icons.pause),
        iconSize: 64.0,
        onPressed: AudioService.pause,
      );

  IconButton stopButton() => IconButton(
        icon: Icon(Icons.stop),
        iconSize: 64.0,
        onPressed: AudioService.stop,
      );

  Widget positionIndicator(MediaItem mediaItem, PlaybackState state) {
    double seekPos;
    return StreamBuilder(
      stream: Rx.combineLatest2<double, double, double>(
          _dragPositionSubject.stream,
          Stream.periodic(Duration(milliseconds: 200)),
          (dragPosition, _) => dragPosition),
      builder: (context, snapshot) {
        double position = snapshot.data ?? state.currentPosition.toDouble();
        double duration = mediaItem?.duration?.toDouble();
        return Column(
          children: [
            if (duration != null)
              Slider(
                min: 0.0,
                max: duration,
                value: seekPos ?? max(0.0, min(position, duration)),
                onChanged: (value) {
                  _dragPositionSubject.add(value);
                },
                onChangeEnd: (value) {
                  AudioService.seekTo(value.toInt());
                  // Due to a delay in platform channel communication, there is
                  // a brief moment after releasing the Slider thumb before the
                  // new position is broadcast from the platform side. This
                  // hack is to hold onto seekPos until the next state update
                  // comes through.
                  // TODO: Improve this code.
                  seekPos = value;
                  _dragPositionSubject.add(null);
                },
              ),
            Text("${(state.currentPosition / 1000).toStringAsFixed(3)}"),
          ],
        );
      },
    );
  }
}

class ScreenState {
  final List<MediaItem> queue;
  final MediaItem mediaItem;
  final PlaybackState playbackState;

  ScreenState(this.queue, this.mediaItem, this.playbackState);
}

void _audioPlayerTaskEntrypoint() async {
  AudioServiceBackground.run(() => AudioPlayerTask());
}

class AudioPlayerTask extends BackgroundAudioTask {
  final _queue = <MediaItem>[
    MediaItem(
      id: "https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3",
      album: "Science Friday",
      title: "A Salute To Head-Scratching Science",
      artist: "Science Friday and WNYC Studios",
      duration: 5739820,
      artUri:
          "https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg",
    ),
    MediaItem(
      id: "https://s3.amazonaws.com/scifri-segments/scifri201711241.mp3",
      album: "Science Friday",
      title: "From Cat Rheology To Operatic Incompetence",
      artist: "Science Friday and WNYC Studios",
      duration: 2856950,
      artUri:
          "https://media.wnyc.org/i/1400/1400/l/80/1/ScienceFriday_WNYCStudios_1400.jpg",
    ),
  ];
  int _queueIndex = -1;
  AudioPlayer _audioPlayer = new AudioPlayer();
  Completer _completer = Completer();
  BasicPlaybackState _skipState;
  bool _playing;

  bool get hasNext => _queueIndex + 1 < _queue.length;

  bool get hasPrevious => _queueIndex > 0;

  MediaItem get mediaItem => _queue[_queueIndex];

  BasicPlaybackState _stateToBasicState(AudioPlaybackState state) {
    switch (state) {
      case AudioPlaybackState.none:
        return BasicPlaybackState.none;
      case AudioPlaybackState.stopped:
        return BasicPlaybackState.stopped;
      case AudioPlaybackState.paused:
        return BasicPlaybackState.paused;
      case AudioPlaybackState.playing:
        return BasicPlaybackState.playing;
      case AudioPlaybackState.buffering:
        return BasicPlaybackState.buffering;
      case AudioPlaybackState.connecting:
        return _skipState ?? BasicPlaybackState.connecting;
      case AudioPlaybackState.completed:
        return BasicPlaybackState.stopped;
      default:
        throw Exception("Illegal state");
    }
  }

  @override
  Future<void> onStart() async {
    var playerStateSubscription = _audioPlayer.playbackStateStream
        .where((state) => state == AudioPlaybackState.completed)
        .listen((state) {
      _handlePlaybackCompleted();
    });
    var eventSubscription = _audioPlayer.playbackEventStream.listen((event) {
      final state = _stateToBasicState(event.state);
      if (state != BasicPlaybackState.stopped) {
        _setState(
          state: state,
          position: event.position.inMilliseconds,
        );
      }
    });

    AudioServiceBackground.setQueue(_queue);
    await onSkipToNext();
    await _completer.future;
    playerStateSubscription.cancel();
    eventSubscription.cancel();
  }

  void _handlePlaybackCompleted() {
    if (hasNext) {
      onSkipToNext();
    } else {
      onStop();
    }
  }

  void playPause() {
    if (AudioServiceBackground.state.basicState == BasicPlaybackState.playing)
      onPause();
    else
      onPlay();
  }

  @override
  Future<void> onSkipToNext() => _skip(1);

  @override
  Future<void> onSkipToPrevious() => _skip(-1);

  Future<void> _skip(int offset) async {
    final newPos = _queueIndex + offset;
    if (!(newPos >= 0 && newPos < _queue.length)) return;
    if (_playing == null) {
      // First time, we want to start playing
      _playing = true;
    } else if (_playing) {
      // Stop current item
      await _audioPlayer.stop();
    }
    // Load next item
    _queueIndex = newPos;
    AudioServiceBackground.setMediaItem(mediaItem);
    _skipState = offset > 0
        ? BasicPlaybackState.skippingToNext
        : BasicPlaybackState.skippingToPrevious;
    await _audioPlayer.setUrl(mediaItem.id);
    _skipState = null;
    // Resume playback if we were playing
    if (_playing) {
      onPlay();
    } else {
      _setState(state: BasicPlaybackState.paused);
    }
  }

  @override
  void onPlay() {
    if (_skipState == null) {
      _playing = true;
      _audioPlayer.play();
    }
  }

  @override
  void onPause() {
    if (_skipState == null) {
      _playing = false;
      _audioPlayer.pause();
    }
  }

  @override
  void onSeekTo(int position) {
    _audioPlayer.seek(Duration(milliseconds: position));
  }

  @override
  void onClick(MediaButton button) {
    playPause();
  }

  @override
  void onStop() {
    _audioPlayer.stop();
    _setState(state: BasicPlaybackState.stopped);
    _completer.complete();
  }

  void _setState({@required BasicPlaybackState state, int position}) {
    if (position == null) {
      position = _audioPlayer.playbackEvent.position.inMilliseconds;
    }
    AudioServiceBackground.setState(
      controls: getControls(state),
      systemActions: [MediaAction.seekTo],
      basicState: state,
      position: position,
    );
  }

  List<MediaControl> getControls(BasicPlaybackState state) {
    if (_playing) {
      return [
        skipToPreviousControl,
        pauseControl,
        stopControl,
        skipToNextControl
      ];
    } else {
      return [
        skipToPreviousControl,
        playControl,
        stopControl,
        skipToNextControl
      ];
    }
  }
}

void _textToSpeechTaskEntrypoint() async {
  AudioServiceBackground.run(() => TextPlayerTask());
}

class TextPlayerTask extends BackgroundAudioTask {
  FlutterTts _tts = FlutterTts();

  /// Represents the completion of a period of playing or pausing.
  Completer _playPauseCompleter = Completer();

  /// This wraps [_playPauseCompleter.future], replacing [_playPauseCompleter]
  /// if it has already completed.
  Future _playPauseFuture() {
    if (_playPauseCompleter.isCompleted) _playPauseCompleter = Completer();
    return _playPauseCompleter.future;
  }

  BasicPlaybackState get _basicState => AudioServiceBackground.state.basicState;

  @override
  Future<void> onStart() async {
    playPause();
    for (var i = 1; i <= 10 && _basicState != BasicPlaybackState.stopped; i++) {
      AudioServiceBackground.setMediaItem(mediaItem(i));
      AudioServiceBackground.androidForceEnableMediaButtons();
      _tts.speak('$i');
      // Wait for the speech or a pause request.
      await Future.any(
          [Future.delayed(Duration(seconds: 1)), _playPauseFuture()]);
      // If we were just paused...
      if (_playPauseCompleter.isCompleted &&
          _basicState == BasicPlaybackState.paused) {
        // Wait to be unpaused...
        await _playPauseFuture();
      }
    }
    if (_basicState != BasicPlaybackState.stopped) onStop();
  }

  MediaItem mediaItem(int number) => MediaItem(
      id: 'tts_$number',
      album: 'Numbers',
      title: 'Number $number',
      artist: 'Sample Artist');

  void playPause() {
    if (_basicState == BasicPlaybackState.playing) {
      _tts.stop();
      AudioServiceBackground.setState(
        controls: [playControl, stopControl],
        basicState: BasicPlaybackState.paused,
      );
    } else {
      AudioServiceBackground.setState(
        controls: [pauseControl, stopControl],
        basicState: BasicPlaybackState.playing,
      );
    }
    _playPauseCompleter.complete();
  }

  @override
  void onPlay() {
    playPause();
  }

  @override
  void onPause() {
    playPause();
  }

  @override
  void onClick(MediaButton button) {
    playPause();
  }

  @override
  void onStop() {
    if (_basicState == BasicPlaybackState.stopped) return;
    _tts.stop();
    AudioServiceBackground.setState(
      controls: [],
      basicState: BasicPlaybackState.stopped,
    );
    _playPauseCompleter.complete();
  }
}

pubspec.yaml

name: audio_service_example
description: Demonstrates how to use the audio_service plugin.
publish_to: 'none'

environment:
  sdk: '>=2.6.0 <3.0.0'

dependencies:
  flutter:
    sdk: flutter

  flutter_audio_query: ^0.3.3 # add this line
  path_provider: ^1.5.1
  just_audio: ^0.0.6
  flutter_tts: ^0.8.5
  rxdart: ^0.23.1
  audio_service:
    path: ../

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter

# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #  - images/a_dot_burr.jpeg
  #  - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.io/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.io/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.io/custom-fonts/#from-packages

@riskikukuh
Copy link
Author

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ryanheise.audioserviceexample">

    <!-- The INTERNET permission is required for development. Specifically,
         flutter needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:label="Audio Service Demo"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name="com.ryanheise.audioservice.AudioService">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>

        <receiver android:name="androidx.media.session.MediaButtonReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver> 

        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />

    </application>
</manifest>

@Adnanbd
Copy link

Adnanbd commented Jul 4, 2021

I don't get the solution.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 13, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants