diff --git a/lib/components/Home/Home.dart b/lib/components/Home/Home.dart index 15495d51..b6433417 100644 --- a/lib/components/Home/Home.dart +++ b/lib/components/Home/Home.dart @@ -18,7 +18,6 @@ import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Player/Player.dart'; import 'package:spotube/components/Library/UserLibrary.dart'; import 'package:spotube/hooks/useBreakpointValue.dart'; -import 'package:spotube/hooks/useHotKeys.dart'; import 'package:spotube/hooks/usePaginatedFutureProvider.dart'; import 'package:spotube/hooks/useUpdateChecker.dart'; import 'package:spotube/models/Logger.dart'; @@ -54,8 +53,6 @@ class Home extends HookConsumerWidget { final _selectedIndex = useState(0); _onSelectedIndexChanged(int index) => _selectedIndex.value = index; - // initializing global hot keys - useHotKeys(ref); // checks for latest version of the application useUpdateChecker(ref); diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index ce28fea8..59ace618 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -1,19 +1,15 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/components/Settings/About.dart'; import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart'; -import 'package:spotube/components/Settings/SettingsHotkeyTile.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/helpers/search-youtube.dart'; import 'package:spotube/models/SpotifyMarkets.dart'; import 'package:spotube/models/SpotubeTrack.dart'; import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/UserPreferences.dart'; -import 'package:spotube/utils/platform.dart'; import 'package:url_launcher/url_launcher_string.dart'; class Settings extends HookConsumerWidget { @@ -57,29 +53,6 @@ class Settings extends HookConsumerWidget { constraints: const BoxConstraints(maxWidth: 1366), child: ListView( children: [ - if (!kIsMobile) ...[ - SettingsHotKeyTile( - title: "Next track global shortcut", - currentHotKey: preferences.nextTrackHotKey, - onHotKeyRecorded: (value) { - preferences.setNextTrackHotKey(value); - }, - ), - SettingsHotKeyTile( - title: "Prev track global shortcut", - currentHotKey: preferences.prevTrackHotKey, - onHotKeyRecorded: (value) { - preferences.setPrevTrackHotKey(value); - }, - ), - SettingsHotKeyTile( - title: "Play/Pause global shortcut", - currentHotKey: preferences.playPauseHotKey, - onHotKeyRecorded: (value) { - preferences.setPlayPauseHotKey(value); - }, - ), - ], ListTile( title: const Text("Theme"), horizontalTitleGap: 10, diff --git a/lib/components/Settings/SettingsHotkeyTile.dart b/lib/components/Settings/SettingsHotkeyTile.dart deleted file mode 100644 index a1f32abf..00000000 --- a/lib/components/Settings/SettingsHotkeyTile.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hotkey_manager/hotkey_manager.dart'; -import 'package:spotube/components/Shared/RecordHotKeyDialog.dart'; - -class SettingsHotKeyTile extends StatelessWidget { - final String title; - final HotKey? currentHotKey; - final ValueChanged onHotKeyRecorded; - const SettingsHotKeyTile({ - required this.onHotKeyRecorded, - required this.title, - Key? key, - this.currentHotKey, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ListTile( - title: Text(title), - trailing: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (currentHotKey != null) HotKeyVirtualView(hotKey: currentHotKey!), - const SizedBox(width: 10), - ElevatedButton( - child: const Text("Set Shortcut"), - onPressed: () { - showDialog( - context: context, - builder: (context) { - return RecordHotKeyDialog( - onHotKeyRecorded: onHotKeyRecorded, - ); - }, - ); - }, - ), - ], - ), - ); - } -} diff --git a/lib/components/Shared/DownloadTrackButton.dart b/lib/components/Shared/DownloadTrackButton.dart index f11493dd..332f20fc 100644 --- a/lib/components/Shared/DownloadTrackButton.dart +++ b/lib/components/Shared/DownloadTrackButton.dart @@ -115,6 +115,24 @@ class DownloadTrackButton extends HookConsumerWidget { if (!await outputFile.exists()) await outputFile.create(recursive: true); + IOSink outputFileStream = outputFile.openWrite(); + await audioStream.pipe(outputFileStream); + await outputFileStream.flush(); + await outputFileStream.close().then((value) async { + if (status.value == TrackStatus.downloading) { + status.value = TrackStatus.done; + await Future.delayed( + const Duration(seconds: 3), + () { + if (status.value == TrackStatus.done) { + status.value = TrackStatus.idle; + } + }, + ); + } + return statusCb.cancel(); + }); + if (preferences.saveTrackLyrics && playback.currentTrack != null) { if (!await outputLyricsFile.exists()) { await outputLyricsFile.create(recursive: true); @@ -136,24 +154,6 @@ class DownloadTrackButton extends HookConsumerWidget { ); } } - - IOSink outputFileStream = outputFile.openWrite(); - await audioStream.pipe(outputFileStream); - await outputFileStream.flush(); - await outputFileStream.close().then((value) async { - if (status.value == TrackStatus.downloading) { - status.value = TrackStatus.done; - await Future.delayed( - const Duration(seconds: 3), - () { - if (status.value == TrackStatus.done) { - status.value = TrackStatus.idle; - } - }, - ); - } - return statusCb.cancel(); - }); }, [ track, status, diff --git a/lib/components/Shared/RecordHotKeyDialog.dart b/lib/components/Shared/RecordHotKeyDialog.dart deleted file mode 100644 index ff8a6885..00000000 --- a/lib/components/Shared/RecordHotKeyDialog.dart +++ /dev/null @@ -1,85 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:go_router/go_router.dart'; -import 'package:hotkey_manager/hotkey_manager.dart'; - -class RecordHotKeyDialog extends HookWidget { - final ValueChanged onHotKeyRecorded; - - const RecordHotKeyDialog({ - Key? key, - required this.onHotKeyRecorded, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - final _hotKey = useState(null); - return AlertDialog( - content: SingleChildScrollView( - child: ListBody( - children: [ - Text( - 'Press the keys you want to use', - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 10), - const Text( - "DO NOT Use only letters (e.g. k, g etc..)\nUse in combination with these"), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - KeyCode.control, - KeyCode.shift, - KeyCode.alt, - KeyCode.superKey, - KeyCode.meta, - ] - .map((key) => HotKeyVirtualView( - hotKey: HotKey(key), - )) - .toList(), - ), - Container( - width: 100, - height: 60, - margin: const EdgeInsets.only(top: 20), - decoration: BoxDecoration( - border: Border.all( - color: Theme.of(context).primaryColor, - ), - ), - child: Stack( - alignment: Alignment.center, - children: [ - HotKeyRecorder( - onHotKeyRecorded: (hotKey) { - _hotKey.value = hotKey; - }, - ), - ], - ), - ), - ], - ), - ), - actions: [ - TextButton( - child: const Text('Cancel'), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: const Text('OK'), - onPressed: _hotKey.value == null - ? null - : () { - onHotKeyRecorded(_hotKey.value!); - GoRouter.of(context).pop(); - }, - ), - ], - ); - } -} diff --git a/lib/hooks/useHotKeys.dart b/lib/hooks/useHotKeys.dart deleted file mode 100644 index e4989870..00000000 --- a/lib/hooks/useHotKeys.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'dart:io'; - -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:hotkey_manager/hotkey_manager.dart'; -import 'package:spotube/hooks/playback.dart'; -import 'package:spotube/models/GlobalKeyActions.dart'; -import 'package:spotube/provider/Playback.dart'; -import 'package:spotube/provider/UserPreferences.dart'; -import 'package:spotube/utils/platform.dart'; - -useHotKeys(WidgetRef ref) { - final playback = ref.watch(playbackProvider); - final preferences = ref.watch(userPreferencesProvider); - List _hotKeys = []; - - final onNext = useNextTrack(playback); - - final onPrevious = usePreviousTrack(playback); - - final _playOrPause = useTogglePlayPause(playback); - - useEffect(() { - if (kIsMobile) return null; - _hotKeys = [ - GlobalKeyActions( - HotKey(KeyCode.space, scope: HotKeyScope.inapp), - _playOrPause, - ), - if (preferences.nextTrackHotKey != null) - GlobalKeyActions(preferences.nextTrackHotKey!, (key) => onNext()), - if (preferences.prevTrackHotKey != null) - GlobalKeyActions(preferences.prevTrackHotKey!, (key) => onPrevious()), - if (preferences.playPauseHotKey != null) - GlobalKeyActions(preferences.playPauseHotKey!, _playOrPause) - ]; - Future.wait( - _hotKeys.map((e) { - return hotKeyManager.register( - e.hotKey, - keyDownHandler: e.onKeyDown, - ); - }), - ); - return () { - Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e.hotKey))); - }; - }); -} diff --git a/lib/interfaces/media_player2.dart b/lib/interfaces/media_player2.dart new file mode 100644 index 00000000..cbacc307 --- /dev/null +++ b/lib/interfaces/media_player2.dart @@ -0,0 +1,207 @@ +// This file was generated using the following command and may be overwritten. +// dart-dbus generate-object defs/org.mpris.MediaPlayer2.xml + +import 'package:bitsdojo_window/bitsdojo_window.dart'; +import 'package:dbus/dbus.dart'; + +class Media_Player extends DBusObject { + /// Creates a new object to expose on [path]. + Media_Player() : super(DBusObjectPath('/org/mpris/MediaPlayer2')); + + /// Gets value of property org.mpris.MediaPlayer2.CanQuit + Future getCanQuit() async { + return DBusMethodSuccessResponse([const DBusBoolean(true)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Fullscreen + Future getFullscreen() async { + return DBusMethodSuccessResponse([const DBusBoolean(false)]); + } + + /// Sets property org.mpris.MediaPlayer2.Fullscreen + Future setFullscreen(bool value) async { + return DBusMethodSuccessResponse(); + } + + /// Gets value of property org.mpris.MediaPlayer2.CanSetFullscreen + Future getCanSetFullscreen() async { + return DBusMethodSuccessResponse([const DBusBoolean(false)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.CanRaise + Future getCanRaise() async { + return DBusMethodSuccessResponse([const DBusBoolean(false)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.HasTrackList + Future getHasTrackList() async { + return DBusMethodSuccessResponse([const DBusBoolean(false)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Identity + Future getIdentity() async { + return DBusMethodSuccessResponse([const DBusString("Spotube")]); + } + + /// Gets value of property org.mpris.MediaPlayer2.DesktopEntry + Future getDesktopEntry() async { + return DBusMethodSuccessResponse([const DBusString("spotube")]); + } + + /// Gets value of property org.mpris.MediaPlayer2.SupportedUriSchemes + Future getSupportedUriSchemes() async { + return DBusMethodSuccessResponse([ + DBusArray.string(["http"]) + ]); + } + + /// Gets value of property org.mpris.MediaPlayer2.SupportedMimeTypes + Future getSupportedMimeTypes() async { + return DBusMethodSuccessResponse([ + DBusArray.string(["audio/mpeg"]) + ]); + } + + /// Implementation of org.mpris.MediaPlayer2.Raise() + Future doRaise() async { + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Quit() + Future doQuit() async { + appWindow.close(); + return DBusMethodSuccessResponse(); + } + + @override + List introspect() { + return [ + DBusIntrospectInterface('org.mpris.MediaPlayer2', methods: [ + DBusIntrospectMethod('Raise'), + DBusIntrospectMethod('Quit') + ], properties: [ + DBusIntrospectProperty('CanQuit', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('Fullscreen', DBusSignature('b'), + access: DBusPropertyAccess.readwrite), + DBusIntrospectProperty('CanSetFullscreen', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanRaise', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('HasTrackList', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('Identity', DBusSignature('s'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('DesktopEntry', DBusSignature('s'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('SupportedUriSchemes', DBusSignature('as'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('SupportedMimeTypes', DBusSignature('as'), + access: DBusPropertyAccess.read) + ]) + ]; + } + + @override + Future handleMethodCall(DBusMethodCall methodCall) async { + if (methodCall.interface == 'org.mpris.MediaPlayer2') { + if (methodCall.name == 'Raise') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doRaise(); + } else if (methodCall.name == 'Quit') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doQuit(); + } else { + return DBusMethodErrorResponse.unknownMethod(); + } + } else { + return DBusMethodErrorResponse.unknownInterface(); + } + } + + @override + Future getProperty(String interface, String name) async { + if (interface == 'org.mpris.MediaPlayer2') { + if (name == 'CanQuit') { + return getCanQuit(); + } else if (name == 'Fullscreen') { + return getFullscreen(); + } else if (name == 'CanSetFullscreen') { + return getCanSetFullscreen(); + } else if (name == 'CanRaise') { + return getCanRaise(); + } else if (name == 'HasTrackList') { + return getHasTrackList(); + } else if (name == 'Identity') { + return getIdentity(); + } else if (name == 'DesktopEntry') { + return getDesktopEntry(); + } else if (name == 'SupportedUriSchemes') { + return getSupportedUriSchemes(); + } else if (name == 'SupportedMimeTypes') { + return getSupportedMimeTypes(); + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } + + @override + Future setProperty( + String interface, String name, DBusValue value) async { + if (interface == 'org.mpris.MediaPlayer2') { + if (name == 'CanQuit') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'Fullscreen') { + if (value.signature != DBusSignature('b')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return setFullscreen((value as DBusBoolean).value); + } else if (name == 'CanSetFullscreen') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanRaise') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'HasTrackList') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'Identity') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'DesktopEntry') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'SupportedUriSchemes') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'SupportedMimeTypes') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } + + @override + Future getAllProperties(String interface) async { + var properties = {}; + if (interface == 'org.mpris.MediaPlayer2') { + properties['CanQuit'] = (await getCanQuit()).returnValues[0]; + properties['Fullscreen'] = (await getFullscreen()).returnValues[0]; + properties['CanSetFullscreen'] = + (await getCanSetFullscreen()).returnValues[0]; + properties['CanRaise'] = (await getCanRaise()).returnValues[0]; + properties['HasTrackList'] = (await getHasTrackList()).returnValues[0]; + properties['Identity'] = (await getIdentity()).returnValues[0]; + properties['DesktopEntry'] = (await getDesktopEntry()).returnValues[0]; + properties['SupportedUriSchemes'] = + (await getSupportedUriSchemes()).returnValues[0]; + properties['SupportedMimeTypes'] = + (await getSupportedMimeTypes()).returnValues[0]; + } + return DBusMethodSuccessResponse([DBusDict.stringVariant(properties)]); + } +} diff --git a/lib/interfaces/media_player2_player.dart b/lib/interfaces/media_player2_player.dart new file mode 100644 index 00000000..8e65c62e --- /dev/null +++ b/lib/interfaces/media_player2_player.dart @@ -0,0 +1,470 @@ +// This file was generated using the following command and may be overwritten. +// dart-dbus generate-object defs/org.mpris.MediaPlayer2.Player.xml + +import 'package:dbus/dbus.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:spotube/helpers/image-to-url-string.dart'; +import 'package:spotube/models/SpotubeTrack.dart'; +import 'package:spotube/provider/Playback.dart'; + +class Player_Interface extends DBusObject { + final AudioPlayer player; + final Playback playback; + + /// Creates a new object to expose on [path]. + Player_Interface({ + required this.player, + required this.playback, + }) : super(DBusObjectPath("/org/mpris/MediaPlayer2")); + + /// Gets value of property org.mpris.MediaPlayer2.Player.PlaybackStatus + Future getPlaybackStatus() async { + final status = player.playing + ? "Playing" + : playback.currentPlaylist == null + ? "Stopped" + : "Paused"; + return DBusMethodSuccessResponse([DBusString(status)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.LoopStatus + Future getLoopStatus() async { + return DBusMethodSuccessResponse([const DBusString("Playlist")]); + } + + /// Sets property org.mpris.MediaPlayer2.Player.LoopStatus + Future setLoopStatus(String value) async { + return DBusMethodErrorResponse.failed( + 'Set org.mpris.MediaPlayer2.Player.LoopStatus not implemented'); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.Rate + Future getRate() async { + return DBusMethodSuccessResponse([DBusDouble(player.speed)]); + } + + /// Sets property org.mpris.MediaPlayer2.Player.Rate + Future setRate(double value) async { + player.setSpeed(value); + return DBusMethodSuccessResponse(); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.Shuffle + Future getShuffle() async { + return DBusMethodSuccessResponse([DBusBoolean(playback.shuffled)]); + } + + /// Sets property org.mpris.MediaPlayer2.Player.Shuffle + Future setShuffle(bool value) async { + if (value) { + playback.shuffle(); + } else { + playback.unshuffle(); + } + return DBusMethodSuccessResponse(); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.Metadata + Future getMetadata() async { + try { + if (playback.currentTrack == null) { + return DBusMethodSuccessResponse([DBusDict.stringVariant({})]); + } + final id = (playback.currentPlaylist != null + ? playback.currentPlaylist!.tracks.indexWhere( + (track) => playback.currentTrack!.id == track.id!, + ) + : 0) + .abs(); + + return DBusMethodSuccessResponse([ + DBusDict.stringVariant({ + "mpris:trackid": DBusString("${path.value}/Track/$id"), + "mpris:length": DBusInt32(playback.duration?.inMicroseconds ?? 0), + "mpris:artUrl": DBusString( + imageToUrlString(playback.currentTrack?.album?.images)), + "xesam:album": DBusString(playback.currentTrack!.album!.name!), + "xesam:artist": DBusArray.string( + playback.currentTrack!.artists!.map((artist) => artist.name!), + ), + "xesam:title": DBusString(playback.currentTrack!.name!), + "xesam:url": DBusString( + playback.currentTrack is SpotubeTrack + ? (playback.currentTrack as SpotubeTrack).ytUri + : playback.currentTrack!.previewUrl!, + ), + "xesam:genre": const DBusString("Unknown"), + }), + ]); + } catch (e) { + print("[DBUS ERROR] $e"); + rethrow; + } + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.Volume + Future getVolume() async { + return DBusMethodSuccessResponse([DBusDouble(player.volume)]); + } + + /// Sets property org.mpris.MediaPlayer2.Player.Volume + Future setVolume(double value) async { + player.setVolume(value); + return DBusMethodSuccessResponse(); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.Position + Future getPosition() async { + return DBusMethodSuccessResponse([ + DBusInt64(player.position.inMicroseconds), + ]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.MinimumRate + Future getMinimumRate() async { + return DBusMethodSuccessResponse([const DBusDouble(1)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.MaximumRate + Future getMaximumRate() async { + return DBusMethodSuccessResponse([const DBusDouble(1)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.CanGoNext + Future getCanGoNext() async { + return DBusMethodSuccessResponse([ + DBusBoolean( + playback.currentPlaylist?.tracks.isNotEmpty == true, + ) + ]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.CanGoPrevious + Future getCanGoPrevious() async { + return DBusMethodSuccessResponse([ + DBusBoolean( + playback.currentPlaylist?.tracks.isNotEmpty == true, + ) + ]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.CanPlay + Future getCanPlay() async { + return DBusMethodSuccessResponse([const DBusBoolean(true)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.CanPause + Future getCanPause() async { + return DBusMethodSuccessResponse([const DBusBoolean(true)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.CanSeek + Future getCanSeek() async { + return DBusMethodSuccessResponse([const DBusBoolean(true)]); + } + + /// Gets value of property org.mpris.MediaPlayer2.Player.CanControl + Future getCanControl() async { + return DBusMethodSuccessResponse([const DBusBoolean(true)]); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.Next() + Future doNext() async { + playback.movePlaylistPositionBy(1); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.Previous() + Future doPrevious() async { + playback.movePlaylistPositionBy(-1); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.Pause() + Future doPause() async { + player.pause(); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.PlayPause() + Future doPlayPause() async { + player.playing ? player.pause() : player.play(); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.Stop() + Future doStop() async { + await player.pause(); + await player.seek(Duration.zero); + playback.reset(); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.Play() + Future doPlay() async { + player.play(); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.Seek() + Future doSeek(int offset) async { + player.seek(Duration(microseconds: offset)); + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.SetPosition() + Future doSetPosition(String TrackId, int Position) async { + return DBusMethodSuccessResponse(); + } + + /// Implementation of org.mpris.MediaPlayer2.Player.OpenUri() + Future doOpenUri(String Uri) async { + return DBusMethodSuccessResponse(); + } + + /// Emits signal org.mpris.MediaPlayer2.Player.Seeked + Future emitSeeked(int Position) async { + await emitSignal( + 'org.mpris.MediaPlayer2.Player', 'Seeked', [DBusInt64(Position)]); + } + + @override + List introspect() { + return [ + DBusIntrospectInterface('org.mpris.MediaPlayer2.Player', methods: [ + DBusIntrospectMethod('Next'), + DBusIntrospectMethod('Previous'), + DBusIntrospectMethod('Pause'), + DBusIntrospectMethod('PlayPause'), + DBusIntrospectMethod('Stop'), + DBusIntrospectMethod('Play'), + DBusIntrospectMethod('Seek', args: [ + DBusIntrospectArgument(DBusSignature('x'), DBusArgumentDirection.in_, + name: 'Offset') + ]), + DBusIntrospectMethod('SetPosition', args: [ + DBusIntrospectArgument(DBusSignature('o'), DBusArgumentDirection.in_, + name: 'TrackId'), + DBusIntrospectArgument(DBusSignature('x'), DBusArgumentDirection.in_, + name: 'Position') + ]), + DBusIntrospectMethod('OpenUri', args: [ + DBusIntrospectArgument(DBusSignature('s'), DBusArgumentDirection.in_, + name: 'Uri') + ]) + ], signals: [ + DBusIntrospectSignal('Seeked', args: [ + DBusIntrospectArgument(DBusSignature('x'), DBusArgumentDirection.out, + name: 'Position') + ]) + ], properties: [ + DBusIntrospectProperty('PlaybackStatus', DBusSignature('s'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('LoopStatus', DBusSignature('s'), + access: DBusPropertyAccess.readwrite), + DBusIntrospectProperty('Rate', DBusSignature('d'), + access: DBusPropertyAccess.readwrite), + DBusIntrospectProperty('Shuffle', DBusSignature('b'), + access: DBusPropertyAccess.readwrite), + DBusIntrospectProperty('Metadata', DBusSignature('a{sv}'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('Volume', DBusSignature('d'), + access: DBusPropertyAccess.readwrite), + DBusIntrospectProperty('Position', DBusSignature('x'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('MinimumRate', DBusSignature('d'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('MaximumRate', DBusSignature('d'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanGoNext', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanGoPrevious', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanPlay', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanPause', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanSeek', DBusSignature('b'), + access: DBusPropertyAccess.read), + DBusIntrospectProperty('CanControl', DBusSignature('b'), + access: DBusPropertyAccess.read) + ]) + ]; + } + + @override + Future handleMethodCall(DBusMethodCall methodCall) async { + if (methodCall.interface == 'org.mpris.MediaPlayer2.Player') { + if (methodCall.name == 'Next') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doNext(); + } else if (methodCall.name == 'Previous') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doPrevious(); + } else if (methodCall.name == 'Pause') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doPause(); + } else if (methodCall.name == 'PlayPause') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doPlayPause(); + } else if (methodCall.name == 'Stop') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doStop(); + } else if (methodCall.name == 'Play') { + if (methodCall.values.isNotEmpty) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doPlay(); + } else if (methodCall.name == 'Seek') { + if (methodCall.signature != DBusSignature('x')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doSeek((methodCall.values[0] as DBusInt64).value); + } else if (methodCall.name == 'SetPosition') { + if (methodCall.signature != DBusSignature('ox')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doSetPosition((methodCall.values[0] as DBusObjectPath).value, + (methodCall.values[1] as DBusInt64).value); + } else if (methodCall.name == 'OpenUri') { + if (methodCall.signature != DBusSignature('s')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return doOpenUri((methodCall.values[0] as DBusString).value); + } else { + return DBusMethodErrorResponse.unknownMethod(); + } + } else { + return DBusMethodErrorResponse.unknownInterface(); + } + } + + @override + Future getProperty(String interface, String name) async { + if (interface == 'org.mpris.MediaPlayer2.Player') { + if (name == 'PlaybackStatus') { + return getPlaybackStatus(); + } else if (name == 'LoopStatus') { + return getLoopStatus(); + } else if (name == 'Rate') { + return getRate(); + } else if (name == 'Shuffle') { + return getShuffle(); + } else if (name == 'Metadata') { + return getMetadata(); + } else if (name == 'Volume') { + return getVolume(); + } else if (name == 'Position') { + return getPosition(); + } else if (name == 'MinimumRate') { + return getMinimumRate(); + } else if (name == 'MaximumRate') { + return getMaximumRate(); + } else if (name == 'CanGoNext') { + return getCanGoNext(); + } else if (name == 'CanGoPrevious') { + return getCanGoPrevious(); + } else if (name == 'CanPlay') { + return getCanPlay(); + } else if (name == 'CanPause') { + return getCanPause(); + } else if (name == 'CanSeek') { + return getCanSeek(); + } else if (name == 'CanControl') { + return getCanControl(); + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } + + @override + Future setProperty( + String interface, String name, DBusValue value) async { + if (interface == 'org.mpris.MediaPlayer2.Player') { + if (name == 'PlaybackStatus') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'LoopStatus') { + if (value.signature != DBusSignature('s')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return setLoopStatus((value as DBusString).value); + } else if (name == 'Rate') { + if (value.signature != DBusSignature('d')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return setRate((value as DBusDouble).value); + } else if (name == 'Shuffle') { + if (value.signature != DBusSignature('b')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return setShuffle((value as DBusBoolean).value); + } else if (name == 'Metadata') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'Volume') { + if (value.signature != DBusSignature('d')) { + return DBusMethodErrorResponse.invalidArgs(); + } + return setVolume((value as DBusDouble).value); + } else if (name == 'Position') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'MinimumRate') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'MaximumRate') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanGoNext') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanGoPrevious') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanPlay') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanPause') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanSeek') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else if (name == 'CanControl') { + return DBusMethodErrorResponse.propertyReadOnly(); + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } else { + return DBusMethodErrorResponse.unknownProperty(); + } + } + + @override + Future getAllProperties(String interface) async { + var properties = {}; + if (interface == 'org.mpris.MediaPlayer2.Player') { + properties['PlaybackStatus'] = + (await getPlaybackStatus()).returnValues[0]; + properties['LoopStatus'] = (await getLoopStatus()).returnValues[0]; + properties['Rate'] = (await getRate()).returnValues[0]; + properties['Shuffle'] = (await getShuffle()).returnValues[0]; + properties['Metadata'] = (await getMetadata()).returnValues[0]; + properties['Volume'] = (await getVolume()).returnValues[0]; + properties['Position'] = (await getPosition()).returnValues[0]; + properties['MinimumRate'] = (await getMinimumRate()).returnValues[0]; + properties['MaximumRate'] = (await getMaximumRate()).returnValues[0]; + properties['CanGoNext'] = (await getCanGoNext()).returnValues[0]; + properties['CanGoPrevious'] = (await getCanGoPrevious()).returnValues[0]; + properties['CanPlay'] = (await getCanPlay()).returnValues[0]; + properties['CanPause'] = (await getCanPause()).returnValues[0]; + properties['CanSeek'] = (await getCanSeek()).returnValues[0]; + properties['CanControl'] = (await getCanControl()).returnValues[0]; + } + return DBusMethodSuccessResponse([DBusDict.stringVariant(properties)]); + } +} diff --git a/lib/main.dart b/lib/main.dart index 97dd971e..9f3d97ff 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,17 @@ -import 'dart:io'; - import 'package:audio_service/audio_service.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart'; +import 'package:dbus/dbus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:spotube/entities/CacheTrack.dart'; +import 'package:spotube/interfaces/media_player2.dart'; import 'package:spotube/models/GoRouteDeclarations.dart'; import 'package:spotube/models/Logger.dart'; import 'package:spotube/provider/AudioPlayer.dart'; +import 'package:spotube/provider/DBus.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/UserPreferences.dart'; import 'package:spotube/provider/YouTube.dart'; @@ -34,7 +34,8 @@ void main() async { ); if (kIsDesktop) { WidgetsFlutterBinding.ensureInitialized(); - await hotKeyManager.unregisterAll(); + // final client = DBusClient.session(); + // await client.registerObject(Media_Player()); doWhenWindowReady(() { appWindow.minSize = const Size(359, 700); appWindow.alignment = Alignment.center; @@ -49,10 +50,12 @@ void main() async { playbackProvider.overrideWithProvider(ChangeNotifierProvider( (ref) { final youtube = ref.watch(youtubeProvider); + final dbus = ref.watch(dbusClientProvider); return Playback( player: audioPlayerHandler, youtube: youtube, ref: ref, + dbus: dbus, ); }, )) diff --git a/lib/models/GlobalKeyActions.dart b/lib/models/GlobalKeyActions.dart deleted file mode 100644 index b4348539..00000000 --- a/lib/models/GlobalKeyActions.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:hotkey_manager/hotkey_manager.dart'; - -class GlobalKeyActions { - late final HotKey hotKey; - late final Function(HotKey hotKey) onKeyDown; - GlobalKeyActions(this.hotKey, this.onKeyDown); -} diff --git a/lib/models/GoRouteDeclarations.dart b/lib/models/GoRouteDeclarations.dart index 75666930..894a1d97 100644 --- a/lib/models/GoRouteDeclarations.dart +++ b/lib/models/GoRouteDeclarations.dart @@ -24,8 +24,8 @@ GoRouter createGoRouter() => GoRouter( ), GoRoute( path: "/settings", - pageBuilder: (context, state) => SpotubePage( - child: const Settings(), + pageBuilder: (context, state) => const SpotubePage( + child: Settings(), ), ), GoRoute( diff --git a/lib/provider/DBus.dart b/lib/provider/DBus.dart new file mode 100644 index 00000000..a0b0e942 --- /dev/null +++ b/lib/provider/DBus.dart @@ -0,0 +1,6 @@ +import 'package:dbus/dbus.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final dbusClientProvider = Provider((ref) { + return DBusClient.session(); +}); diff --git a/lib/provider/Playback.dart b/lib/provider/Playback.dart index 2475588c..30efe227 100644 --- a/lib/provider/Playback.dart +++ b/lib/provider/Playback.dart @@ -1,7 +1,10 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:audio_service/audio_service.dart'; +import 'package:dbus/dbus.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive/hive.dart'; import 'package:just_audio/just_audio.dart'; @@ -10,8 +13,11 @@ import 'package:spotube/entities/CacheTrack.dart'; import 'package:spotube/helpers/artist-to-string.dart'; import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/search-youtube.dart'; +import 'package:spotube/interfaces/media_player2.dart'; +import 'package:spotube/interfaces/media_player2_player.dart'; import 'package:spotube/models/CurrentPlaylist.dart'; import 'package:spotube/models/Logger.dart'; +import 'package:spotube/provider/DBus.dart'; import 'package:spotube/provider/UserPreferences.dart'; import 'package:spotube/provider/YouTube.dart'; import 'package:spotube/utils/AudioPlayerHandler.dart'; @@ -37,15 +43,23 @@ class Playback extends PersistedChangeNotifier { LazyBox? cacheTrackBox; + @protected + final DBusClient dbus; + final Media_Player _media_player; + late final Player_Interface _mpris; + Playback({ required this.player, required this.youtube, required this.ref, + required this.dbus, CurrentPlaylist? currentPlaylist, Track? currentTrack, }) : _currentPlaylist = currentPlaylist, _currentTrack = currentTrack, + _media_player = Media_Player(), super() { + _mpris = Player_Interface(player: player.core, playback: this); player.onNextRequest = () { movePlaylistPositionBy(1); }; @@ -61,6 +75,19 @@ class Playback extends PersistedChangeNotifier { StreamSubscription? _playingStream; void _init() async { + // dbus m.p.r.i.s stuff + try { + final nameStatus = + await dbus.requestName("org.mpris.MediaPlayer2.spotube"); + if (nameStatus == DBusRequestNameReply.exists) { + await dbus.requestName("org.mpris.MediaPlayer2.spotube.instance$pid"); + } + await dbus.registerObject(_media_player); + await dbus.registerObject(_mpris); + } catch (e) { + logger.e("[MPRIS initialization error]", e); + } + cacheTrackBox = await Hive.openLazyBox("track-cache"); _playingStream = player.core.playingStream.listen( @@ -118,6 +145,8 @@ class Playback extends PersistedChangeNotifier { _playingStream?.cancel(); _durationStream?.cancel(); cacheTrackBox?.close(); + dbus.unregisterObject(_media_player); + dbus.unregisterObject(_mpris); super.dispose(); } @@ -299,9 +328,11 @@ class Playback extends PersistedChangeNotifier { final playbackProvider = ChangeNotifierProvider((ref) { final player = AudioPlayerHandler(); final youtube = ref.watch(youtubeProvider); + final dbus = ref.watch(dbusClientProvider); return Playback( player: player, youtube: youtube, ref: ref, + dbus: dbus, ); }); diff --git a/lib/provider/UserPreferences.dart b/lib/provider/UserPreferences.dart index 55780d7e..be069305 100644 --- a/lib/provider/UserPreferences.dart +++ b/lib/provider/UserPreferences.dart @@ -1,9 +1,7 @@ import 'dart:async'; -import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:spotube/components/Settings/ColorSchemePickerDialog.dart'; import 'package:spotube/helpers/get-random-element.dart'; import 'package:spotube/helpers/search-youtube.dart'; @@ -18,9 +16,6 @@ class UserPreferences extends PersistedChangeNotifier { String recommendationMarket; bool saveTrackLyrics; String geniusAccessToken; - HotKey? nextTrackHotKey; - HotKey? prevTrackHotKey; - HotKey? playPauseHotKey; bool checkUpdate; SpotubeTrackMatchAlgorithm trackMatchAlgorithm; AudioQuality audioQuality; @@ -35,9 +30,6 @@ class UserPreferences extends PersistedChangeNotifier { this.saveTrackLyrics = false, this.accentColorScheme = Colors.green, this.backgroundColorScheme = Colors.grey, - this.nextTrackHotKey, - this.prevTrackHotKey, - this.playPauseHotKey, this.checkUpdate = true, this.trackMatchAlgorithm = SpotubeTrackMatchAlgorithm.authenticPopular, this.audioQuality = AudioQuality.high, @@ -67,24 +59,6 @@ class UserPreferences extends PersistedChangeNotifier { updatePersistence(); } - void setNextTrackHotKey(HotKey? value) { - nextTrackHotKey = value; - notifyListeners(); - updatePersistence(); - } - - void setPrevTrackHotKey(HotKey? value) { - prevTrackHotKey = value; - notifyListeners(); - updatePersistence(); - } - - void setPlayPauseHotKey(HotKey? value) { - playPauseHotKey = value; - notifyListeners(); - updatePersistence(); - } - void setYtSearchFormat(String format) { ytSearchFormat = format; notifyListeners(); @@ -128,15 +102,7 @@ class UserPreferences extends PersistedChangeNotifier { checkUpdate = map["checkUpdate"] ?? checkUpdate; geniusAccessToken = map["geniusAccessToken"] ?? getRandomElement(lyricsSecrets); - nextTrackHotKey = map["nextTrackHotKey"] != null - ? HotKey.fromJson(jsonDecode(map["nextTrackHotKey"])) - : null; - prevTrackHotKey = map["prevTrackHotKey"] != null - ? HotKey.fromJson(jsonDecode(map["prevTrackHotKey"])) - : null; - playPauseHotKey = map["playPauseHotKey"] != null - ? HotKey.fromJson(jsonDecode(map["playPauseHotKey"])) - : null; + ytSearchFormat = map["ytSearchFormat"] ?? ytSearchFormat; themeMode = ThemeMode.values[map["themeMode"] ?? 0]; backgroundColorScheme = colorsMap.values @@ -159,15 +125,6 @@ class UserPreferences extends PersistedChangeNotifier { "saveTrackLyrics": saveTrackLyrics, "recommendationMarket": recommendationMarket, "geniusAccessToken": geniusAccessToken, - "nextTrackHotKey": nextTrackHotKey != null - ? jsonEncode(nextTrackHotKey?.toJson()) - : null, - "prevTrackHotKey": prevTrackHotKey != null - ? jsonEncode(prevTrackHotKey?.toJson()) - : null, - "playPauseHotKey": playPauseHotKey != null - ? jsonEncode(playPauseHotKey?.toJson()) - : null, "ytSearchFormat": ytSearchFormat, "themeMode": themeMode.index, "backgroundColorScheme": backgroundColorScheme.value, diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index f914b16d..a4c04a0f 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,7 +7,6 @@ #include "generated_plugin_registrant.h" #include -#include #include #include @@ -15,9 +14,6 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); - g_autoptr(FlPluginRegistrar) hotkey_manager_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "HotkeyManagerPlugin"); - hotkey_manager_plugin_register_with_registrar(hotkey_manager_registrar); g_autoptr(FlPluginRegistrar) libwinmedia_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "LibwinmediaPlugin"); libwinmedia_plugin_register_with_registrar(libwinmedia_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 5553b585..51b46f6e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,7 +4,6 @@ list(APPEND FLUTTER_PLUGIN_LIST bitsdojo_window_linux - hotkey_manager libwinmedia url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index fc68c0c2..435c9977 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,7 +8,6 @@ import Foundation import audio_service import audio_session import bitsdojo_window_macos -import hotkey_manager import just_audio import package_info_plus_macos import path_provider_macos @@ -20,7 +19,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) - HotkeyManagerPlugin.register(with: registry.registrar(forPlugin: "HotkeyManagerPlugin")) JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index f0519b87..1c212924 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -267,6 +267,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + dbus: + dependency: "direct main" + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.3" fading_edge_scrollview: dependency: transitive description: @@ -429,13 +436,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" - hotkey_manager: - dependency: "direct main" - description: - name: hotkey_manager - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.7" html: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 730394a8..2f3a3c38 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,6 @@ dependencies: url_launcher: ^6.0.17 youtube_explode_dart: ^1.10.8 bitsdojo_window: ^0.1.2 - hotkey_manager: ^0.1.6 just_audio: ^0.9.18 just_audio_libwinmedia: ^0.0.4 path: ^1.8.0 @@ -64,6 +63,7 @@ dependencies: skeleton_text: ^3.0.0 hive: ^2.2.2 hive_flutter: ^1.1.0 + dbus: ^0.7.3 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index e52f4036..a9dc0ec8 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,7 +7,6 @@ #include "generated_plugin_registrant.h" #include -#include #include #include #include @@ -15,8 +14,6 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { BitsdojoWindowPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); - HotkeyManagerPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("HotkeyManagerPlugin")); LibwinmediaPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("LibwinmediaPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 2019ee88..44cee163 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,7 +4,6 @@ list(APPEND FLUTTER_PLUGIN_LIST bitsdojo_window_windows - hotkey_manager libwinmedia permission_handler_windows url_launcher_windows