From fd274e55c5924fa03664c9145e9d8345fbf61f30 Mon Sep 17 00:00:00 2001 From: FernTheDev <15272073+Fernthedev@users.noreply.github.com> Date: Sat, 29 Oct 2022 19:36:42 -0400 Subject: [PATCH] Improve logger code and fix line endings --- lib/main.dart | 4 - lib/pages/devices.dart | 5 +- lib/pages/logger.dart | 159 ++++++++++++++++++++++++++++------------ lib/utils/adb.dart | 16 ++-- lib/utils/platform.dart | 2 + pubspec.lock | 64 ++++++++-------- pubspec.yaml | 3 +- 7 files changed, 165 insertions(+), 88 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 3e7bb9d..5f91cce 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,9 +3,6 @@ import 'package:desktop_adb_file_browser/pages/browser.dart'; import 'package:desktop_adb_file_browser/pages/devices.dart'; import 'package:desktop_adb_file_browser/pages/logger.dart'; import 'package:desktop_adb_file_browser/pigeon_impl.dart'; -import 'package:desktop_adb_file_browser/routes.dart'; -import 'package:desktop_adb_file_browser/utils/adb.dart'; -import 'package:dio/dio.dart'; import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -85,7 +82,6 @@ class MyApp extends StatelessWidget { scheme: FlexScheme.sanJuanBlue, surfaceMode: FlexSurfaceMode.highScaffoldLowSurface, blendLevel: 15, - appBarStyle: FlexAppBarStyle.background, appBarOpacity: 0.90, subThemesData: const FlexSubThemesData( blendOnLevel: 30, diff --git a/lib/pages/devices.dart b/lib/pages/devices.dart index 0e5e7d2..01a6c5a 100644 --- a/lib/pages/devices.dart +++ b/lib/pages/devices.dart @@ -58,7 +58,10 @@ class _DevicesPageState extends State { return FutureBuilder( future: _deviceListFuture, builder: (BuildContext context, AsyncSnapshot?> snapshot) { - // TODO: Error handling + if (snapshot.hasError) { + return Text("Error occurred: ${snapshot.error}"); + } + if (snapshot.hasData && snapshot.data != null && snapshot.connectionState == ConnectionState.done) { diff --git a/lib/pages/logger.dart b/lib/pages/logger.dart index aee8006..fc7a73c 100644 --- a/lib/pages/logger.dart +++ b/lib/pages/logger.dart @@ -1,20 +1,84 @@ -import 'dart:convert'; +import 'dart:async'; import 'dart:io'; import 'package:desktop_adb_file_browser/utils/adb.dart'; -import 'package:desktop_adb_file_browser/utils/scroll.dart'; +import 'package:desktop_adb_file_browser/utils/platform.dart'; import 'package:file_selector/file_selector.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:flutter/material.dart'; import 'package:routemaster/routemaster.dart'; -class LogPage extends StatelessWidget { +import '../utils/scroll.dart'; + +class LogPage extends StatefulWidget { LogPage({Key? key, required String serial}) : logFuture = Adb.logcat(serial), super(key: key); final Future> logFuture; + final scrollController = AdjustableScrollController(); + + @override + State createState() => _LogPageState(); +} + +class _LogPageState extends State { final List logs = []; + bool showLogs = false; + StreamSubscription? _streamSubscription; + + // Since Dart can't keep up fast enough with logcat when spammed, + // we queue the save for when the stream slows down + // which we assume is no longer spam + bool waitForSave = false; + DateTime lastStreamSend = DateTime.now(); + + @override + void initState() { + super.initState(); + widget.logFuture.then((stream) { + try { + _streamSubscription = stream.listen((event) { + setState(() { + var newLogs = event + .split(PlatformUtils.platformFileEnding) + .where((element) => element.trim().isNotEmpty); + logs.addAll(newLogs); + + if (waitForSave) { + var timeSinceSend = DateTime.now().difference(lastStreamSend); + if (timeSinceSend.inMilliseconds > 30) { + _saveLog(); + waitForSave = false; + } + } + + lastStreamSend = DateTime.now(); + }); + }); + _streamSubscription?.onError((e) { + debugPrint(e); + _showError(e); + }); + _streamSubscription?.onDone(() { + debugPrint("Done"); + }); + } catch (e) { + debugPrint(e.toString()); + _showError(e.toString()); + } + }).onError((error, stackTrace) { + debugPrint("Error $error"); + debugPrint(stackTrace.toString()); + _showError(error.toString()); + }); + } + + @override + void dispose() { + super.dispose(); + _streamSubscription?.cancel(); + } @override Widget build(BuildContext context) { @@ -34,67 +98,70 @@ class LogPage extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: IconButton( - onPressed: _saveLog, + onPressed: _queueSave, icon: const Icon( FluentIcons.save_28_regular, size: 28, )), - ) + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Switch( + value: showLogs, + onChanged: (v) => setState(() { + showLogs = v; + }))) ], ), body: Padding( padding: const EdgeInsets.all(8.0), - child: Container( - color: Theme.of(context).backgroundColor, - child: FutureBuilder>( - future: logFuture, - builder: buildStream, + child: Visibility( + visible: showLogs, + replacement: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Padding( + padding: EdgeInsets.all(8.0), + child: CircularProgressIndicator(), + ), + Text("Reading from logcat") + ], + ), + ), + child: Container( + color: Theme.of(context).backgroundColor, + child: buildList(), ), ), ), ); } - Widget buildStream( - BuildContext context, AsyncSnapshot> snapshot) { - if (!snapshot.hasData) { - return const CircularProgressIndicator(); - } - - return StreamBuilder( - stream: snapshot.data!, - builder: buildList, - ); + Future _showError(String error) { + return showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text("Error while streaming logcat"), + content: Text(error), + actions: [ + TextButton(onPressed: _queueSave, child: const Text("Save")), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text("Back")) + ], + )); } - Widget buildList(BuildContext context, AsyncSnapshot snapshot) { - if (!snapshot.hasData) { + Widget buildList() { + if (_streamSubscription == null) { return const CircularProgressIndicator(); } - if (snapshot.hasError) { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text("Error while streaming logcat"), - content: Text(snapshot.error.toString()), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text("Back")) - ], - )); - } - - final newString = snapshot.data; - if (newString != null) { - logs.add(newString); - } - return ListView.builder( key: ValueKey(logs.length), shrinkWrap: true, - controller: AdjustableScrollController(), + controller: widget.scrollController, itemBuilder: ((context, index) => SelectableText( logs[index], key: ValueKey(index), @@ -103,6 +170,10 @@ class LogPage extends StatelessWidget { ); } + void _queueSave() { + waitForSave = true; + } + void _saveLog() async { const String fileName = 'log.txt'; final String? path = await getSavePath(suggestedName: fileName); @@ -112,9 +183,7 @@ class LogPage extends StatelessWidget { var file = File(path); var writer = file.openWrite(); - String lineEnding = Platform.isWindows ? '\n\r' : '\n'; - - writer.writeAll(logs, lineEnding); + writer.writeAll(logs, PlatformUtils.platformFileEnding); await writer.flush(); await writer.close(); diff --git a/lib/utils/adb.dart b/lib/utils/adb.dart index 7341b94..86ed7d8 100644 --- a/lib/utils/adb.dart +++ b/lib/utils/adb.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:archive/archive_io.dart'; +import 'package:async/async.dart'; import 'package:desktop_adb_file_browser/utils/platform.dart'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; @@ -90,7 +91,10 @@ abstract class Adb { var process = await Process.run(await _getAdbPath(), newArgs); if (process.stderr != null && process.stderr.toString().isNotEmpty) { - throw process.stderr.toString(); + final error = process.stderr; + debugPrint("Error $error"); + debugPrintStack(); + throw error.toString(); } // if (process.exitCode != 0) throw "Process exit code was not 0!"; @@ -193,10 +197,12 @@ abstract class Adb { await runAdbCommand(serialName, ["logcat", "-c"]); // flush var result = await startAdbCommand(serialName, ["logcat"]); - - return result.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()); + + return StreamGroup.mergeBroadcast([ + result.stderr.transform(utf8.decoder), + result.stdout.transform(utf8.decoder), + ]); + // .transform(const LineSplitter()); } static Future getDeviceName(String? serialName) async { diff --git a/lib/utils/platform.dart b/lib/utils/platform.dart index e2943a7..990d7de 100644 --- a/lib/utils/platform.dart +++ b/lib/utils/platform.dart @@ -11,6 +11,8 @@ import 'package:path/path.dart' as host_path; abstract class PlatformUtils { + static String get platformFileEnding => Platform.isWindows ? '\r\n' : '\n'; + /// The default directory name for Flutter's configs. /// Configs will be written to the user's config path. If there is already a /// file with the name `.${kConfigDir}_$name` in the user's home path, that diff --git a/pubspec.lock b/pubspec.lock index 924b0ad..fba071e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,7 +21,7 @@ packages: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.3.1" + version: "3.3.2" args: dependency: transitive description: @@ -30,7 +30,7 @@ packages: source: hosted version: "2.3.1" async: - dependency: transitive + dependency: "direct main" description: name: async url: "https://pub.dartlang.org" @@ -77,14 +77,14 @@ packages: name: convert url: "https://pub.dartlang.org" source: hosted - version: "3.0.2" + version: "3.1.1" cross_file: dependency: transitive description: name: cross_file url: "https://pub.dartlang.org" source: hosted - version: "0.3.3+1" + version: "0.3.3+2" crypto: dependency: transitive description: @@ -140,49 +140,49 @@ packages: name: file_selector url: "https://pub.dartlang.org" source: hosted - version: "0.9.2" + version: "0.9.2+2" file_selector_ios: dependency: transitive description: name: file_selector_ios url: "https://pub.dartlang.org" source: hosted - version: "0.5.0+1" + version: "0.5.0+2" file_selector_linux: dependency: transitive description: name: file_selector_linux url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.9.0+1" file_selector_macos: dependency: transitive description: name: file_selector_macos url: "https://pub.dartlang.org" source: hosted - version: "0.9.0+1" + version: "0.9.0+3" file_selector_platform_interface: dependency: transitive description: name: file_selector_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.3.0" file_selector_web: dependency: transitive description: name: file_selector_web url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.9.0+2" file_selector_windows: dependency: transitive description: name: file_selector_windows url: "https://pub.dartlang.org" source: hosted - version: "0.9.1+2" + version: "0.9.1+4" filesize: dependency: "direct main" description: @@ -196,14 +196,21 @@ packages: name: flex_color_scheme url: "https://pub.dartlang.org" source: hosted - version: "5.1.0" + version: "6.0.1" + flex_seed_scheme: + dependency: transitive + description: + name: flex_seed_scheme + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" fluentui_system_icons: dependency: "direct main" description: name: fluentui_system_icons url: "https://pub.dartlang.org" source: hosted - version: "1.1.178" + version: "1.1.185" flutter: dependency: "direct main" description: flutter @@ -253,16 +260,16 @@ packages: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "4.0.1" + version: "4.0.2" innosetup: dependency: "direct dev" description: path: "." ref: HEAD - resolved-ref: "172a6a8af51468adb3a5f3c068f36d89fe5d185b" + resolved-ref: fe85895cf46e699c00907637e778dcc538a36de5 url: "https://github.com/aswinmurali-io/flutter_innosetup.git" source: git - version: "0.1.2" + version: "0.1.3" js: dependency: transitive description: @@ -276,7 +283,7 @@ packages: name: lints url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" matcher: dependency: transitive description: @@ -367,7 +374,7 @@ packages: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.4" + version: "2.0.5" path_provider_windows: dependency: transitive description: @@ -395,7 +402,7 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.1.2" + version: "2.1.3" process: dependency: transitive description: @@ -409,14 +416,7 @@ packages: name: pub_semver url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" - quiver: - dependency: transitive - description: - name: quiver - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" + version: "2.1.2" routemaster: dependency: "direct main" description: @@ -437,7 +437,7 @@ packages: name: shared_preferences_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.12" + version: "2.0.14" shared_preferences_ios: dependency: transitive description: @@ -533,7 +533,7 @@ packages: name: tuple url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" typed_data: dependency: transitive description: @@ -561,7 +561,7 @@ packages: name: watcher url: "https://pub.dartlang.org" source: hosted - version: "1.0.1" + version: "1.0.2" win32: dependency: "direct main" description: @@ -584,5 +584,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.17.0 <3.0.0" - flutter: ">=3.0.0" + dart: ">=2.18.0 <3.0.0" + flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index 572d6a8..5df0e29 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: shared_preferences: ^2.0.13 path_provider: ^2.0.9 file_selector: ^0.9.0 - flex_color_scheme: ^5.1.0 + flex_color_scheme: ^6.0.0 google_fonts: ^3.0.1 fluentui_system_icons: ^1.1.162 routemaster: ^1.0.1 @@ -51,6 +51,7 @@ dependencies: watcher: ^1.0.1 tuple: ^2.0.0 open_file: ^3.2.1 + async: ^2.9.0 dev_dependencies: flutter_test: