diff --git a/README.md b/README.md index e65315c..888513e 100644 --- a/README.md +++ b/README.md @@ -44,10 +44,9 @@ Currently, the following is done in terms of functionality: The desktop app (unix-ish only; developed on linux, untested on anything else, but should run on macos) is closer to its final form in terms of UI/UX, though it currently can only transmit. The Android -app is about halfway done in terms of functionality, but the UI is, shall we say, "hideous": +app is about halfway done in terms of functionality, but the UI is, shall we say, "bad": -![cuttle mobile app receiving data](./mobile_receiving.png) ![cuttle mobile app done receiving -data](./mobile_finished.png) +![cuttle mobile app receiving data](./mobile_receiving.png) I might take a crack at making it less hideous before I start on the mobile transmitting/desktop receiving side of the functionality, but maybe not! I often want to transfer a file or text from my diff --git a/mobile/justfile b/mobile/justfile index a16d174..e31d430 100644 --- a/mobile/justfile +++ b/mobile/justfile @@ -5,9 +5,16 @@ gen: flutter_rust_bridge_codegen lint: - cd native && cargo fmt dart format . +native: native-debug native-release + +native-release: + cd android && ./gradlew :app:cargoBuildRelease + +native-debug: + cd android && ./gradlew :app:cargoBuildDebug + clean: flutter clean cd native && cargo clean diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index fb19e1a..40e51ad 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,10 +1,12 @@ import 'dart:async'; +import 'dart:collection'; import 'dart:io'; import 'dart:typed_data'; import 'package:camerawesome/camerawesome_plugin.dart'; import 'package:flutter/material.dart'; import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart'; +import 'package:intl/intl.dart'; import 'package:rxdart/rxdart.dart'; import 'ffi.dart'; import 'utils/mlkit_utils.dart'; @@ -45,12 +47,14 @@ class _MyHomePageState extends State { final _barcodeScanner = BarcodeScanner(formats: [BarcodeFormat.qrCode]); final _rxTextController = BehaviorSubject(); late final Stream _rxTextStream = _rxTextController.stream; - final _scrollController = ScrollController(); TxConfig? _txConfig; var _cuttleState = CuttleState.unitialized; - final List _rxData = []; + final HashSet _rxData = HashSet(); String _rxText = ''; + int _rxCount = 1; + + final _formatter = NumberFormat('###,###,###'); @override void dispose() { @@ -65,15 +69,14 @@ class _MyHomePageState extends State { onImageForAnalysis: (img) => _processImageBarcode(img), imageAnalysisConfig: AnalysisConfig( androidOptions: const AndroidAnalysisOptions.nv21( - width: 1024, + width: 600, ), - maxFramesPerSecond: 30, + maxFramesPerSecond: 20, autoStart: false, ), builder: (cameraModeState, previewSize, previewRect) { return _RxTextDisplayWidget( rxTextStream: _rxTextStream, - scrollController: _scrollController, analysisController: cameraModeState.analysisController!, ); }, @@ -103,7 +106,8 @@ class _MyHomePageState extends State { _txConfig!.filename ?? "large text on the command line"; final desc = _txConfig!.description; - final text = 'Receiving $fname, ${_txConfig!.len} bytes: $desc'; + final text = + 'Receiving $fname, ${_formatter.format(_txConfig!.len)} bytes ($desc)'; _rxText = text; _rxTextController.add(text); continue; @@ -121,25 +125,32 @@ class _MyHomePageState extends State { case CuttleState.receiving: { var txconf = await api.getTxConfig(bytes: dbytes); - if (txconf != null || barcode.rawValue != null) { - await api.dropTxConfig(txc: txconf!); + if (txconf != null) { + await api.dropTxConfig(txc: txconf); continue; } final packet = RaptorPacket(field0: dbytes); _rxData.add(packet); - final content = - await api.decodePackets(packets: _rxData, txconf: _txConfig!); - if (content != null) { - _rxData.clear(); - _barcodeScanner.close(); - _cuttleState = CuttleState.received; - _rxTextController.add("DONE RECEIVING $_rxText"); - final f = await _saveReceivedFile(_txConfig!.filename, content); - _rxTextController.add("Saved content to $f"); - continue; + _rxCount += 1; + if (_rxCount % 40 == 0) { + _rxCount = 1; + final content = await api.decodePackets( + packets: _rxData.toList(), txconf: _txConfig!); + if (content != null) { + _rxData.clear(); + _barcodeScanner.close(); + _cuttleState = CuttleState.received; + _rxTextController.add("DONE RECEIVING $_rxText"); + final f = + await _saveReceivedFile(_txConfig!.filename, content); + _rxTextController.add("Saved content to $f"); + continue; + } } - _rxTextController - .add("$_rxText -- ${_rxData.length} bytes so far"); + final bytesTotal = _rxData.length * dbytes.length; + final pct = (100.0 * bytesTotal / _txConfig!.len).floor(); + _rxTextController.add( + "$_rxText -- $pct% received (${_formatter.format(bytesTotal)} bytes)"); } case CuttleState.received: @@ -164,7 +175,6 @@ class _MyHomePageState extends State { class _RxTextDisplayWidget extends StatefulWidget { final Stream rxTextStream; - final ScrollController scrollController; final AnalysisController analysisController; @@ -172,7 +182,6 @@ class _RxTextDisplayWidget extends StatefulWidget { // ignore: unused_element super.key, required this.rxTextStream, - required this.scrollController, required this.analysisController, }); @@ -186,8 +195,8 @@ class _RxTextDisplayWidgetState extends State<_RxTextDisplayWidget> { return Align( alignment: Alignment.bottomCenter, child: Container( - decoration: BoxDecoration( - color: Colors.tealAccent.withOpacity(0.7), + decoration: const BoxDecoration( + color: Colors.white, ), child: Column(mainAxisSize: MainAxisSize.min, children: [ Material( diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index aae02f8..c76163a 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: path_provider: ^2.1.0 rxdart: ^0.27.7 uuid: ^3.0.7 + intl: ^0.18.1 dev_dependencies: flutter_test: diff --git a/mobile_receiving.png b/mobile_receiving.png index c134aeb..88b019b 100644 Binary files a/mobile_receiving.png and b/mobile_receiving.png differ