From 730fb22be34cd34a29638e8e0d153859470f48af Mon Sep 17 00:00:00 2001 From: Krille Date: Wed, 5 Mar 2025 19:01:34 +0100 Subject: [PATCH] feat: Add advanced configuration page --- lib/config/routes.dart | 9 +++ lib/config/setting_keys.dart | 42 +++++++++++ lib/pages/chat/recording_dialog.dart | 18 ++--- lib/utils/platform_infos.dart | 12 +++ lib/widgets/config_viewer.dart | 105 +++++++++++++++++++++++++++ 5 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 lib/widgets/config_viewer.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index c34fd8201..644fb0876 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -31,6 +31,7 @@ import 'package:fluffychat/pages/settings_notifications/settings_notifications.d import 'package:fluffychat/pages/settings_password/settings_password.dart'; import 'package:fluffychat/pages/settings_security/settings_security.dart'; import 'package:fluffychat/pages/settings_style/settings_style.dart'; +import 'package:fluffychat/widgets/config_viewer.dart'; import 'package:fluffychat/widgets/layouts/empty_page.dart'; import 'package:fluffychat/widgets/layouts/two_column_layout.dart'; import 'package:fluffychat/widgets/log_view.dart'; @@ -86,6 +87,14 @@ abstract class AppRoutes { const LogViewer(), ), ), + GoRoute( + path: '/configs', + pageBuilder: (context, state) => defaultPageBuilder( + context, + state, + const ConfigViewer(), + ), + ), ShellRoute( // Never use a transition on the shell route. Changing the PageBuilder // here based on a MediaQuery causes the child to briefly be rendered diff --git a/lib/config/setting_keys.dart b/lib/config/setting_keys.dart index 6d30298dd..3f5d083ea 100644 --- a/lib/config/setting_keys.dart +++ b/lib/config/setting_keys.dart @@ -1,3 +1,5 @@ +import 'package:shared_preferences/shared_preferences.dart'; + abstract class SettingKeys { static const String renderHtml = 'chat.fluffy.renderHtml'; static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents'; @@ -36,3 +38,43 @@ abstract class SettingKeys { 'chat.fluffy.no_encryption_warning_shown'; static const String shareKeysWith = 'chat.fluffy.share_keys_with'; } + +enum AppSettings { + audioRecordingNumChannels('audioRecordingNumChannels', 1), + audioRecordingAutoGain('audioRecordingAutoGain', true), + audioRecordingEchoCancel('audioRecordingEchoCancel', true), + audioRecordingNoiseSuppress('audioRecordingNoiseSuppress', true), + audioRecordingBitRate('audioRecordingBitRate', 64000), + audioRecordingSamplingRate('audioRecordingSamplingRate', 44100); + + final String key; + final T defaultValue; + + const AppSettings(this.key, this.defaultValue); +} + +extension AppSettingsBoolExtension on AppSettings { + bool getItem(SharedPreferences store) => store.getBool(key) ?? defaultValue; + Future setItem(SharedPreferences store, bool value) => + store.setBool(key, value); +} + +extension AppSettingsStringExtension on AppSettings { + String getItem(SharedPreferences store) => + store.getString(key) ?? defaultValue; + Future setItem(SharedPreferences store, String value) => + store.setString(key, value); +} + +extension AppSettingsIntExtension on AppSettings { + int getItem(SharedPreferences store) => store.getInt(key) ?? defaultValue; + Future setItem(SharedPreferences store, int value) => + store.setInt(key, value); +} + +extension AppSettingsDoubleExtension on AppSettings { + double getItem(SharedPreferences store) => + store.getDouble(key) ?? defaultValue; + Future setItem(SharedPreferences store, double value) => + store.setDouble(key, value); +} diff --git a/lib/pages/chat/recording_dialog.dart b/lib/pages/chat/recording_dialog.dart index 12ba99920..31fb86f8a 100644 --- a/lib/pages/chat/recording_dialog.dart +++ b/lib/pages/chat/recording_dialog.dart @@ -11,7 +11,9 @@ import 'package:record/record.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/utils/platform_infos.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'events/audio_player.dart'; class RecordingDialog extends StatefulWidget { @@ -34,10 +36,8 @@ class RecordingDialogState extends State { String? fileName; - static const int bitRate = 64000; - static const int samplingRate = 44100; - Future startRecording() async { + final store = Matrix.of(context).store; try { final codec = kIsWeb // Web seems to create webm instead of ogg when using opus encoder @@ -64,12 +64,12 @@ class RecordingDialogState extends State { await _audioRecorder.start( RecordConfig( - bitRate: bitRate, - sampleRate: samplingRate, - numChannels: 1, - autoGain: true, - echoCancel: true, - noiseSuppress: true, + bitRate: AppSettings.audioRecordingBitRate.getItem(store), + sampleRate: AppSettings.audioRecordingSamplingRate.getItem(store), + numChannels: AppSettings.audioRecordingNumChannels.getItem(store), + autoGain: AppSettings.audioRecordingAutoGain.getItem(store), + echoCancel: AppSettings.audioRecordingEchoCancel.getItem(store), + noiseSuppress: AppSettings.audioRecordingNoiseSuppress.getItem(store), encoder: codec, ), path: path ?? '', diff --git a/lib/utils/platform_infos.dart b/lib/utils/platform_infos.dart index c67568798..c23548397 100644 --- a/lib/utils/platform_infos.dart +++ b/lib/utils/platform_infos.dart @@ -66,6 +66,18 @@ abstract class PlatformInfos { ); }, ), + Builder( + builder: (innerContext) { + return TextButton.icon( + onPressed: () { + context.go('/configs'); + Navigator.of(innerContext).pop(); + }, + icon: const Icon(Icons.settings_applications_outlined), + label: const Text('Advanced Configs'), + ); + }, + ), ], applicationIcon: Image.asset( 'assets/logo.png', diff --git a/lib/widgets/config_viewer.dart b/lib/widgets/config_viewer.dart new file mode 100644 index 000000000..ac35e9ae4 --- /dev/null +++ b/lib/widgets/config_viewer.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; + +import 'package:go_router/go_router.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; +import 'package:fluffychat/widgets/matrix.dart'; + +class ConfigViewer extends StatelessWidget { + const ConfigViewer({super.key}); + + void _changeSetting( + BuildContext context, + AppSettings appSetting, + SharedPreferences store, + Function setState, + ) async { + final value = await showTextInputDialog( + context: context, + title: appSetting.name, + initialText: appSetting.defaultValue.toString(), + ); + if (value == null) return; + + if (appSetting is AppSettings) { + appSetting.setItem(store, value); + } + if (appSetting is AppSettings) { + appSetting.setItem(store, value == 'true'); + } + if (appSetting is AppSettings) { + appSetting.setItem(store, int.parse(value)); + } + if (appSetting is AppSettings) { + appSetting.setItem(store, double.parse(value)); + } + + setState(() {}); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Scaffold( + appBar: AppBar( + title: const Text('Advanced configurations'), + leading: BackButton( + onPressed: () => context.go('/'), + ), + ), + body: Column( + children: [ + Container( + margin: const EdgeInsets.all(16), + padding: const EdgeInsets.all(16), + color: theme.colorScheme.errorContainer, + child: Text( + 'Changing configs by hand is untested! Use without any warranty!', + style: TextStyle( + color: theme.colorScheme.onErrorContainer, + ), + ), + ), + Expanded( + child: StatefulBuilder( + builder: (context, setState) { + return ListView.builder( + itemCount: AppSettings.values.length, + itemBuilder: (context, i) { + final store = Matrix.of(context).store; + final appSetting = AppSettings.values[i]; + var value = ''; + if (appSetting is AppSettings) { + value = appSetting.getItem(store); + } + if (appSetting is AppSettings) { + value = appSetting.getItem(store).toString(); + } + if (appSetting is AppSettings) { + value = appSetting.getItem(store).toString(); + } + if (appSetting is AppSettings) { + value = appSetting.getItem(store).toString(); + } + return ListTile( + title: Text(appSetting.name), + subtitle: Text(value), + onTap: () => _changeSetting( + context, + appSetting, + store, + setState, + ), + ); + }, + ); + }, + ), + ), + ], + ), + ); + } +}