From e6c7213067cc0717e4010cd7b072138cbaabc762 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sun, 4 Oct 2026 03:55:25 +0700 Subject: [PATCH] feat(player): show mixing status beside audio quality --- lib/screens/now_playing_screen.dart | 13 ++++++++--- lib/widgets/automix_status_badge.dart | 31 +++++++++++++++++++++++++++ test/automix_status_badge_test.dart | 31 +++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 lib/widgets/automix_status_badge.dart create mode 100644 test/automix_status_badge_test.dart diff --git a/lib/screens/now_playing_screen.dart b/lib/screens/now_playing_screen.dart index 3d6c23f2..97ee3de5 100644 --- a/lib/screens/now_playing_screen.dart +++ b/lib/screens/now_playing_screen.dart @@ -42,6 +42,7 @@ import 'package:spotiflac_android/widgets/expressive_icon_button.dart'; import 'package:spotiflac_android/widgets/aligned_lyric_pronunciation.dart'; import 'package:spotiflac_android/widgets/lyric_supplement_transition.dart'; import 'package:spotiflac_android/widgets/lyric_scroll_motion.dart'; +import 'package:spotiflac_android/widgets/automix_status_badge.dart'; import 'package:spotiflac_android/widgets/audio_quality_badges.dart'; import 'package:spotiflac_android/widgets/audio_output_button.dart'; import 'package:spotiflac_android/widgets/lyric_gap_indicator.dart'; @@ -2917,9 +2918,15 @@ class _PlaybackControls extends ConsumerWidget { Text(formatClock(elapsedSeconds), style: timeStyle), Expanded( child: Center( - child: _QualityBadge( - label: qualityLabel, - colorScheme: colorScheme, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _QualityBadge( + label: qualityLabel, + colorScheme: colorScheme, + ), + const AutoMixStatusBadge(), + ], ), ), ), diff --git a/lib/widgets/automix_status_badge.dart b/lib/widgets/automix_status_badge.dart new file mode 100644 index 00000000..b41080eb --- /dev/null +++ b/lib/widgets/automix_status_badge.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:spotiflac_android/l10n/l10n.dart'; +import 'package:spotiflac_android/services/automix_status.dart'; + +class AutoMixStatusBadge extends StatelessWidget { + const AutoMixStatusBadge({super.key}); + + @override + Widget build(BuildContext context) => ValueListenableBuilder( + valueListenable: autoMixStatus, + builder: (context, status, _) { + if (status == AutoMixStatus.idle) return const SizedBox.shrink(); + return Semantics( + liveRegion: true, + child: Padding( + padding: const EdgeInsets.only(top: 2), + child: Text( + status == AutoMixStatus.mixing + ? context.l10n.autoMixMixing + : context.l10n.autoMixCrossfading, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ); + }, + ); +} diff --git a/test/automix_status_badge_test.dart b/test/automix_status_badge_test.dart new file mode 100644 index 00000000..5ecc46d3 --- /dev/null +++ b/test/automix_status_badge_test.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:spotiflac_android/l10n/app_localizations.dart'; +import 'package:spotiflac_android/services/automix_status.dart'; +import 'package:spotiflac_android/widgets/automix_status_badge.dart'; + +void main() { + testWidgets( + 'badge follows actual transition state and disappears after cancellation', + (tester) async { + addTearDown(() => autoMixStatus.value = AutoMixStatus.idle); + await tester.pumpWidget( + const MaterialApp( + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: Scaffold(body: AutoMixStatusBadge()), + ), + ); + expect(find.text('Mixing'), findsNothing); + autoMixStatus.value = AutoMixStatus.mixing; + await tester.pump(); + expect(find.text('Mixing'), findsOneWidget); + autoMixStatus.value = AutoMixStatus.crossfading; + await tester.pump(); + expect(find.text('Crossfade'), findsOneWidget); + autoMixStatus.value = AutoMixStatus.idle; + await tester.pump(); + expect(find.text('Crossfade'), findsNothing); + }, + ); +}