mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-20 22:41:38 +02:00
feat(autoplay): enable personalization by default and confirm learning resets
This commit is contained in:
1 parent
befcd2adec
commit
21fd51d62c
5 files changed
+54
-23
No files matched your search
@@ -198,7 +198,7 @@ class AppSettings {
|
||||
this.usbAllowFixedVolume = false,
|
||||
this.dapExclusive = false,
|
||||
this.autoplay = false,
|
||||
this.autoplayPersonalization = false,
|
||||
this.autoplayPersonalization = true,
|
||||
this.isFirstLaunch = true,
|
||||
this.checkForUpdates = true,
|
||||
this.updateChannel = 'stable',
|
||||
|
||||
@@ -59,7 +59,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) => AppSettings(
|
||||
usbAllowFixedVolume: json['usbAllowFixedVolume'] as bool? ?? false,
|
||||
dapExclusive: json['dapExclusive'] as bool? ?? false,
|
||||
autoplay: json['autoplay'] as bool? ?? false,
|
||||
autoplayPersonalization: json['autoplayPersonalization'] as bool? ?? false,
|
||||
autoplayPersonalization: json['autoplayPersonalization'] as bool? ?? true,
|
||||
isFirstLaunch: json['isFirstLaunch'] as bool? ?? true,
|
||||
checkForUpdates: json['checkForUpdates'] as bool? ?? true,
|
||||
updateChannel: json['updateChannel'] as String? ?? 'stable',
|
||||
|
||||
@@ -47,6 +47,31 @@ class _PlaybackSettingsPageState extends ConsumerState<PlaybackSettingsPage> {
|
||||
false;
|
||||
}
|
||||
|
||||
/// Learning cannot be restored once cleared, so a stray tap must not wipe it.
|
||||
Future<void> _confirmResetLearning() async {
|
||||
final confirmed =
|
||||
await showAppDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AppAlertDialog(
|
||||
title: Text(context.l10n.autoplayResetLearningConfirmTitle),
|
||||
content: Text(context.l10n.autoplayResetLearningConfirmMessage),
|
||||
actions: [
|
||||
AppDialogAction(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: Text(context.l10n.dialogCancel),
|
||||
),
|
||||
AppDialogAction(
|
||||
isDestructive: true,
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: Text(context.l10n.autoplayResetLearningConfirm),
|
||||
),
|
||||
],
|
||||
),
|
||||
) ??
|
||||
false;
|
||||
if (confirmed) await resetAutoplayLearning();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settings = ref.watch(settingsProvider);
|
||||
@@ -160,7 +185,7 @@ class _PlaybackSettingsPageState extends ConsumerState<PlaybackSettingsPage> {
|
||||
SettingsItem(
|
||||
icon: Icons.restart_alt,
|
||||
title: context.l10n.autoplayResetLearning,
|
||||
onTap: resetAutoplayLearning,
|
||||
onTap: _confirmResetLearning,
|
||||
showDivider: false,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -112,21 +112,18 @@ void main() {
|
||||
'${durations[(durations.length * .95).floor()]} us; model parameters 65536 bytes.',
|
||||
);
|
||||
});
|
||||
test(
|
||||
'cold start is neutral and personalization is an independent opt-in',
|
||||
() {
|
||||
final model = AutoplayPreferenceModel();
|
||||
expect(model.preference(_rock, at: _at), 0.5);
|
||||
expect(model.rankAdjustment(_rock, previous: _jazz, at: _at), 0);
|
||||
expect(AppSettings.fromJson({}).autoplayPersonalization, isFalse);
|
||||
final settings = const AppSettings().copyWith(
|
||||
autoplayPersonalization: true,
|
||||
);
|
||||
final restored = AppSettings.fromJson(settings.toJson());
|
||||
expect(restored.autoplayPersonalization, isTrue);
|
||||
expect(restored.autoplay, isFalse);
|
||||
},
|
||||
);
|
||||
test('cold start is neutral and personalization is on independently', () {
|
||||
final model = AutoplayPreferenceModel();
|
||||
expect(model.preference(_rock, at: _at), 0.5);
|
||||
expect(model.rankAdjustment(_rock, previous: _jazz, at: _at), 0);
|
||||
expect(AppSettings.fromJson({}).autoplayPersonalization, isTrue);
|
||||
final settings = const AppSettings().copyWith(
|
||||
autoplayPersonalization: false,
|
||||
);
|
||||
final restored = AppSettings.fromJson(settings.toJson());
|
||||
expect(restored.autoplayPersonalization, isFalse);
|
||||
expect(restored.autoplay, isFalse);
|
||||
});
|
||||
|
||||
test('learning favors engagement, transfers genre and handles context', () {
|
||||
final model = AutoplayPreferenceModel();
|
||||
|
||||
@@ -50,24 +50,33 @@ void main() {
|
||||
scrollable: find.byType(Scrollable).first,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Reset recommendation learning'), findsNothing);
|
||||
// On by default; the reset action belongs to the enabled state.
|
||||
expect(container.read(settingsProvider).autoplayPersonalization, isTrue);
|
||||
expect(find.text('Reset recommendation learning'), findsOneWidget);
|
||||
// A stray tap only asks; cancelling keeps what was learned.
|
||||
await tester.tap(find.text('Reset recommendation learning'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Reset recommendation learning?'), findsOneWidget);
|
||||
await tester.tap(find.text('Cancel'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Reset recommendation learning?'), findsNothing);
|
||||
await tester.tap(label);
|
||||
await tester.pumpAndSettle();
|
||||
expect(container.read(settingsProvider).autoplayPersonalization, isTrue);
|
||||
expect(container.read(settingsProvider).autoplayPersonalization, isFalse);
|
||||
expect(container.read(settingsProvider).autoplay, isFalse);
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
expect(
|
||||
AppSettings.fromJson(
|
||||
jsonDecode(prefs.getString('app_settings')!) as Map<String, dynamic>,
|
||||
).autoplayPersonalization,
|
||||
isTrue,
|
||||
isFalse,
|
||||
);
|
||||
expect(find.text('Reset recommendation learning'), findsOneWidget);
|
||||
expect(find.text('Reset recommendation learning'), findsNothing);
|
||||
await tester.ensureVisible(label);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(label);
|
||||
await tester.pumpAndSettle();
|
||||
expect(container.read(settingsProvider).autoplayPersonalization, isFalse);
|
||||
expect(container.read(settingsProvider).autoplayPersonalization, isTrue);
|
||||
final l10n = AppLocalizations.of(tester.element(label));
|
||||
expect(
|
||||
SettingsSearchCatalog(l10n).playback.any(
|
||||
|
||||
Reference in new issue
Block a user