diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index d4b694c..745d21c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,8 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
-
-
+
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/example/groovybox/MainActivity.kt b/android/app/src/main/kotlin/com/example/groovybox/MainActivity.kt
index 0fd0dd4..0242d8c 100644
--- a/android/app/src/main/kotlin/com/example/groovybox/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/groovybox/MainActivity.kt
@@ -1,5 +1,5 @@
package dev.solsynth.groovybox
-import io.flutter.embedding.android.FlutterActivity
+import com.ryanheise.audioservice.AudioServiceActivity
-class MainActivity : FlutterActivity()
+class MainActivity : AudioServiceActivity()
diff --git a/lib/logic/audio_handler.dart b/lib/logic/audio_handler.dart
index 87e22ef..af1b3d6 100644
--- a/lib/logic/audio_handler.dart
+++ b/lib/logic/audio_handler.dart
@@ -1,26 +1,200 @@
-import 'package:media_kit/media_kit.dart';
+import 'package:audio_service/audio_service.dart';
+import 'package:media_kit/media_kit.dart' as media_kit;
+import 'package:groovybox/data/db.dart';
-class AudioHandler {
- final Player _player;
+class AudioHandler extends BaseAudioHandler with QueueHandler, SeekHandler {
+ final media_kit.Player _player;
+ List _queue = [];
+ int _queueIndex = 0;
- AudioHandler() : _player = Player() {
+ AudioHandler() : _player = media_kit.Player() {
// Configure for audio
// _player.setPlaylistMode(PlaylistMode.loop); // Optional
+
+ // Listen to player state changes and broadcast to audio_service
+ _player.stream.playing.listen((playing) {
+ _broadcastPlaybackState();
+ });
+
+ _player.stream.position.listen((position) {
+ _broadcastPlaybackState();
+ });
+
+ _player.stream.duration.listen((duration) {
+ _broadcastPlaybackState();
+ });
+
+ _player.stream.playlist.listen((playlist) {
+ if (playlist.medias.isNotEmpty) {
+ final currentIndex = playlist.index;
+ if (currentIndex >= 0 && currentIndex < _queue.length) {
+ _queueIndex = currentIndex;
+ mediaItem.add(_queue[_queueIndex]);
+ }
+ }
+ });
}
- Player get player => _player;
+ media_kit.Player get player => _player;
+ // AudioService callbacks
+ @override
Future play() => _player.play();
+
+ @override
Future pause() => _player.pause();
+
+ @override
Future stop() => _player.stop();
+
+ @override
Future seek(Duration position) => _player.seek(position);
- Future setSource(String path) async {
- await _player.open(Media(path));
+ @override
+ Future skipToNext() async {
+ if (_queueIndex < _queue.length - 1) {
+ _queueIndex++;
+ await _player.jump(_queueIndex);
+ }
}
- Future openPlaylist(List medias, {int initialIndex = 0}) async {
- await _player.open(Playlist(medias, index: initialIndex), play: true);
+ @override
+ Future skipToPrevious() async {
+ if (_queueIndex > 0) {
+ _queueIndex--;
+ await _player.jump(_queueIndex);
+ }
+ }
+
+ @override
+ Future skipToQueueItem(int index) async {
+ if (index >= 0 && index < _queue.length) {
+ _queueIndex = index;
+ await _player.jump(index);
+ }
+ }
+
+ @override
+ Future addQueueItem(MediaItem mediaItem) async {
+ _queue.add(mediaItem);
+ queue.add(_queue);
+ await _updatePlaylist();
+ }
+
+ @override
+ Future insertQueueItem(int index, MediaItem mediaItem) async {
+ if (index >= 0 && index <= _queue.length) {
+ _queue.insert(index, mediaItem);
+ queue.add(_queue);
+ await _updatePlaylist();
+ }
+ }
+
+ @override
+ Future removeQueueItem(MediaItem mediaItem) async {
+ _queue.remove(mediaItem);
+ queue.add(_queue);
+ await _updatePlaylist();
+ }
+
+ @override
+ Future updateQueue(List queue) async {
+ _queue = List.from(queue);
+ this.queue.add(_queue);
+ await _updatePlaylist();
+ }
+
+ Future _updatePlaylist() async {
+ final medias = _queue.map((item) => media_kit.Media(item.id)).toList();
+ if (medias.isNotEmpty) {
+ await _player.open(media_kit.Playlist(medias, index: _queueIndex));
+ }
+ }
+
+ void _broadcastPlaybackState() {
+ final playing = _player.state.playing;
+ final position = _player.state.position;
+ final duration = _player.state.duration;
+
+ playbackState.add(
+ PlaybackState(
+ controls: [
+ MediaControl.skipToPrevious,
+ playing ? MediaControl.pause : MediaControl.play,
+ MediaControl.stop,
+ MediaControl.skipToNext,
+ ],
+ systemActions: const {
+ MediaAction.seek,
+ MediaAction.seekForward,
+ MediaAction.seekBackward,
+ },
+ androidCompactActionIndices: const [0, 1, 3],
+ processingState: AudioProcessingState.ready,
+ playing: playing,
+ updatePosition: position,
+ bufferedPosition: duration,
+ speed: 1.0,
+ queueIndex: _queueIndex,
+ ),
+ );
+ }
+
+ // New methods that accept Track objects with proper metadata
+ Future playTrack(Track track) async {
+ final mediaItem = _trackToMediaItem(track);
+ await updateQueue([mediaItem]);
+ }
+
+ Future playTracks(List