diff --git a/lib/main.dart b/lib/main.dart index 8eb62f1e..54943c48 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -44,6 +44,10 @@ class MyApp extends StatelessWidget { SystemChannels.lifecycle.setMessageHandler((msg) async { if (msg == "AppLifecycleState.resumed") { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } else if (msg == "AppLifecycleState.detached") { + if (Hive.box("AppPrefs").get('restrorePlaybackSession') ?? false) { + Get.find().customAction("saveSession"); + } } return null; }); @@ -58,8 +62,9 @@ class MyApp extends StatelessWidget { Hive.box("AppPrefs").get('currentAppLanguageCode') ?? "en"), fallbackLocale: const Locale("en"), builder: (context, child) { - final scale = - MediaQuery.of(context).textScaler.clamp(minScaleFactor:1.0,maxScaleFactor: 1.1); + final scale = MediaQuery.of(context) + .textScaler + .clamp(minScaleFactor: 1.0, maxScaleFactor: 1.1); return MediaQuery( data: MediaQuery.of(context).copyWith(textScaler: scale), child: child!, @@ -89,9 +94,11 @@ Future startApplicationServices() async { initHive() async { String applicationDataDirectoryPath; if (GetPlatform.isDesktop) { - applicationDataDirectoryPath = "${(await getApplicationSupportDirectory()).path}/db"; + applicationDataDirectoryPath = + "${(await getApplicationSupportDirectory()).path}/db"; } else { - applicationDataDirectoryPath = (await getApplicationDocumentsDirectory()).path; + applicationDataDirectoryPath = + (await getApplicationDocumentsDirectory()).path; } await Hive.initFlutter(applicationDataDirectoryPath); await Hive.openBox("SongsCache"); diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index 0ae102e8..3989a870 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -272,7 +272,7 @@ class MyAudioHandler extends BaseAudioHandler with GetxServiceMixin { @override Future play() async { - if (currentSongUrl == null) { + if (currentSongUrl == null || (GetPlatform.isDesktop && (_player.duration == null || _player.duration?.inMilliseconds==0))) { await customAction("playByIndex", {'index': currentIndex}); return; } @@ -340,11 +340,12 @@ class MyAudioHandler extends BaseAudioHandler with GetxServiceMixin { await _player.dispose(); super.stop(); } else if (name == 'playByIndex') { + final bool restoreSession = extras!['restoreSession'] ?? false; isSongLoading = true; if (_playList.children.isNotEmpty) { await _playList.clear(); } - final songIndex = extras!['index']; + final songIndex = extras['index']; currentIndex = songIndex; final isNewUrlReq = extras['newUrl'] ?? false; final currentSong = queue.value[currentIndex]; @@ -359,7 +360,25 @@ class MyAudioHandler extends BaseAudioHandler with GetxServiceMixin { playbackState.add(playbackState.value.copyWith(queueIndex: currentIndex)); await _playList.add(_createAudioSource(currentSong)); isSongLoading = false; - await _player.play(); + + if (restoreSession) { + if (!GetPlatform.isDesktop) { + final position = extras['position']; + await _player.load(); + await _player.seek( + Duration( + milliseconds: position, + ), + ); + await _player.seek( + Duration( + milliseconds: position, + ), + ); + } + } else { + await _player.play(); + } if (currentIndex == 0) { cacheNextSongUrl(offset: 1); } @@ -435,6 +454,24 @@ class MyAudioHandler extends BaseAudioHandler with GetxServiceMixin { queue.add(currentQueue); } else if (name == 'openEqualizer') { await DeviceEqualizer().open(_player.androidAudioSessionId!); + } else if (name == "saveSession") { + await saveSessionData(); + } + } + + Future saveSessionData() async { + final currQueue = queue.value; + if (currQueue.isNotEmpty) { + final queueData = + currQueue.map((e) => MediaItemBuilder.toJson(e)).toList(); + final currIndex = currentIndex ?? 0; + final position = _player.position.inMilliseconds; + final prevSessionData = await Hive.openBox("prevSessionData"); + await prevSessionData.clear(); + await prevSessionData.putAll( + {"queue": queueData, "position": position, "index": currIndex}); + await prevSessionData.close(); + printINFO("Saved session data"); } } @@ -449,6 +486,7 @@ class MyAudioHandler extends BaseAudioHandler with GetxServiceMixin { @override Future stop() async { + //await saveSessionData(); await _player.stop(); return super.stop(); } diff --git a/lib/ui/home.dart b/lib/ui/home.dart index a5c1bfd3..e851ea1b 100644 --- a/lib/ui/home.dart +++ b/lib/ui/home.dart @@ -62,19 +62,15 @@ class Home extends StatelessWidget { endDrawer: Container( constraints: const BoxConstraints(maxWidth: 600), decoration: BoxDecoration( - borderRadius: - const BorderRadius.only(topLeft: Radius.circular(10)), - border: Border( - left: BorderSide( - color: Theme.of(context).colorScheme.secondary), - top: BorderSide( - color: Theme.of(context).colorScheme.secondary), - bottom: BorderSide( - width: 0, - color: Theme.of(context).colorScheme.secondary), - right: BorderSide( - width: 0, - color: Theme.of(context).colorScheme.secondary))), + borderRadius: + const BorderRadius.only(topLeft: Radius.circular(10)), + border: Border( + left: BorderSide( + color: Theme.of(context).colorScheme.secondary), + top: BorderSide( + color: Theme.of(context).colorScheme.secondary), + ), + ), margin: const EdgeInsets.only( top: 5, bottom: 106, diff --git a/lib/ui/player/player_controller.dart b/lib/ui/player/player_controller.dart index 673512a4..900ca5ac 100644 --- a/lib/ui/player/player_controller.dart +++ b/lib/ui/player/player_controller.dart @@ -76,6 +76,7 @@ class PlayerController extends GetxController { if (GetPlatform.isWindows) { Get.put(WindowsAudioService()); } + _restorePrevSession(); super.onReady(); } @@ -208,6 +209,29 @@ class PlayerController extends GetxController { }); } + Future _restorePrevSession() async { + final restrorePrevSessionEnabled = + Hive.box("AppPrefs").get("restrorePlaybackSession") ?? false; + if (restrorePrevSessionEnabled) { + final prevSessionData = await Hive.openBox("prevSessionData"); + if (prevSessionData.keys.isNotEmpty) { + final songList = (prevSessionData.get("queue") as List) + .map((e) => MediaItemBuilder.fromJson(e)) + .toList(); + final int currentIndex = prevSessionData.get("index"); + final int position = prevSessionData.get("position"); + prevSessionData.close(); + await _audioHandler.addQueueItems(songList); + _playerPanelCheck(restoreSession: true); + await _audioHandler.customAction("playByIndex", { + "index": currentIndex, + "position": position, + "restoreSession": true + }); + } + } + } + ///pushSongToPlaylist method clear previous song queue, plays the tapped song and push related ///songs into Queue Future pushSongToQueue(MediaItem? mediaItem, @@ -333,9 +357,10 @@ class PlayerController extends GetxController { } } - void _playerPanelCheck() { + void _playerPanelCheck({bool restoreSession = false}) { final isWideScreen = Get.size.width > 800; - if (!isWideScreen && playerPanelController.isAttached) { + if ((!isWideScreen && playerPanelController.isAttached) && + !restoreSession) { playerPanelController.open(); } diff --git a/lib/ui/screens/Settings/settings_screen.dart b/lib/ui/screens/Settings/settings_screen.dart index c5873bab..d2f7a934 100644 --- a/lib/ui/screens/Settings/settings_screen.dart +++ b/lib/ui/screens/Settings/settings_screen.dart @@ -289,6 +289,17 @@ class SettingsScreen extends StatelessWidget { ), ) : const SizedBox.shrink()), + ListTile( + contentPadding: const EdgeInsets.only(left: 5, right: 10), + title: Text("restoreLastPlaybackSession".tr), + subtitle: Text("restoreLastPlaybackSessionDes".tr, + style: Theme.of(context).textTheme.bodyMedium), + trailing: Obx( + () => Switch( + value: settingsController.restorePlaybackSession.value, + onChanged: + settingsController.toggleRestorePlaybackSession), + )), if (!isDesktop) ListTile( contentPadding: diff --git a/lib/ui/screens/Settings/settings_screen_controller.dart b/lib/ui/screens/Settings/settings_screen_controller.dart index 41dc6cf4..af03eb9f 100644 --- a/lib/ui/screens/Settings/settings_screen_controller.dart +++ b/lib/ui/screens/Settings/settings_screen_controller.dart @@ -37,6 +37,7 @@ class SettingsScreenController extends GetxController { final hideDloc = true.obs; final isBottomNavBarEnabled = false.obs; final backgroundPlayEnabled = true.obs; + final restorePlaybackSession = false.obs; final currentVersion = "V1.8.0"; @override @@ -73,6 +74,8 @@ class SettingsScreenController extends GetxController { cacheSongs.value = setBox.get('cacheSongs'); themeModetype.value = ThemeType.values[setBox.get('themeModeType')]; skipSilenceEnabled.value = setBox.get("skipSilenceEnabled"); + restorePlaybackSession.value = + setBox.get("restrorePlaybackSession") ?? false; streamingQuality.value = AudioQuality.values[setBox.get('streamingQuality')]; backgroundPlayEnabled.value = setBox.get("backgroundPlayEnabled") ?? true; @@ -189,7 +192,12 @@ class SettingsScreenController extends GetxController { skipSilenceEnabled.value = val; } - void toggleBackgroundPlay(bool val){ + void toggleRestorePlaybackSession(bool val) { + setBox.put("restrorePlaybackSession", val); + restorePlaybackSession.value = val; + } + + void toggleBackgroundPlay(bool val) { setBox.put('backgroundPlayEnabled', val); backgroundPlayEnabled.value = val; } diff --git a/lib/utils/get_localization.dart b/lib/utils/get_localization.dart index a32dce47..b2a77105 100644 --- a/lib/utils/get_localization.dart +++ b/lib/utils/get_localization.dart @@ -733,6 +733,9 @@ class Languages extends Translations { "trending": "Trending", "topmusicvideos": "Top Music Videos", "basedOnLast": "Based on last interaction", + "restoreLastPlaybackSession": "Restore last playback session", + "restoreLastPlaybackSessionDes": + "Automatically restore the last playback session on app start", "homeContentCount": "Home content count", "homeContentCountDes": "Select the number of initial homescreen-content(approx). Lesser results faster loading", diff --git a/lib/utils/system_tray.dart b/lib/utils/system_tray.dart index 5ef33457..4c7d2171 100644 --- a/lib/utils/system_tray.dart +++ b/lib/utils/system_tray.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:audio_service/audio_service.dart'; import 'package:get/get.dart'; import 'package:harmonymusic/ui/player/player_controller.dart'; import 'package:harmonymusic/ui/screens/Settings/settings_screen_controller.dart'; @@ -62,7 +63,15 @@ class DesktopSystemTray extends GetxService { }), MenuSeparator(), MenuItemLabel( - label: 'Quit', onClicked: (menuItem) => exit(0)), + label: 'Quit', + onClicked: (menuItem) async { + if (Get.find() + .restorePlaybackSession + .isTrue) { + await Get.find().customAction("saveSession"); + } + exit(0); + }), ]); // set context menu @@ -95,11 +104,15 @@ class DesktopSystemTray extends GetxService { class CloseWindowListener extends WindowListener { @override Future onWindowClose() async { - if (Get.find().backgroundPlayEnabled.isTrue && + final settingsScrnController = Get.find(); + if (settingsScrnController.backgroundPlayEnabled.isTrue && Get.find().buttonState.value == PlayButtonState.playing) { await windowManager.hide(); } else { + if (settingsScrnController.restorePlaybackSession.isTrue) { + await Get.find().customAction("saveSession"); + } exit(0); } } diff --git a/localization/en.json b/localization/en.json index 88d868c7..b52f84bb 100644 --- a/localization/en.json +++ b/localization/en.json @@ -65,6 +65,8 @@ "trending": "Trending", "topmusicvideos": "Top Music Videos", "basedOnLast": "Based on last interaction", + "restoreLastPlaybackSession": "Restore last playback session", + "restoreLastPlaybackSessionDes": "Automatically restore the last playback session on app start", "homeContentCount": "Home content count", "homeContentCountDes": "Select the number of initial homescreen-content(approx). Lesser results faster loading", "enableBottomNav": "Bottom nav bar",