mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: android audio service and notification and fallback for lyrics when anonymous
This commit is contained in:
parent
f7b12924db
commit
f160ec767d
@ -5,12 +5,14 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:platform_ui/platform_ui.dart';
|
||||
import 'package:spotube/collections/spotube_icons.dart';
|
||||
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/components/shared/image/universal_image.dart';
|
||||
import 'package:spotube/hooks/use_custom_status_bar_color.dart';
|
||||
import 'package:spotube/hooks/use_palette_color.dart';
|
||||
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
|
||||
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/playlist_queue_provider.dart';
|
||||
import 'package:spotube/utils/platform.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
@ -39,6 +41,17 @@ class LyricsPage extends HookConsumerWidget {
|
||||
noSetBGColor: true,
|
||||
);
|
||||
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
|
||||
if (auth == null) {
|
||||
return PlatformScaffold(
|
||||
appBar: !kIsMacOS && platform != TargetPlatform.windows && !isModal
|
||||
? PageWindowTitleBar()
|
||||
: null,
|
||||
body: const AnonymousFallback(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget body = [
|
||||
SyncedLyrics(palette: palette, isModal: isModal),
|
||||
PlainLyrics(palette: palette, isModal: isModal),
|
||||
|
@ -18,6 +18,7 @@ import 'package:spotube/hooks/use_custom_status_bar_color.dart';
|
||||
import 'package:spotube/hooks/use_palette_color.dart';
|
||||
import 'package:spotube/models/local_track.dart';
|
||||
import 'package:spotube/pages/lyrics/lyrics.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/playlist_queue_provider.dart';
|
||||
import 'package:spotube/provider/user_preferences_provider.dart';
|
||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||
@ -29,6 +30,7 @@ class PlayerView extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
final currentTrack = ref.watch(PlaylistQueueNotifier.provider.select(
|
||||
(value) => value?.activeTrack,
|
||||
));
|
||||
@ -104,11 +106,13 @@ class PlayerView extends HookConsumerWidget {
|
||||
height: 30,
|
||||
child: SpotubeMarqueeText(
|
||||
text: currentTrack?.name ?? "Not playing",
|
||||
style:
|
||||
Theme.of(context).textTheme.headline5?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: paletteColor.titleTextColor,
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: paletteColor.titleTextColor,
|
||||
),
|
||||
isHovering: true,
|
||||
),
|
||||
),
|
||||
@ -117,20 +121,24 @@ class PlayerView extends HookConsumerWidget {
|
||||
TypeConversionUtils.artists_X_String<Artist>(
|
||||
currentTrack?.artists ?? [],
|
||||
),
|
||||
style:
|
||||
Theme.of(context).textTheme.headline6!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: paletteColor.bodyTextColor,
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge!
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: paletteColor.bodyTextColor,
|
||||
),
|
||||
)
|
||||
else
|
||||
TypeConversionUtils.artists_X_ClickableArtists(
|
||||
currentTrack?.artists ?? [],
|
||||
textStyle:
|
||||
Theme.of(context).textTheme.headline6!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: paletteColor.bodyTextColor,
|
||||
),
|
||||
textStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge!
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: paletteColor.bodyTextColor,
|
||||
),
|
||||
onRouteChange: (route) {
|
||||
GoRouter.of(context).pop();
|
||||
GoRouter.of(context).push(route);
|
||||
@ -193,32 +201,33 @@ class PlayerView extends HookConsumerWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
floatingQueue: false,
|
||||
extraActions: [
|
||||
PlatformIconButton(
|
||||
tooltip: "Open Lyrics",
|
||||
icon: const Icon(SpotubeIcons.music),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isDismissible: true,
|
||||
enableDrag: true,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.black38,
|
||||
barrierColor: Colors.black12,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
if (auth != null)
|
||||
PlatformIconButton(
|
||||
tooltip: "Open Lyrics",
|
||||
icon: const Icon(SpotubeIcons.music),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isDismissible: true,
|
||||
enableDrag: true,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.black38,
|
||||
barrierColor: Colors.black12,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(
|
||||
maxHeight:
|
||||
MediaQuery.of(context).size.height * 0.8,
|
||||
),
|
||||
builder: (context) =>
|
||||
const LyricsPage(isModal: true),
|
||||
);
|
||||
},
|
||||
)
|
||||
constraints: BoxConstraints(
|
||||
maxHeight:
|
||||
MediaQuery.of(context).size.height * 0.8,
|
||||
),
|
||||
builder: (context) =>
|
||||
const LyricsPage(isModal: true),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
PlayerControls(iconColor: paletteColor.bodyTextColor),
|
||||
|
@ -10,15 +10,9 @@ import 'package:spotube/components/root/sidebar.dart';
|
||||
import 'package:spotube/components/root/spotube_navigation_bar.dart';
|
||||
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||
import 'package:spotube/hooks/use_update_checker.dart';
|
||||
import 'package:spotube/provider/authentication_provider.dart';
|
||||
import 'package:spotube/provider/downloader_provider.dart';
|
||||
|
||||
const rootPaths = {
|
||||
0: "/",
|
||||
1: "/search",
|
||||
2: "/library",
|
||||
3: "/lyrics",
|
||||
};
|
||||
|
||||
class RootApp extends HookConsumerWidget {
|
||||
final Widget child;
|
||||
const RootApp({
|
||||
@ -30,6 +24,14 @@ class RootApp extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, ref) {
|
||||
final index = useState(0);
|
||||
final isMounted = useIsMounted();
|
||||
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||
|
||||
final rootPaths = [
|
||||
"/",
|
||||
if (auth != null) "/search",
|
||||
"/library",
|
||||
if (auth != null) "/lyrics",
|
||||
].asMap();
|
||||
|
||||
final downloader = ref.watch(downloaderProvider);
|
||||
useEffect(() {
|
||||
|
@ -152,7 +152,10 @@ class PlaylistQueueNotifier extends PersistedStateNotifier<PlaylistQueue?> {
|
||||
void configure() async {
|
||||
if (kIsMobile || kIsMacOS) {
|
||||
mobileService = await AudioService.init(
|
||||
builder: () => MobileAudioService(this),
|
||||
builder: () => MobileAudioService(
|
||||
this,
|
||||
ref.read(VolumeProvider.provider.notifier),
|
||||
),
|
||||
config: const AudioServiceConfig(
|
||||
androidNotificationChannelId: 'com.krtirtho.Spotube',
|
||||
androidNotificationChannelName: 'Spotube',
|
||||
|
@ -9,22 +9,28 @@ import 'package:spotube/services/audio_player.dart';
|
||||
class MobileAudioService extends BaseAudioHandler {
|
||||
AudioSession? session;
|
||||
final PlaylistQueueNotifier playlistNotifier;
|
||||
final VolumeProvider volumeNotifier;
|
||||
|
||||
PlaylistQueue? get playlist => playlistNotifier.state;
|
||||
|
||||
MobileAudioService(this.playlistNotifier) {
|
||||
MobileAudioService(this.playlistNotifier, this.volumeNotifier) {
|
||||
AudioSession.instance.then((s) {
|
||||
session = s;
|
||||
session?.configure(const AudioSessionConfiguration.music());
|
||||
s.interruptionEventStream.listen((event) async {
|
||||
if (event.type != AudioInterruptionType.duck) {
|
||||
await playlistNotifier.pause();
|
||||
switch (event.type) {
|
||||
case AudioInterruptionType.duck:
|
||||
await volumeNotifier.setVolume(event.begin ? 0.5 : 1.0);
|
||||
break;
|
||||
case AudioInterruptionType.pause:
|
||||
case AudioInterruptionType.unknown:
|
||||
await playlistNotifier.pause();
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
audioPlayer.onPlayerStateChanged.listen((state) async {
|
||||
if (state != PlayerState.completed) {
|
||||
playbackState.add(await _transformEvent());
|
||||
}
|
||||
playbackState.add(await _transformEvent());
|
||||
});
|
||||
|
||||
audioPlayer.onPositionChanged.listen((pos) async {
|
||||
@ -46,6 +52,27 @@ class MobileAudioService extends BaseAudioHandler {
|
||||
@override
|
||||
Future<void> seek(Duration position) => playlistNotifier.seek(position);
|
||||
|
||||
@override
|
||||
Future<void> setShuffleMode(AudioServiceShuffleMode shuffleMode) async {
|
||||
await super.setShuffleMode(shuffleMode);
|
||||
|
||||
if (shuffleMode == AudioServiceShuffleMode.all) {
|
||||
playlistNotifier.shuffle();
|
||||
} else {
|
||||
playlistNotifier.unshuffle();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setRepeatMode(AudioServiceRepeatMode repeatMode) async {
|
||||
super.setRepeatMode(repeatMode);
|
||||
if (repeatMode == AudioServiceRepeatMode.all) {
|
||||
playlistNotifier.loop();
|
||||
} else {
|
||||
playlistNotifier.unloop();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
await playlistNotifier.stop();
|
||||
@ -71,6 +98,7 @@ class MobileAudioService extends BaseAudioHandler {
|
||||
}
|
||||
|
||||
Future<PlaybackState> _transformEvent() async {
|
||||
final position = (await audioPlayer.getCurrentPosition()) ?? Duration.zero;
|
||||
return PlaybackState(
|
||||
controls: [
|
||||
MediaControl.skipToPrevious,
|
||||
@ -85,12 +113,17 @@ class MobileAudioService extends BaseAudioHandler {
|
||||
},
|
||||
androidCompactActionIndices: const [0, 1, 2],
|
||||
playing: audioPlayer.state == PlayerState.playing,
|
||||
updatePosition: (await audioPlayer.getCurrentPosition()) ?? Duration.zero,
|
||||
processingState: audioPlayer.state == PlayerState.paused
|
||||
? AudioProcessingState.buffering
|
||||
: audioPlayer.state == PlayerState.playing
|
||||
? AudioProcessingState.ready
|
||||
: AudioProcessingState.idle,
|
||||
updatePosition: position,
|
||||
bufferedPosition: position,
|
||||
shuffleMode: playlist?.isShuffled == true
|
||||
? AudioServiceShuffleMode.all
|
||||
: AudioServiceShuffleMode.none,
|
||||
repeatMode: playlist?.isLooping == true
|
||||
? AudioServiceRepeatMode.one
|
||||
: AudioServiceRepeatMode.all,
|
||||
processingState: playlist?.isLoading == true
|
||||
? AudioProcessingState.loading
|
||||
: AudioProcessingState.ready,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user