diff --git a/lib/src/app.dart b/lib/src/app.dart index efa5dda..af96011 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -111,6 +111,22 @@ class LogViewerScreenState extends State { }, ), ), + Checkbox( + value: settingsController.useRegex, + onChanged: (value) { + settingsController.updateUseRegex(value ?? false); + _filterLogs(); + }, + ), + const Text('Use Regex'), + Checkbox( + value: settingsController.caseSensitive, + onChanged: (value) { + settingsController.updateCaseSensitive(value ?? false); + _filterLogs(); + }, + ), + const Text('Case Sensitive'), DropdownButton( underline: Container(height: 0), alignment: Alignment.center, @@ -235,7 +251,8 @@ class LogViewerScreenState extends State { visibleLogs = logs .where((log) => log.level.index >= selectedLogLevel.index && - log.contains(controller.text)) + log.contains(controller.text, settingsController.useRegex, + caseSensitive: settingsController.caseSensitive)) .toList(); }); } @@ -247,14 +264,12 @@ class LogViewerScreenState extends State { if (line.isNotEmpty) { List parts = line.split('\t'); if (parts.length >= 5) { - var component = parts[4]; - int i = component.lastIndexOf('@'); parsedLogs.add(LogEntry( parts[0], fromString(parts[1]), parts[2], parts[3], - Component(component.substring(0, i), component.substring(i + 1)), + parts[4], parts.sublist(5), )); } else if (!line.contains('*** NEW LOG FILE ***')) { @@ -327,21 +342,44 @@ class LogEntry { final LogLevel level; final String category; final String threadId; - final Component component; + final String component; final List message; LogEntry(this.timestamp, this.level, this.category, this.threadId, this.component, this.message); - bool contains(String value) { - var v = value.toLowerCase(); - return timestamp.toLowerCase().contains(v) || - level.lowerCaseName.contains(v) || - category.toLowerCase().contains(v) || - threadId.toLowerCase().contains(v) || - component.name.toLowerCase().contains(v) || - component.address.toLowerCase().contains(v) || - message.where((value) => value.toLowerCase().contains(v)).isNotEmpty; + bool contains(String value, bool useRegex, {bool caseSensitive = false}) { + if (useRegex) { + try { + var regex = + RegExp(value, caseSensitive: caseSensitive, multiLine: true); + + return regex.hasMatch(timestamp) || + regex.hasMatch(level.lowerCaseName) || + regex.hasMatch(category) || + regex.hasMatch(threadId) || + regex.hasMatch(component) || + regex.hasMatch(message.join('\n')); + } catch (e) { + return false; + } + } else if (caseSensitive) { + var v = value; + return timestamp.contains(v) || + level.name.contains(v) || + category.contains(v) || + threadId.contains(v) || + component.contains(v) || + message.where((value) => value.contains(v)).isNotEmpty; + } else { + var v = value.toLowerCase(); + return timestamp.toLowerCase().contains(v) || + level.lowerCaseName.contains(v) || + category.toLowerCase().contains(v) || + threadId.toLowerCase().contains(v) || + component.toLowerCase().contains(v) || + message.where((value) => value.toLowerCase().contains(v)).isNotEmpty; + } } } @@ -367,7 +405,7 @@ class LogEntryWidget extends StatelessWidget { ), ), Text( - 'Component: ${log.component.name}@${log.component.address}', + 'Component: ${log.component}', style: const TextStyle( fontStyle: FontStyle.italic, ), @@ -390,7 +428,7 @@ class LogEntryWidget extends StatelessWidget { case LogLevel.debug: return Colors.green; default: - return Colors.black; + return Colors.blueGrey; } } } diff --git a/lib/src/settings/settings_controller.dart b/lib/src/settings/settings_controller.dart index e32c0df..9a8b4cf 100644 --- a/lib/src/settings/settings_controller.dart +++ b/lib/src/settings/settings_controller.dart @@ -16,24 +16,63 @@ class SettingsController with ChangeNotifier { // Make ThemeMode a private variable so it is not updated directly without // also persisting the changes with the SettingsService. late ThemeMode _themeMode; + late bool _useRegex; + late bool _caseSensitive; - // Allow Widgets to read the user's preferred ThemeMode. ThemeMode get themeMode => _themeMode; + bool get useRegex => _useRegex; + bool get caseSensitive => _caseSensitive; /// Load the user's settings from the SettingsService. It may load from a /// local database or the internet. The controller only knows it can load the /// settings from the service. Future loadSettings() async { _themeMode = await _settingsService.themeMode(); + _useRegex = await _settingsService.useRegex(); + _caseSensitive = await _settingsService.caseSensitive(); // Important! Inform listeners a change has occurred. notifyListeners(); } + Future updateCaseSensitive(bool? caseSensitive) async { + if (caseSensitive == null) return; // Do nothing if null is passed in. + + // Do not perform any work if new and old ThemeMode are identical + if (caseSensitive == _caseSensitive) return; + + // Otherwise, store the new ThemeMode in memory + _caseSensitive = caseSensitive; + + // Important! Inform listeners a change has occurred. + notifyListeners(); + + // Persist the changes to a local database or the internet using the + // SettingService. + await _settingsService.updateCaseSensitive(caseSensitive); + } + + /// Update and persist the ThemeMode based on the user's selection. + Future updateUseRegex(bool? regex) async { + if (regex == null) return; // Do nothing if null is passed in. + + // Do not perform any work if new and old ThemeMode are identical + if (regex == _useRegex) return; + + // Otherwise, store the new ThemeMode in memory + _useRegex = regex; + + // Important! Inform listeners a change has occurred. + notifyListeners(); + + // Persist the changes to a local database or the internet using the + // SettingService. + await _settingsService.updateUseRegex(regex); + } + /// Update and persist the ThemeMode based on the user's selection. Future updateThemeMode(ThemeMode? newThemeMode) async { - if (newThemeMode == null) return; - + if (newThemeMode == null) return; // Do nothing if null is passed in. // Do not perform any work if new and old ThemeMode are identical if (newThemeMode == _themeMode) return; diff --git a/lib/src/settings/settings_service.dart b/lib/src/settings/settings_service.dart index e03e57c..268d18e 100644 --- a/lib/src/settings/settings_service.dart +++ b/lib/src/settings/settings_service.dart @@ -17,9 +17,30 @@ class SettingsService { return ThemeMode.system; } + Future useRegex() async { + var prefs = await SharedPreferences.getInstance(); + return prefs.getBool("useRegex") ?? false; + } + + Future caseSensitive() async { + var prefs = await SharedPreferences.getInstance(); + return prefs.getBool("caseSensitive") ?? false; + } + /// Persists the user's preferred ThemeMode to local or remote storage. Future updateThemeMode(ThemeMode theme) async { var prefs = await SharedPreferences.getInstance(); prefs.setInt("theme", theme.index); } + + /// Persists the user's preferred ThemeMode to local or remote storage. + Future updateUseRegex(bool useRegex) async { + var prefs = await SharedPreferences.getInstance(); + prefs.setBool("useRegex", useRegex); + } + + Future updateCaseSensitive(bool caseSensitive) async { + var prefs = await SharedPreferences.getInstance(); + prefs.setBool("caseSensitive", caseSensitive); + } }