From 94830296a7bdfa81f0934514b80f71d2cd6ae631 Mon Sep 17 00:00:00 2001 From: Paulis Gributs Date: Wed, 18 Jan 2023 23:44:55 +0000 Subject: [PATCH] First Release Candidate --- lib/logger/logger.dart | 21 +++++- lib/logger/logger_factory.dart | 12 +++ lib/main.dart | 49 ++++++------- lib/sentiment_db.dart | 52 +++++++++++-- lib/ui/common_ui.dart | 73 ++++++++++++++++++- lib/ui/daily_breakdown.dart | 129 +++++++++++++++++++++++++++++++++ lib/ui/daily_dashboard.dart | 46 ------------ lib/ui/recommendations.dart | 69 ++---------------- lib/ui/settings.dart | 115 ++++++++++++++++++++++------- 9 files changed, 392 insertions(+), 174 deletions(-) create mode 100644 lib/ui/daily_breakdown.dart delete mode 100644 lib/ui/daily_dashboard.dart diff --git a/lib/logger/logger.dart b/lib/logger/logger.dart index 181a2a0..8ccc605 100644 --- a/lib/logger/logger.dart +++ b/lib/logger/logger.dart @@ -6,7 +6,9 @@ import 'dart:isolate'; import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:drift/isolate.dart'; +import 'package:flutter/cupertino.dart'; import 'package:negate/sentiment_db.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import '../sentiment_analysis.dart'; @@ -27,9 +29,10 @@ class SentenceLogger { static late DriftIsolate _iso; static late TfParams _tfp; static const int _updateFreq = 1; //update db every 5 minutes - final RegExp blacklist = RegExp(r".*system.*|.*keyboard.*|.*input.*|.*honeyboard.*|.*swiftkey.*"); + RegExp blacklist = RegExp(r".*system.*|.*keyboard.*|.*input.*|.*honeyboard.*|.*swiftkey.*"); String _lastUsedApp = ""; bool _dbUpdated = false; + double? _multiplier; factory SentenceLogger() { return _instance; @@ -62,6 +65,14 @@ class SentenceLogger { IsolateStartRequest(sendDriftIsolate: rPort.sendPort, targetPath: request.targetPath), ); + var prefs = request.prefs; + if (prefs.getBool('average_sentiment')!) { + _multiplier = prefs.getDouble('multiplier_sentiment')!; + } + if (prefs.getString('blacklist') != null) { + blacklist = RegExp(prefs.getString('blacklist')!); + } + _iso = await rPort.first as DriftIsolate; _tfp = request.tfp; var sdb = SentimentDB.connect(await _iso.connect()); @@ -100,7 +111,13 @@ class SentenceLogger { var appsInPeriod = _appMap.entries.where((element) => element.value.lastTimeUsed.difference(now).inMinutes <= 10); for (var app in appsInPeriod) { if (app.key == name) continue; - app.value.avgScore = ((app.value.avgScore * app.value.numCalled) + score) / (++app.value.numCalled); + if (_multiplier == null) { + app.value.avgScore = + ((app.value.avgScore * app.value.numCalled) + score) / + (++app.value.numCalled); + } else { + app.value.avgScore = (app.value.avgScore * _multiplier!) + (score * (1 - _multiplier!)); + } } if (_appMap.containsKey(name)) { diff --git a/lib/logger/logger_factory.dart b/lib/logger/logger_factory.dart index ee4ee61..2a44512 100644 --- a/lib/logger/logger_factory.dart +++ b/lib/logger/logger_factory.dart @@ -1,5 +1,7 @@ import 'dart:io' show Platform; +import 'package:negate/logger/logger.dart'; + import '../sentiment_db.dart'; import 'android_logger.dart'; import 'win_logger.dart'; @@ -16,4 +18,14 @@ class LoggerFactory { throw UnsupportedError("This platform is unsupported!"); } } + + static SentenceLogger getLogger() { + if (Platform.isWindows) { + return WinLogger(); + } else if (Platform.isAndroid) { + return AndroidLogger(); + } else { + throw UnsupportedError("This platform is unsupported!"); + } + } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 28f583e..2b3e233 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,7 +15,7 @@ import 'package:negate/sentiment_db.dart'; import 'package:drift/isolate.dart'; import 'package:flutter/material.dart' hide MenuItem; -import 'package:negate/ui/daily_dashboard.dart'; +import 'package:negate/ui/daily_breakdown.dart'; import 'package:negate/ui/settings.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -42,13 +42,25 @@ Future main() async { await analyser.init(); var tfp = TfParams(analyser.sInterpreter.address, analyser.dictionary); + //WidgetsFlutterBinding.ensureInitialized(); + var prefs = await SharedPreferences.getInstance(); + if (prefs.getBool('dynamic_theme') == null) { + prefs.setBool('dynamic_theme', true); + getIt.registerSingleton(true); + } else { + getIt.registerSingleton(prefs.getBool('dynamic_theme')!); + } + prefs.getBool('average_sentiment') == null ? prefs.setBool('average_sentiment', true) : null; + prefs.getDouble('multiplier_sentiment') == null ? prefs.setDouble('multiplier_sentiment', 0.75) : null; + prefs.getString('blacklist') == null ? prefs.setString('blacklist', LoggerFactory.getLogger().blacklist.pattern) : null; + if (Platform.isAndroid || Platform.isIOS) { LoggerFactory.startLoggerFactory( - TfliteRequest(rPort.sendPort, dbString, tfp)); + TfliteRequest(rPort.sendPort, dbString, tfp, prefs)); } else { await loggerUI.initSystemTray(); await Isolate.spawn(LoggerFactory.startLoggerFactory, - TfliteRequest(rPort.sendPort, dbString, tfp)); + TfliteRequest(rPort.sendPort, dbString, tfp, prefs)); } var iPort = await rPort.first as SendPort; @@ -114,7 +126,9 @@ class ThemedHourlyUI extends StatelessWidget { builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { ColorScheme light; ColorScheme dark; - if (lightDynamic != null && darkDynamic != null) { + bool enabled = getIt.call(); + bool dynamicCheck = lightDynamic != null && darkDynamic != null && enabled; + if (dynamicCheck) { light = lightDynamic; dark = darkDynamic; } else { @@ -134,11 +148,11 @@ class ThemedHourlyUI extends StatelessWidget { title: 'Negate Mental Health Tracker', theme: ThemeData( colorScheme: light, - scaffoldBackgroundColor: light.background, + scaffoldBackgroundColor: dynamicCheck ? light.background : null, useMaterial3: true), darkTheme: ThemeData( colorScheme: dark, - scaffoldBackgroundColor: dark.background, + scaffoldBackgroundColor: dynamicCheck ? dark.background : null, useMaterial3: true), themeMode: ThemeMode.system, home: home); @@ -171,7 +185,7 @@ class HourlyDashboard extends ConsumerWidget { }); return Scaffold( appBar: AppBar( - title: const Text('Hourly Dashboard'), + title: const Text('Dashboard'), actions: [ IconButton( onPressed: () { @@ -194,7 +208,7 @@ class HourlyDashboard extends ConsumerWidget { MaterialPageRoute( builder: (context) { return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return DailyDashboard.dashboard(context, sdb, ref, setState); + return DailyBreakdown.dashboard(context, sdb, ref, setState); }); })); }, @@ -302,7 +316,7 @@ class HourlyDashboard extends ConsumerWidget { persistentFooterButtons: [ TextButton( style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.primary, + foregroundColor: Theme.of(context).colorScheme.secondary, padding: const EdgeInsets.all(16.0), textStyle: const TextStyle(fontSize: 20), ), @@ -399,7 +413,7 @@ class HourlyDashboard extends ConsumerWidget { BarChartRodData( toY: (res[i] * 100).roundToDouble(), width: 10, - color: getBarColour(res[i]), + color: CommonUI.getBarColour(res[i]), borderRadius: const BorderRadius.all(Radius.zero)) ], showingTooltipIndicators: show)); @@ -407,21 +421,6 @@ class HourlyDashboard extends ConsumerWidget { return bars; } - Color getBarColour(double val) { - int percent = (val * 100).round(); - if (percent >= 75) { - return Colors.green[900]!; - } else if (percent >= 65) { - return Colors.green; - } else if (percent >= 45) { - return Colors.greenAccent; - } else if (percent >= 35) { - return Colors.yellow; - } else { - return Colors.red; - } - } - void handleMenu(String value) async { switch (value) { case 'Export': diff --git a/lib/sentiment_db.dart b/lib/sentiment_db.dart index 2148efb..5736ab5 100644 --- a/lib/sentiment_db.dart +++ b/lib/sentiment_db.dart @@ -1,6 +1,5 @@ import 'dart:collection'; import 'dart:convert'; -import 'dart:developer'; import 'dart:isolate'; import 'package:drift/drift.dart'; @@ -65,9 +64,13 @@ class SentimentDB extends _$SentimentDB { return jsonEncode(res); } - Future>>>> getRecommendations(DateTime after) async { + Future>>> _getSentimentsByName(DateTime after, [DateTime? before]) async { var query = select(sentimentLogs)..where((tbl) => tbl.hour.isBiggerOrEqualValue(after)); + if (before != null) { + query = select(sentimentLogs)..where((tbl) => + tbl.hour.isBetweenValues(after, before)); + } Map> weeklyAverage = >{}; Map weeklyCount = {}; var res = await query.get(); @@ -77,19 +80,53 @@ class SentimentDB extends _$SentimentDB { weeklyCount[log.name] = weeklyCount[log.name]! + 1; weeklyAverage[log.name]![1] = weeklyAverage[log.name]![1] + log.timeUsed; } else { - weeklyAverage.putIfAbsent(log.name, () => [log.avgScore, log.timeUsed.toDouble()]); + weeklyAverage.putIfAbsent(log.name, () => [log.avgScore, log.timeUsed.toDouble(), 0]); weeklyCount.putIfAbsent(log.name, () => 1); } } - var sorted = weeklyAverage.entries.toList(); + return weeklyAverage.entries.toList(); + } + + Future>>>> getRecommendations(DateTime after) async { + var sorted = await _getSentimentsByName(after); //Ignore apps used for less than 10 minutes sorted.removeWhere((element) => element.value[1] < 10); sorted.sort((a, b) => a.value[0].compareTo(b.value[0])); - var negative = sorted.sublist(0,5); - var positive = sorted.reversed.toList().sublist(0, 5); + var negative = sorted; + var positive = sorted.reversed.toList(); + if (sorted.length > 5) { + negative = sorted.sublist(0,5); + positive = sorted.reversed.toList().sublist(0, 5); + } return [negative, positive]; } + Future>>> getDailyBreakdown(DateTime date) async { + var selectedDate = date.alignDateTime(const Duration(days: 1)); + var sentiments = await _getSentimentsByName(selectedDate, selectedDate.add(const Duration(days: 1))); + sentiments.sort((b, a) => a.value[1].compareTo(b.value[1])); + var sub = sentiments; + double totalTime = 0; + double subTime = 0; + int counter = 0; + + for (var sentiment in sentiments) { + totalTime += sentiment.value[1]; + if (counter == 7) { + subTime = totalTime; + } + counter++; + } + for (var sentiment in sub) { + sentiment.value[2] = sentiment.value[1] / totalTime; + } + if (sentiments.length > 8) { + sub = sentiments.sublist(0,8); + sub.add(MapEntry('Other', [-1, (totalTime - subTime), (totalTime - subTime)/totalTime])); + } + return sub; + } + Future> getAvgHourlySentiment(DateTime date) async { var query = (select(sentimentLogs)..where((tbl) => tbl.hour.year.equals(date.year) & tbl.hour.month.equals(date.month) @@ -203,8 +240,9 @@ class TfParams { class TfliteRequest extends IsolateStartRequest { final TfParams tfp; + final SharedPreferences prefs; - TfliteRequest(SendPort sendDriftIsolate,String targetPath, this.tfp) : super(sendDriftIsolate: sendDriftIsolate, targetPath: targetPath); + TfliteRequest(SendPort sendDriftIsolate,String targetPath, this.tfp, this.prefs) : super(sendDriftIsolate: sendDriftIsolate, targetPath: targetPath); } class AddSentimentRequest { diff --git a/lib/ui/common_ui.dart b/lib/ui/common_ui.dart index a5d2196..f8a95e0 100644 --- a/lib/ui/common_ui.dart +++ b/lib/ui/common_ui.dart @@ -34,6 +34,11 @@ class CommonUI { )), persistentFooterButtons: [ TextButton( + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).colorScheme.secondary, + padding: const EdgeInsets.all(16.0), + textStyle: const TextStyle(fontSize: 20), + ), onPressed: () => Navigator.pop(context), child: const Text("Start")) ], ); @@ -74,6 +79,10 @@ class CommonUI { ), actions: [ TextButton( + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).colorScheme.secondary, + textStyle: const TextStyle(fontSize: 20), + ), onPressed: () { if (Platform.isAndroid) { SystemNavigator.pop(); @@ -83,6 +92,10 @@ class CommonUI { }, child: const Text('Exit')), TextButton( + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).colorScheme.secondary, + textStyle: const TextStyle(fontSize: 20), + ), onPressed: () { final Uri url = Uri.parse( 'https://kevinx8.github.io/Negate/Privacy-Policy.md'); @@ -90,6 +103,10 @@ class CommonUI { }, child: const Text('Policy')), TextButton( + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).colorScheme.secondary, + textStyle: const TextStyle(fontSize: 20), + ), child: const Text('Accept'), onPressed: () { pref.setBool('accepted_privacy', true); @@ -110,7 +127,7 @@ class CommonUI { alignment: MainAxisAlignment.center, children: [ ElevatedButton( - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.primary)), + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.secondary)), onPressed: () { selectedDate = selectedDate.subtract(const Duration(days: 1)); @@ -126,7 +143,7 @@ class CommonUI { ), Text(DateFormat.yMMMd().format(selectedDate)), ElevatedButton( - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.primary)), + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.secondary)), onPressed: () { var now = DateTime.now(); var midnight = DateTime(now.year, now.month, now.day); @@ -150,4 +167,56 @@ class CommonUI { ], ); } + + static Color getBarColour(double val) { + int percent = (val * 100).round(); + if (percent >= 75) { + return Colors.green[900]!; + } else if (percent >= 65) { + return Colors.green; + } else if (percent >= 45) { + return Colors.greenAccent; + } else if (percent >= 35) { + return Colors.yellow; + } else if (percent > 0) { + return Colors.red; + } else { + return Colors.blueGrey; + } + } + + static Widget appListView(List>> logs, SentimentDB sdb) { + return ListView.separated( + itemCount: logs.length, + itemBuilder: (BuildContext context, int index) { + var timeUsed = Duration( + minutes: logs[index].value[1].toInt()); + Text used = Text("Used for ${timeUsed.inMinutes} m"); + if (timeUsed.inHours != 0) { + used = Text( + "Used for ${timeUsed.inHours} h ${timeUsed.inMinutes % 60} m"); + } + return Container( + height: 50, + child: ListTile( + leading: FutureBuilder( + future: + sdb.getAppIcon(logs[index].key), + builder: (ctx, ico) { + if (ico.hasData) { + return Image.memory(ico.data!); + } + return const ImageIcon(null); + }, + ), + trailing: Text( + "${(logs[index].value[0] * 100).toStringAsFixed(2)}%"), + title: Text(logs[index].key), + subtitle: used, + )); + }, + separatorBuilder: (BuildContext context, int index) => + const Divider(), + ); + } } \ No newline at end of file diff --git a/lib/ui/daily_breakdown.dart b/lib/ui/daily_breakdown.dart new file mode 100644 index 0000000..72fbed3 --- /dev/null +++ b/lib/ui/daily_breakdown.dart @@ -0,0 +1,129 @@ +import 'dart:developer'; +import 'dart:typed_data'; + +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; +import 'package:negate/ui/common_ui.dart'; +import 'package:negate/ui/globals.dart'; + +import '../sentiment_db.dart'; + +class DailyBreakdown { + static Widget dashboard(BuildContext context, SentimentDB sdb, WidgetRef ref, + StateSetter setState) { + return Scaffold( + appBar: AppBar(title: const Text("Breakdown")), + body: Column(children: [ + CommonUI.dateChanger(context, sdb, ref, setState), + const Text("Overall Breakdown of the day", + style: TextStyle(fontWeight: FontWeight.bold)), + Expanded( + child: Padding( + padding: const EdgeInsets.all(30), + child: FutureBuilder>>>( + future: sdb.getDailyBreakdown(selectedDate), + builder: (context, s) { + List>> breakdownList = []; + if (s.hasData) { + if (s.data!.isNotEmpty) { + breakdownList = s.data!; + } + } + return Column( + children: [ + Expanded(child: + PieChart(PieChartData( + borderData: FlBorderData( + show: false, + ), + sections: showingSections(breakdownList, sdb), + ))), + Expanded(child: + CommonUI.appListView(breakdownList, sdb) + ) + ]); + }))), + ]), + ); + } + + static List showingSections(List>> breakdownList, SentimentDB sdb) { + List data = []; + const fontSize = 16.0; + const radius = 100.0; + const widgetSize = 40.0; + for (var entry in breakdownList) { + data.add(PieChartSectionData( + color: CommonUI.getBarColour(entry.value[0]), + value: entry.value[2] * 360, + title: entry.value[0] > 0 ? '${(entry.value[0] * 100).toStringAsFixed(0)}%' : '...', + radius: radius, + titleStyle: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: Colors.grey[900], + ), + badgeWidget: _Badge( + entry.key, + size: widgetSize, + borderColor: CommonUI.getBarColour(entry.value[0]), + sdb: sdb, + ), + badgePositionPercentageOffset: .98, + )); + } + return data; + } +} + +class _Badge extends StatelessWidget { + const _Badge(this.appName, { + required this.size, + required this.borderColor, + required this.sdb, + }); + + final String appName; + final double size; + final Color borderColor; + final SentimentDB sdb; + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + duration: PieChart.defaultDuration, + width: size, + height: size, + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + border: Border.all( + color: borderColor, + width: 2, + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(.5), + offset: const Offset(3, 3), + blurRadius: 3, + ), + ], + ), + padding: EdgeInsets.all(size * .15), + child: Center( + child: FutureBuilder( + future: sdb.getAppIcon(appName), + builder: (ctx, ico) { + if (ico.hasData) { + return Image.memory(ico.data!); + } + return const ImageIcon(null); + }, + ), + ), + ); + } +} diff --git a/lib/ui/daily_dashboard.dart b/lib/ui/daily_dashboard.dart deleted file mode 100644 index 3c7d081..0000000 --- a/lib/ui/daily_dashboard.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:fl_chart/fl_chart.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:intl/intl.dart'; -import 'package:negate/ui/common_ui.dart'; -import 'package:negate/ui/globals.dart'; - -import '../sentiment_db.dart'; - -class DailyDashboard { - static Widget dashboard(BuildContext context, SentimentDB sdb, WidgetRef ref, - StateSetter setState) { - return Scaffold( - appBar: AppBar(title: const Text("Daily Dashboard")), - body: Column(children: [ - CommonUI.dateChanger(context, sdb, ref, setState), - Expanded( - child: FutureBuilder>>>>( - future: sdb.getRecommendations( - DateTime.now().alignDateTime(const Duration(days: 1))), - builder: (context, s) { - var negativeLogs = >>[]; - var positiveLogs = >>[]; - if (s.hasData) { - if (s.data!.isNotEmpty) { - negativeLogs = s.data![0]; - positiveLogs = s.data![1]; - } - } - return PieChart(PieChartData( - borderData: FlBorderData( - show: false, - ), - sections: showingSections(), - )); - })), - ]), - ); - } - - static List showingSections() { - List data = []; - return data; - } -} diff --git a/lib/ui/recommendations.dart b/lib/ui/recommendations.dart index 851c4c6..fd616f6 100644 --- a/lib/ui/recommendations.dart +++ b/lib/ui/recommendations.dart @@ -1,5 +1,6 @@ import 'package:flutter/services.dart'; import 'package:negate/sentiment_db.dart'; +import 'package:negate/ui/common_ui.dart'; import 'package:negate/ui/globals.dart'; import 'package:flutter/material.dart' hide MenuItem; @@ -33,38 +34,8 @@ class RecommendationsPage extends StatelessWidget { } return Column(children: [ Expanded(child: - ListView.separated( - itemCount: negativeLogs.length, - itemBuilder: (BuildContext context, int index) { - var timeUsed = Duration( - minutes: negativeLogs[index].value[1].toInt()); - Text used = Text("Used for ${timeUsed.inMinutes} m"); - if (timeUsed.inHours != 0) { - used = Text( - "Used for ${timeUsed.inHours} h ${timeUsed.inMinutes % 60} m"); - } - return Container( - height: 50, - child: ListTile( - leading: FutureBuilder( - future: - sdb.getAppIcon(negativeLogs[index].key), - builder: (ctx, ico) { - if (ico.hasData) { - return Image.memory(ico.data!); - } - return const ImageIcon(null); - }, - ), - trailing: Text( - "${(negativeLogs[index].value[0] * 100).toStringAsFixed(2)}%"), - title: Text(negativeLogs[index].key), - subtitle: used, - )); - }, - separatorBuilder: (BuildContext context, int index) => - const Divider(), - )), + CommonUI.appListView(negativeLogs, sdb) + ), const Padding( padding: EdgeInsets.all(10), child: Text( @@ -72,38 +43,8 @@ class RecommendationsPage extends StatelessWidget { style: TextStyle(fontWeight: FontWeight.bold))), Expanded(child: - ListView.separated( - itemCount: positiveLogs.length, - itemBuilder: (BuildContext context, int index) { - var timeUsed = Duration( - minutes: positiveLogs[index].value[1].toInt()); - Text used = Text("Used for ${timeUsed.inMinutes} m"); - if (timeUsed.inHours != 0) { - used = Text( - "Used for ${timeUsed.inHours} h ${timeUsed.inMinutes % 60} m"); - } - return Container( - height: 50, - child: ListTile( - leading: FutureBuilder( - future: - sdb.getAppIcon(positiveLogs[index].key), - builder: (ctx, ico) { - if (ico.hasData) { - return Image.memory(ico.data!); - } - return const ImageIcon(null); - }, - ), - trailing: Text( - "${(positiveLogs[index].value[0] * 100).toStringAsFixed(2)}%"), - title: Text(positiveLogs[index].key), - subtitle: used, - )); - }, - separatorBuilder: (BuildContext context, int index) => - const Divider(), - )), + CommonUI.appListView(positiveLogs, sdb) + ), ]); })), ], diff --git a/lib/ui/settings.dart b/lib/ui/settings.dart index adac367..6946fd1 100644 --- a/lib/ui/settings.dart +++ b/lib/ui/settings.dart @@ -1,39 +1,98 @@ +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:settings_ui/settings_ui.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../logger/logger_factory.dart'; class SettingsPage { - static bool test = false; + static int _sliderState = 75; static Widget build(BuildContext context, StateSetter setState) { - var theme = SettingsThemeData(settingsListBackground: Theme.of(context).colorScheme.background); + var theme = SettingsThemeData( + settingsListBackground: Theme.of(context).scaffoldBackgroundColor, + ); return Scaffold( appBar: AppBar( title: const Text("Settings"), ), - body: SettingsList( - lightTheme: theme, - darkTheme: theme, - platform: DevicePlatform.android, - sections: [ - SettingsSection( - title: Text('Common'), - tiles: [ - SettingsTile.navigation( - leading: Icon(Icons.language), - title: Text('Language'), - value: Text('English'), - ), - SettingsTile.switchTile( - onToggle: (value) {setState(() { test = !test;});}, - initialValue: test, - leading: Icon(Icons.format_paint), - title: Text('Enable custom theme'), - ), - ], - ), - ], - ) - ); + body: FutureBuilder( + future: SharedPreferences.getInstance(), + builder: (context, prefs) { + if (prefs.data == null) { + return const Text('Loading...'); + } + return Column(children: [ + Expanded( + child: SettingsList( + lightTheme: theme, + darkTheme: theme, + platform: DevicePlatform.android, + sections: [ + SettingsSection( + title: const Text('Theme'), + tiles: [ + SettingsTile.switchTile( + activeSwitchColor: Theme.of(context).colorScheme.primary, + leading: const Icon(Icons.format_paint), + initialValue: prefs.data?.getBool('dynamic_theme'), + onToggle: (value) => setState(() { + prefs.data?.setBool('dynamic_theme', value); + }), + title: const Text('Use Dynamic Theme')), + ], + ), + SettingsSection( + title: const Text('DEVELOPER SETTINGS: Sentiment'), + tiles: [ + SettingsTile.switchTile( + activeSwitchColor: Theme.of(context).colorScheme.primary, + initialValue: + prefs.data?.getBool('average_sentiment'), + onToggle: (value) => setState(() { + prefs.data + ?.setBool('average_sentiment', value); + }), + title: const Text('Use Average')), + SettingsTile( + title: const Text('Previous Sentiment Split:')), + ], + ), + ], + )), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: Slider( + min: 0, + max: 100, + divisions: 8, + label: "${_sliderState.toString()}%", + value: prefs.data?.getDouble('multiplier_sentiment') != + null + ? prefs.data!.getDouble('multiplier_sentiment')! * 100 + : 75, + onChanged: (double value) { + setState(() { + _sliderState = value.toInt(); + prefs.data! + .setDouble('multiplier_sentiment', value / 100); + }); + }, + )), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: TextFormField( + initialValue: prefs.data!.getString('blacklist'), + onChanged: (regex) { + prefs.data?.setString('blacklist', regex); + }, + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'App Blacklist Regex', + ), + ))), + ]); + })); } - -} \ No newline at end of file +}