From e02da40c44ce38f236998093c05a8969c6cc8316 Mon Sep 17 00:00:00 2001 From: TheLastGimbus Date: Fri, 23 Feb 2024 19:15:23 +0100 Subject: [PATCH] lay out initial settings framework don't implement them yet because we want to merge with 3i's --- lib/headphones/huawei/freebuds4i_sim.dart | 39 +++++++++-- lib/headphones/huawei/settings.dart | 17 +++++ .../simulators/headphones_settings_sim.dart | 17 ----- .../auto_pause_section.dart | 28 -------- .../headphones_settings_page.dart | 39 +++++++---- .../huawei/auto_pause_section.dart | 31 ++++++++ .../{ => huawei}/double_tap_section.dart | 66 ++++++++--------- .../{ => huawei}/hold_section.dart | 70 +++++++++---------- .../controls/headphones_controls_widget.dart | 26 ++++--- 9 files changed, 181 insertions(+), 152 deletions(-) delete mode 100644 lib/headphones/simulators/headphones_settings_sim.dart delete mode 100644 lib/ui/pages/headphones_settings/auto_pause_section.dart create mode 100644 lib/ui/pages/headphones_settings/huawei/auto_pause_section.dart rename lib/ui/pages/headphones_settings/{ => huawei}/double_tap_section.dart (64%) rename lib/ui/pages/headphones_settings/{ => huawei}/hold_section.dart (52%) diff --git a/lib/headphones/huawei/freebuds4i_sim.dart b/lib/headphones/huawei/freebuds4i_sim.dart index 6b84b58..8e9b7d6 100644 --- a/lib/headphones/huawei/freebuds4i_sim.dart +++ b/lib/headphones/huawei/freebuds4i_sim.dart @@ -4,17 +4,44 @@ import '../framework/anc.dart'; import '../framework/lrc_battery.dart'; import '../simulators/anc_sim.dart'; import '../simulators/bluetooth_headphones_sim.dart'; -import '../simulators/headphones_settings_sim.dart'; import '../simulators/lrc_battery_sim.dart'; import 'freebuds4i.dart'; import 'settings.dart'; final class HuaweiFreeBuds4iSim extends HuaweiFreeBuds4i - with - BluetoothHeadphonesSim, - LRCBatteryAlwaysFullSim, - AncSim, - HeadphonesSettingsSim {} + with BluetoothHeadphonesSim, LRCBatteryAlwaysFullSim, AncSim { + // ehhhhhh... + + final _settingsCtrl = BehaviorSubject.seeded( + const HuaweiFreeBuds4iSettings( + doubleTapLeft: DoubleTap.playPause, + doubleTapRight: DoubleTap.playPause, + holdBoth: Hold.cycleAnc, + holdBothToggledAncModes: { + AncMode.noiseCancelling, + AncMode.off, + AncMode.transparency, + }, + autoPause: true, + ), + ); + + @override + ValueStream get settings => _settingsCtrl.stream; + + @override + Future setSettings(HuaweiFreeBuds4iSettings newSettings) async { + _settingsCtrl.add( + _settingsCtrl.value.copyWith( + doubleTapLeft: newSettings.doubleTapLeft, + doubleTapRight: newSettings.doubleTapRight, + holdBoth: newSettings.holdBoth, + holdBothToggledAncModes: newSettings.holdBothToggledAncModes, + autoPause: newSettings.autoPause, + ), + ); + } +} /// Class to use as placeholder for Disabled() widget // this is not done with mixins because we may want to fill it with diff --git a/lib/headphones/huawei/settings.dart b/lib/headphones/huawei/settings.dart index 2ddb54f..f5ec5e1 100644 --- a/lib/headphones/huawei/settings.dart +++ b/lib/headphones/huawei/settings.dart @@ -17,6 +17,23 @@ class HuaweiFreeBuds4iSettings { this.holdBothToggledAncModes, this.autoPause, }); + + // don't want to use codegen *yet* + HuaweiFreeBuds4iSettings copyWith({ + DoubleTap? doubleTapLeft, + DoubleTap? doubleTapRight, + Hold? holdBoth, + Set? holdBothToggledAncModes, + bool? autoPause, + }) => + HuaweiFreeBuds4iSettings( + doubleTapLeft: doubleTapLeft ?? this.doubleTapLeft, + doubleTapRight: doubleTapRight ?? this.doubleTapRight, + holdBoth: holdBoth ?? this.holdBoth, + holdBothToggledAncModes: + holdBothToggledAncModes ?? this.holdBothToggledAncModes, + autoPause: autoPause ?? this.autoPause, + ); } // i don't have idea how to public/privatise those and how to name them diff --git a/lib/headphones/simulators/headphones_settings_sim.dart b/lib/headphones/simulators/headphones_settings_sim.dart deleted file mode 100644 index 659440a..0000000 --- a/lib/headphones/simulators/headphones_settings_sim.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:rxdart/rxdart.dart'; - -import '../framework/headphones_settings.dart'; - -mixin HeadphonesSettingsSim implements HeadphonesSettings { - // No initial data... since we can't know it nor pass it to mixins... - // I thought about making some abstract class for all settings, that would - // require .default()... decided to hold up now... but in future, maybe :) - final _settingsCtrl = BehaviorSubject(); - - @override - ValueStream get settings => _settingsCtrl.stream; - - @override - Future setSettings(T newSettings) async => - _settingsCtrl.add(newSettings); -} diff --git a/lib/ui/pages/headphones_settings/auto_pause_section.dart b/lib/ui/pages/headphones_settings/auto_pause_section.dart deleted file mode 100644 index f034d6e..0000000 --- a/lib/ui/pages/headphones_settings/auto_pause_section.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - -import '../../../headphones/_old/headphones_base.dart'; -import '../../common/list_tile_switch.dart'; - -class AutoPauseSection extends StatelessWidget { - final HeadphonesBase headphones; - - const AutoPauseSection({super.key, required this.headphones}); - - @override - Widget build(BuildContext context) { - final l = AppLocalizations.of(context)!; - return StreamBuilder( - stream: headphones.autoPause, - initialData: headphones.autoPause.valueOrNull ?? false, - builder: (_, snapshot) { - return ListTileSwitch( - title: Text(l.autoPause), - subtitle: Text(l.autoPauseDesc), - value: snapshot.data!, - onChanged: (newVal) => headphones.setAutoPause(newVal), - ); - }, - ); - } -} diff --git a/lib/ui/pages/headphones_settings/headphones_settings_page.dart b/lib/ui/pages/headphones_settings/headphones_settings_page.dart index e0de77e..1e7d0c4 100644 --- a/lib/ui/pages/headphones_settings/headphones_settings_page.dart +++ b/lib/ui/pages/headphones_settings/headphones_settings_page.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import '../../../headphones/framework/headphones_settings.dart'; +import '../../../headphones/huawei/settings.dart'; import '../../common/headphones_connection_ensuring_overlay.dart'; +import 'huawei/auto_pause_section.dart'; +import 'huawei/double_tap_section.dart'; +import 'huawei/hold_section.dart'; class HeadphonesSettingsPage extends StatelessWidget { const HeadphonesSettingsPage({super.key}); @@ -13,22 +18,28 @@ class HeadphonesSettingsPage extends StatelessWidget { appBar: AppBar(title: Text(l.pageHeadphonesSettingsTitle)), body: Center( child: HeadphonesConnectionEnsuringOverlay( - builder: (_, h) { - return ListView( - children: [ - // TODO MIGRATION: hp settings not yet implemented - const Text('HP Settings not yet implemented'), - // AutoPauseSection(headphones: h), - // const Divider(indent: 16, endIndent: 16), - // DoubleTapSection(headphones: h), - // const Divider(indent: 16, endIndent: 16), - // HoldSection(headphones: h), - // const SizedBox(height: 64), - ], - ); - }, + builder: (_, h) => + ListView(children: widgetsForModel(h as HeadphonesSettings)), ), ), ); } } + +// this is shitty. and we don't want this. not here. +// ... +// but i have no better idea for now :))))) +List widgetsForModel(HeadphonesSettings settings) { + if (settings is HeadphonesSettings) { + return [ + AutoPauseSection(settings), + const Divider(indent: 16, endIndent: 16), + DoubleTapSection(settings), + const Divider(indent: 16, endIndent: 16), + HoldSection(settings), + const SizedBox(height: 64), + ]; + } else { + throw "You shouldn't be on this screen if you don't have settings!"; + } +} diff --git a/lib/ui/pages/headphones_settings/huawei/auto_pause_section.dart b/lib/ui/pages/headphones_settings/huawei/auto_pause_section.dart new file mode 100644 index 0000000..e2dfb23 --- /dev/null +++ b/lib/ui/pages/headphones_settings/huawei/auto_pause_section.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../../../../headphones/framework/headphones_settings.dart'; +import '../../../../headphones/huawei/settings.dart'; +import '../../../common/list_tile_switch.dart'; + +class AutoPauseSection extends StatelessWidget { + final HeadphonesSettings headphones; + + const AutoPauseSection(this.headphones, {super.key}); + + @override + Widget build(BuildContext context) { + final l = AppLocalizations.of(context)!; + return StreamBuilder( + stream: headphones.settings.map((s) => s.autoPause), + initialData: false, + builder: (_, snap) { + return ListTileSwitch( + title: Text(l.autoPause), + subtitle: Text(l.autoPauseDesc), + value: snap.data ?? false, + onChanged: (newVal) => headphones.setSettings( + HuaweiFreeBuds4iSettings(autoPause: newVal), + ), + ); + }, + ); + } +} diff --git a/lib/ui/pages/headphones_settings/double_tap_section.dart b/lib/ui/pages/headphones_settings/huawei/double_tap_section.dart similarity index 64% rename from lib/ui/pages/headphones_settings/double_tap_section.dart rename to lib/ui/pages/headphones_settings/huawei/double_tap_section.dart index cf41533..0d67bb0 100644 --- a/lib/ui/pages/headphones_settings/double_tap_section.dart +++ b/lib/ui/pages/headphones_settings/huawei/double_tap_section.dart @@ -1,31 +1,30 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import '../../../headphones/_old/headphones_base.dart'; -import '../../../headphones/_old/headphones_data_objects.dart'; -import '../../common/list_tile_radio.dart'; -import '../../common/list_tile_switch.dart'; -import '../disabled.dart'; +import '../../../../headphones/framework/headphones_settings.dart'; +import '../../../../headphones/huawei/settings.dart'; +import '../../../common/list_tile_radio.dart'; +import '../../../common/list_tile_switch.dart'; +import '../../disabled.dart'; class DoubleTapSection extends StatelessWidget { - final HeadphonesBase headphones; + final HeadphonesSettings headphones; - const DoubleTapSection({super.key, required this.headphones}); + const DoubleTapSection(this.headphones, {super.key}); @override Widget build(BuildContext context) { final t = Theme.of(context); final tt = t.textTheme; final l = AppLocalizations.of(context)!; - return StreamBuilder( - stream: headphones.gestureSettings, - initialData: headphones.gestureSettings.valueOrNull ?? - const HeadphonesGestureSettings(), - builder: (context, snapshot) { - final gs = snapshot.data!; + return StreamBuilder( + stream: headphones.settings + .map((s) => (l: s.doubleTapLeft, r: s.doubleTapRight)), + initialData: (l: null, r: null), + builder: (context, snap) { + final dt = snap.data!; final enabled = - (gs.doubleTapLeft != HeadphonesGestureDoubleTap.nothing || - gs.doubleTapRight != HeadphonesGestureDoubleTap.nothing); + (dt.l != DoubleTap.nothing || dt.r != DoubleTap.nothing); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -34,14 +33,9 @@ class DoubleTapSection extends StatelessWidget { subtitle: Text(l.pageHeadphonesSettingsDoubleTapDesc), value: enabled, onChanged: (newVal) { - final g = newVal - ? HeadphonesGestureDoubleTap.playPause - : HeadphonesGestureDoubleTap.nothing; - headphones.setGestureSettings( - HeadphonesGestureSettings( - doubleTapLeft: g, - doubleTapRight: g, - ), + final g = newVal ? DoubleTap.playPause : DoubleTap.nothing; + headphones.setSettings( + HuaweiFreeBuds4iSettings(doubleTapLeft: g, doubleTapRight: g), ); }, ), @@ -57,10 +51,10 @@ class DoubleTapSection extends StatelessWidget { l.pageHeadphonesSettingsLeftBud, style: tt.titleMedium, ), - value: gs.doubleTapLeft, + value: dt.l, onChanged: enabled - ? (g) => headphones.setGestureSettings( - HeadphonesGestureSettings(doubleTapLeft: g), + ? (g) => headphones.setSettings( + HuaweiFreeBuds4iSettings(doubleTapLeft: g), ) : null, ), @@ -71,10 +65,10 @@ class DoubleTapSection extends StatelessWidget { l.pageHeadphonesSettingsRightBud, style: tt.titleMedium, ), - value: gs.doubleTapRight, + value: dt.r, onChanged: enabled - ? (g) => headphones.setGestureSettings( - HeadphonesGestureSettings(doubleTapRight: g), + ? (g) => headphones.setSettings( + HuaweiFreeBuds4iSettings(doubleTapRight: g), ) : null, ), @@ -92,8 +86,8 @@ class DoubleTapSection extends StatelessWidget { class _DoubleTapSetting extends StatelessWidget { final Widget? title; - final HeadphonesGestureDoubleTap? value; - final void Function(HeadphonesGestureDoubleTap?)? onChanged; + final DoubleTap? value; + final void Function(DoubleTap?)? onChanged; const _DoubleTapSetting({ required this.value, @@ -117,35 +111,35 @@ class _DoubleTapSetting extends StatelessWidget { ], ListTileRadio( title: Text(l.pageHeadphonesSettingsDoubleTapPlayPause), - value: HeadphonesGestureDoubleTap.playPause, + value: DoubleTap.playPause, dense: true, groupValue: value, onChanged: onChanged, ), ListTileRadio( title: Text(l.pageHeadphonesSettingsDoubleTapNextSong), - value: HeadphonesGestureDoubleTap.next, + value: DoubleTap.next, dense: true, groupValue: value, onChanged: onChanged, ), ListTileRadio( title: Text(l.pageHeadphonesSettingsDoubleTapPrevSong), - value: HeadphonesGestureDoubleTap.previous, + value: DoubleTap.previous, dense: true, groupValue: value, onChanged: onChanged, ), ListTileRadio( title: Text(l.pageHeadphonesSettingsDoubleTapAssist), - value: HeadphonesGestureDoubleTap.voiceAssistant, + value: DoubleTap.voiceAssistant, dense: true, groupValue: value, onChanged: onChanged, ), ListTileRadio( title: Text(l.pageHeadphonesSettingsDoubleTapNone), - value: HeadphonesGestureDoubleTap.nothing, + value: DoubleTap.nothing, dense: true, groupValue: value, onChanged: onChanged, diff --git a/lib/ui/pages/headphones_settings/hold_section.dart b/lib/ui/pages/headphones_settings/huawei/hold_section.dart similarity index 52% rename from lib/ui/pages/headphones_settings/hold_section.dart rename to lib/ui/pages/headphones_settings/huawei/hold_section.dart index d29c55a..03e5d80 100644 --- a/lib/ui/pages/headphones_settings/hold_section.dart +++ b/lib/ui/pages/headphones_settings/huawei/hold_section.dart @@ -1,48 +1,46 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import '../../../headphones/_old/headphones_base.dart'; -import '../../../headphones/_old/headphones_data_objects.dart'; -import '../../common/list_tile_checkbox.dart'; -import '../../common/list_tile_switch.dart'; -import '../disabled.dart'; +import '../../../../headphones/framework/anc.dart'; +import '../../../../headphones/framework/headphones_settings.dart'; +import '../../../../headphones/huawei/settings.dart'; +import '../../../common/list_tile_checkbox.dart'; +import '../../../common/list_tile_switch.dart'; +import '../../disabled.dart'; class HoldSection extends StatelessWidget { - final HeadphonesBase headphones; + final HeadphonesSettings headphones; - const HoldSection({super.key, required this.headphones}); + const HoldSection(this.headphones, {super.key}); @override Widget build(BuildContext context) { final l = AppLocalizations.of(context)!; - return StreamBuilder( - stream: headphones.gestureSettings, - initialData: headphones.gestureSettings.valueOrNull ?? - const HeadphonesGestureSettings(), - builder: (context, snapshot) { - final gs = snapshot.data!; - final enabled = gs.holdBoth == HeadphonesGestureHold.cycleAnc; + return StreamBuilder( + stream: headphones.settings + .map((s) => (holdBoth: s.holdBoth, anc: s.holdBothToggledAncModes)), + initialData: (holdBoth: null, anc: null), + builder: (context, snap) { + final gs = snap.data!; + final enabled = gs.holdBoth == Hold.cycleAnc; return Column( children: [ ListTileSwitch( title: Text(l.pageHeadphonesSettingsHold), subtitle: Text(l.pageHeadphonesSettingsHoldDesc), value: enabled, - onChanged: (newVal) => headphones.setGestureSettings( - HeadphonesGestureSettings( - holdBoth: newVal - ? HeadphonesGestureHold.cycleAnc - : HeadphonesGestureHold.nothing, + onChanged: (newVal) => headphones.setSettings( + HuaweiFreeBuds4iSettings( + holdBoth: newVal ? Hold.cycleAnc : Hold.nothing, ), ), ), Disabled( disabled: !enabled, child: _HoldSettingsCard( - enabledModes: MapEntry(snapshot.data!.holdBoth, - snapshot.data!.holdBothToggledAncModes), - onChanged: (m) => headphones.setGestureSettings( - HeadphonesGestureSettings( + enabledModes: MapEntry(gs.holdBoth, gs.anc), + onChanged: (m) => headphones.setSettings( + HuaweiFreeBuds4iSettings( holdBoth: m.key, holdBothToggledAncModes: m.value, ), @@ -57,23 +55,21 @@ class HoldSection extends StatelessWidget { } class _HoldSettingsCard extends StatelessWidget { - final MapEntry?> enabledModes; - final void Function( - MapEntry?>)? onChanged; + final MapEntry?> enabledModes; + final void Function(MapEntry?>)? onChanged; const _HoldSettingsCard({required this.enabledModes, this.onChanged}); - bool checkboxChecked(HeadphonesAncMode mode) => + bool checkboxChecked(AncMode mode) => enabledModes.value?.contains(mode) ?? false; - bool checkboxEnabled(bool enabled) => - (enabledModes.key == HeadphonesGestureHold.cycleAnc && - onChanged != null && - enabledModes.value != null && - // either all modes are enabled, or this is the disabled one - (enabledModes.value!.length > 2 || !enabled)); + bool checkboxEnabled(bool enabled) => (enabledModes.key == Hold.cycleAnc && + onChanged != null && + enabledModes.value != null && + // either all modes are enabled, or this is the disabled one + (enabledModes.value!.length > 2 || !enabled)); - Widget modeCheckbox(String title, String desc, HeadphonesAncMode mode) { + Widget modeCheckbox(String title, String desc, AncMode mode) { final checked = checkboxChecked(mode); return ListTileCheckbox( title: Text(title), @@ -105,17 +101,17 @@ class _HoldSettingsCard extends StatelessWidget { modeCheckbox( l.ancNoiseCancel, l.ancNoiseCancelDesc, - HeadphonesAncMode.noiseCancel, + AncMode.noiseCancelling, ), modeCheckbox( l.ancOff, l.ancOffDesc, - HeadphonesAncMode.off, + AncMode.off, ), modeCheckbox( l.ancAwareness, l.ancAwarenessDesc, - HeadphonesAncMode.awareness, + AncMode.transparency, ), ], ), diff --git a/lib/ui/pages/home/controls/headphones_controls_widget.dart b/lib/ui/pages/home/controls/headphones_controls_widget.dart index 0ce4559..4b632a6 100644 --- a/lib/ui/pages/home/controls/headphones_controls_widget.dart +++ b/lib/ui/pages/home/controls/headphones_controls_widget.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import '../../../../headphones/_old/headphones_base.dart'; import '../../../../headphones/framework/anc.dart'; import '../../../../headphones/framework/bluetooth_headphones.dart'; import '../../../../headphones/framework/headphones_info.dart'; +import '../../../../headphones/framework/headphones_settings.dart'; import '../../../../headphones/framework/lrc_battery.dart'; import '../../../theme/layouts.dart'; import 'anc_card.dart'; @@ -51,11 +51,11 @@ class HeadphonesControlsWidget extends StatelessWidget { else // TODO: This is ugly. Very const Expanded(child: Icon(Icons.headphones, size: 64)), - // TODO MIGRATION: hp settings not yet implemented - // Align( - // alignment: Alignment.centerRight, - // child: _HeadphonesSettingsButton(headphones), - // ), + if (headphones is HeadphonesSettings) + const Align( + alignment: Alignment.centerRight, + child: _HeadphonesSettingsButton(), + ), if (headphones is LRCBattery) BatteryCard(headphones as LRCBattery), if (headphones is Anc) AncCard(headphones as Anc), @@ -87,11 +87,11 @@ class HeadphonesControlsWidget extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - // TODO MIGRATION: hp settings not yet implemented - // Align( - // alignment: Alignment.centerRight, - // child: _HeadphonesSettingsButton(headphones), - // ), + if (headphones is HeadphonesSettings) + const Align( + alignment: Alignment.centerRight, + child: _HeadphonesSettingsButton(), + ), if (headphones is LRCBattery) BatteryCard(headphones as LRCBattery), if (headphones is Anc) AncCard(headphones as Anc), @@ -107,9 +107,7 @@ class HeadphonesControlsWidget extends StatelessWidget { /// Simple button leading to headphones settings page class _HeadphonesSettingsButton extends StatelessWidget { - final HeadphonesBase headphones; - - const _HeadphonesSettingsButton(this.headphones); + const _HeadphonesSettingsButton(); @override Widget build(BuildContext context) {