mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
Global custom hotkey support for playback control
UserPreferences provider genius access token not loading on init bug fix
This commit is contained in:
parent
b378375b19
commit
e666e25ffd
@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/Shared/DownloadTrackButton.dart';
|
import 'package:spotube/components/Shared/DownloadTrackButton.dart';
|
||||||
@ -9,7 +8,6 @@ import 'package:spotube/components/Player/PlayerControls.dart';
|
|||||||
import 'package:spotube/helpers/artists-to-clickable-artists.dart';
|
import 'package:spotube/helpers/artists-to-clickable-artists.dart';
|
||||||
import 'package:spotube/helpers/image-to-url-string.dart';
|
import 'package:spotube/helpers/image-to-url-string.dart';
|
||||||
import 'package:spotube/helpers/search-youtube.dart';
|
import 'package:spotube/helpers/search-youtube.dart';
|
||||||
import 'package:spotube/models/GlobalKeyActions.dart';
|
|
||||||
import 'package:spotube/provider/Playback.dart';
|
import 'package:spotube/provider/Playback.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -35,20 +33,13 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
late YoutubeExplode youtube;
|
late YoutubeExplode youtube;
|
||||||
|
|
||||||
late List<GlobalKeyActions> _hotKeys;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
try {
|
try {
|
||||||
super.initState();
|
super.initState();
|
||||||
player = AudioPlayer();
|
player = AudioPlayer();
|
||||||
youtube = YoutubeExplode();
|
youtube = YoutubeExplode();
|
||||||
_hotKeys = [
|
|
||||||
GlobalKeyActions(
|
|
||||||
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
|
|
||||||
_playOrPause,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
WidgetsBinding.instance?.addObserver(this);
|
WidgetsBinding.instance?.addObserver(this);
|
||||||
WidgetsBinding.instance?.addPostFrameCallback(_init);
|
WidgetsBinding.instance?.addPostFrameCallback(_init);
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
@ -96,15 +87,6 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
|
|||||||
print(stack);
|
print(stack);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await Future.wait(
|
|
||||||
_hotKeys.map((e) {
|
|
||||||
return hotKeyManager.register(
|
|
||||||
e.hotKey,
|
|
||||||
keyDownHandler: e.onKeyDown,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("[Player._init()]: $e");
|
print("[Player._init()]: $e");
|
||||||
}
|
}
|
||||||
@ -115,7 +97,6 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
|
|||||||
WidgetsBinding.instance?.removeObserver(this);
|
WidgetsBinding.instance?.removeObserver(this);
|
||||||
player.dispose();
|
player.dispose();
|
||||||
youtube.close();
|
youtube.close();
|
||||||
Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e.hotKey)));
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,15 +110,6 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_playOrPause(key) async {
|
|
||||||
try {
|
|
||||||
_isPlaying ? await player.pause() : await player.play();
|
|
||||||
} catch (e, stack) {
|
|
||||||
print("[PlayPauseShortcut] $e");
|
|
||||||
print(stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _movePlaylistPositionBy(int pos) {
|
void _movePlaylistPositionBy(int pos) {
|
||||||
Playback playback = context.read<Playback>();
|
Playback playback = context.read<Playback>();
|
||||||
if (playback.currentTrack != null && playback.currentPlaylist != null) {
|
if (playback.currentTrack != null && playback.currentPlaylist != null) {
|
||||||
@ -198,6 +170,29 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onNext() async {
|
||||||
|
try {
|
||||||
|
await player.pause();
|
||||||
|
await player.seek(Duration.zero);
|
||||||
|
_movePlaylistPositionBy(1);
|
||||||
|
print("ON NEXT");
|
||||||
|
} catch (e, stack) {
|
||||||
|
print("[PlayerControls.onNext()] $e");
|
||||||
|
print(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPrevious() async {
|
||||||
|
try {
|
||||||
|
await player.pause();
|
||||||
|
await player.seek(Duration.zero);
|
||||||
|
_movePlaylistPositionBy(-1);
|
||||||
|
} catch (e, stack) {
|
||||||
|
print("[PlayerControls.onPrevious()] $e");
|
||||||
|
print(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -256,26 +251,8 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
|
|||||||
isPlaying: _isPlaying,
|
isPlaying: _isPlaying,
|
||||||
duration: _duration ?? Duration.zero,
|
duration: _duration ?? Duration.zero,
|
||||||
shuffled: _shuffled,
|
shuffled: _shuffled,
|
||||||
onNext: () async {
|
onNext: _onNext,
|
||||||
try {
|
onPrevious: _onPrevious,
|
||||||
await player.pause();
|
|
||||||
await player.seek(Duration.zero);
|
|
||||||
_movePlaylistPositionBy(1);
|
|
||||||
} catch (e, stack) {
|
|
||||||
print("[PlayerControls.onNext()] $e");
|
|
||||||
print(stack);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPrevious: () async {
|
|
||||||
try {
|
|
||||||
await player.pause();
|
|
||||||
await player.seek(Duration.zero);
|
|
||||||
_movePlaylistPositionBy(-1);
|
|
||||||
} catch (e, stack) {
|
|
||||||
print("[PlayerControls.onPrevious()] $e");
|
|
||||||
print(stack);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPause: () async {
|
onPause: () async {
|
||||||
try {
|
try {
|
||||||
await player.pause();
|
await player.pause();
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:spotube/helpers/zero-pad-num-str.dart';
|
import 'package:spotube/helpers/zero-pad-num-str.dart';
|
||||||
|
import 'package:spotube/models/GlobalKeyActions.dart';
|
||||||
|
import 'package:spotube/provider/UserPreferences.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class PlayerControls extends StatefulWidget {
|
class PlayerControls extends StatefulWidget {
|
||||||
final Stream<Duration> positionStream;
|
final Stream<Duration> positionStream;
|
||||||
@ -36,15 +40,57 @@ class PlayerControls extends StatefulWidget {
|
|||||||
|
|
||||||
class _PlayerControlsState extends State<PlayerControls> {
|
class _PlayerControlsState extends State<PlayerControls> {
|
||||||
StreamSubscription? _timePositionListener;
|
StreamSubscription? _timePositionListener;
|
||||||
|
late List<GlobalKeyActions> _hotKeys = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() async {
|
void dispose() async {
|
||||||
await _timePositionListener?.cancel();
|
await _timePositionListener?.cancel();
|
||||||
|
Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e.hotKey)));
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_playOrPause(key) async {
|
||||||
|
try {
|
||||||
|
widget.isPlaying ? widget.onPause?.call() : await widget.onPlay?.call();
|
||||||
|
} catch (e, stack) {
|
||||||
|
print("[PlayPauseShortcut] $e");
|
||||||
|
print(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_configureHotKeys(UserPreferences preferences) async {
|
||||||
|
await Future.wait(_hotKeys.map((e) => hotKeyManager.unregister(e.hotKey)))
|
||||||
|
.then((val) async {
|
||||||
|
_hotKeys = [
|
||||||
|
GlobalKeyActions(
|
||||||
|
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
|
||||||
|
_playOrPause,
|
||||||
|
),
|
||||||
|
if (preferences.nextTrackHotKey != null)
|
||||||
|
GlobalKeyActions(
|
||||||
|
preferences.nextTrackHotKey!, (key) => widget.onNext?.call()),
|
||||||
|
if (preferences.prevTrackHotKey != null)
|
||||||
|
GlobalKeyActions(
|
||||||
|
preferences.prevTrackHotKey!, (key) => widget.onPrevious?.call()),
|
||||||
|
if (preferences.playPauseHotKey != null)
|
||||||
|
GlobalKeyActions(preferences.playPauseHotKey!, _playOrPause)
|
||||||
|
];
|
||||||
|
await Future.wait(
|
||||||
|
_hotKeys.map((e) {
|
||||||
|
return hotKeyManager.register(
|
||||||
|
e.hotKey,
|
||||||
|
keyDownHandler: e.onKeyDown,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
UserPreferences preferences = context.watch<UserPreferences>();
|
||||||
|
_configureHotKeys(preferences);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 700),
|
constraints: const BoxConstraints(maxWidth: 700),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -103,16 +149,13 @@ class _PlayerControlsState extends State<PlayerControls> {
|
|||||||
widget.onPrevious?.call();
|
widget.onPrevious?.call();
|
||||||
}),
|
}),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
widget.isPlaying
|
widget.isPlaying
|
||||||
? Icons.pause_rounded
|
? Icons.pause_rounded
|
||||||
: Icons.play_arrow_rounded,
|
: Icons.play_arrow_rounded,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () => _playOrPause(null),
|
||||||
widget.isPlaying
|
),
|
||||||
? widget.onPause?.call()
|
|
||||||
: widget.onPlay?.call();
|
|
||||||
}),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.skip_next_rounded),
|
icon: const Icon(Icons.skip_next_rounded),
|
||||||
onPressed: () => widget.onNext?.call()),
|
onPressed: () => widget.onNext?.call()),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:spotube/components/Settings/SettingsHotkeyTile.dart';
|
||||||
import 'package:spotube/components/Shared/Hyperlink.dart';
|
import 'package:spotube/components/Shared/Hyperlink.dart';
|
||||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||||
import 'package:spotube/main.dart';
|
import 'package:spotube/main.dart';
|
||||||
@ -94,6 +96,27 @@ class _SettingsState extends State<Settings> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -167,7 +190,7 @@ class _SettingsState extends State<Settings> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: const [
|
children: const [
|
||||||
Hyperlink(
|
Hyperlink(
|
||||||
"Sponsor/Donate",
|
"💚 Sponsor/Donate 💚",
|
||||||
"https://opencollective.com/spotube",
|
"https://opencollective.com/spotube",
|
||||||
),
|
),
|
||||||
Text(" • "),
|
Text(" • "),
|
||||||
|
48
lib/components/Settings/SettingsHotkeyTile.dart
Normal file
48
lib/components/Settings/SettingsHotkeyTile.dart
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
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<HotKey> onHotKeyRecorded;
|
||||||
|
const SettingsHotKeyTile({
|
||||||
|
required this.onHotKeyRecorded,
|
||||||
|
required this.title,
|
||||||
|
Key? key,
|
||||||
|
this.currentHotKey,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(title),
|
||||||
|
Row(
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
91
lib/components/Shared/RecordHotKeyDialog.dart
Normal file
91
lib/components/Shared/RecordHotKeyDialog.dart
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
|
|
||||||
|
class RecordHotKeyDialog extends StatefulWidget {
|
||||||
|
final ValueChanged<HotKey> onHotKeyRecorded;
|
||||||
|
|
||||||
|
const RecordHotKeyDialog({
|
||||||
|
Key? key,
|
||||||
|
required this.onHotKeyRecorded,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_RecordHotKeyDialogState createState() => _RecordHotKeyDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecordHotKeyDialogState extends State<RecordHotKeyDialog> {
|
||||||
|
HotKey _hotKey = HotKey(null);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: ListBody(
|
||||||
|
children: <Widget>[
|
||||||
|
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) {
|
||||||
|
setState(() {
|
||||||
|
_hotKey = hotKey;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('OK'),
|
||||||
|
onPressed: !_hotKey.isSetted
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
widget.onHotKeyRecorded(_hotKey);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,4 +7,7 @@ abstract class LocalStorageKeys {
|
|||||||
static String geniusAccessToken = "genius_access_token";
|
static String geniusAccessToken = "genius_access_token";
|
||||||
|
|
||||||
static String themeMode = "theme_mode";
|
static String themeMode = "theme_mode";
|
||||||
|
static String nextTrackHotKey = "next_track_hot_key";
|
||||||
|
static String prevTrackHotKey = "prev_track_hot_key";
|
||||||
|
static String playPauseHotKey = "play_pause_hot_key";
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,100 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||||
|
|
||||||
class UserPreferences extends ChangeNotifier {
|
class UserPreferences extends ChangeNotifier {
|
||||||
String? _geniusAccessToken;
|
String? geniusAccessToken;
|
||||||
UserPreferences({String? geniusAccessToken}) {
|
HotKey? nextTrackHotKey;
|
||||||
if (geniusAccessToken == null) {
|
HotKey? prevTrackHotKey;
|
||||||
SharedPreferences.getInstance().then((localStorage) {
|
HotKey? playPauseHotKey;
|
||||||
String? accessToken =
|
UserPreferences({
|
||||||
localStorage.getString(LocalStorageKeys.geniusAccessToken);
|
this.nextTrackHotKey,
|
||||||
_geniusAccessToken ??= accessToken;
|
this.prevTrackHotKey,
|
||||||
});
|
this.playPauseHotKey,
|
||||||
} else {
|
this.geniusAccessToken,
|
||||||
_geniusAccessToken = geniusAccessToken;
|
}) {
|
||||||
|
onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<HotKey?> _getHotKeyFromLocalStorage(
|
||||||
|
SharedPreferences preferences, String key) async {
|
||||||
|
String? str = preferences.getString(key);
|
||||||
|
if (str != null) {
|
||||||
|
Map<String, dynamic> json = await jsonDecode(str);
|
||||||
|
if (json.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return HotKey.fromJson(json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? get geniusAccessToken => _geniusAccessToken;
|
onInit() async {
|
||||||
|
try {
|
||||||
|
SharedPreferences localStorage = await SharedPreferences.getInstance();
|
||||||
|
String? accessToken =
|
||||||
|
localStorage.getString(LocalStorageKeys.geniusAccessToken);
|
||||||
|
|
||||||
|
geniusAccessToken ??= accessToken;
|
||||||
|
|
||||||
|
nextTrackHotKey ??= await _getHotKeyFromLocalStorage(
|
||||||
|
localStorage,
|
||||||
|
LocalStorageKeys.nextTrackHotKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
prevTrackHotKey ??= await _getHotKeyFromLocalStorage(
|
||||||
|
localStorage,
|
||||||
|
LocalStorageKeys.prevTrackHotKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
playPauseHotKey ??= await _getHotKeyFromLocalStorage(
|
||||||
|
localStorage,
|
||||||
|
LocalStorageKeys.playPauseHotKey,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e, stack) {
|
||||||
|
print("[UserPreferences.onInit]: $e");
|
||||||
|
print(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setGeniusAccessToken(String? token) {
|
setGeniusAccessToken(String? token) {
|
||||||
_geniusAccessToken = token;
|
geniusAccessToken = token;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setNextTrackHotKey(HotKey? value) {
|
||||||
|
nextTrackHotKey = value;
|
||||||
|
SharedPreferences.getInstance().then((preferences) {
|
||||||
|
preferences.setString(
|
||||||
|
LocalStorageKeys.nextTrackHotKey,
|
||||||
|
jsonEncode(value?.toJson() ?? {}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrevTrackHotKey(HotKey? value) {
|
||||||
|
prevTrackHotKey = value;
|
||||||
|
SharedPreferences.getInstance().then((preferences) {
|
||||||
|
preferences.setString(
|
||||||
|
LocalStorageKeys.prevTrackHotKey,
|
||||||
|
jsonEncode(value?.toJson() ?? {}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlayPauseHotKey(HotKey? value) {
|
||||||
|
playPauseHotKey = value;
|
||||||
|
SharedPreferences.getInstance().then((preferences) {
|
||||||
|
preferences.setString(
|
||||||
|
LocalStorageKeys.playPauseHotKey,
|
||||||
|
jsonEncode(value?.toJson() ?? {}),
|
||||||
|
);
|
||||||
|
});
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user