From a56a1e1f894c1532ce9b1cbde6c5c2a71fc2289f Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 13 Apr 2021 15:29:23 -0700 Subject: [PATCH] Tor Status Pane Initial Cut --- assets/core/Tor_icon.png | Bin 0 -> 575 bytes assets/core/Tor_icon.svg | 58 +++++++++++++++++++++++++++ lib/cwtch/cwtch.dart | 3 ++ lib/cwtch/cwtchNotifier.dart | 7 +++- lib/cwtch/ffi.dart | 7 ++++ lib/cwtch/gomobile.dart | 6 +++ lib/main.dart | 7 +++- lib/torstatus.dart | 21 ++++++++++ lib/views/profilemgrview.dart | 19 +++++++++ lib/views/torstatusview.dart | 63 +++++++++++++++++++++++++++++ pubspec.yaml | 1 + test/profileimage_init.png | Bin 0 -> 6704 bytes test/profileimage_test.dart | 72 ++++++++++++++++++++++++++++++++++ 13 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 assets/core/Tor_icon.png create mode 100644 assets/core/Tor_icon.svg create mode 100644 lib/torstatus.dart create mode 100644 lib/views/torstatusview.dart create mode 100644 test/profileimage_init.png create mode 100644 test/profileimage_test.dart diff --git a/assets/core/Tor_icon.png b/assets/core/Tor_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0c03273feebff9d0cd81e44073509c90ace6d2ad GIT binary patch literal 575 zcmV-F0>J%=P)K~zYI?UX%hR8bIypV^JU%|?P>t3o0{G>FY&IAq z8^J{Xft?`OScza`k;cCuifADL5fOrgjUeL35>Y{1!4C{%c`Wvbak*D_FW1huI?SDU z-Z^(T0|ljSS0~l#pYnzZfNQ``;22N=o&a|Wb(v!@tKL^1sdJ@)6TGgzQNOF_N`)tb zqjq&mjk4iq)oW@?-P~_oWdM&ha2{9!n*YLJ7}x-O1ExzgP>(10d*C5(K|L`*bGhJ} zdQm-+&ULk+zD~owt!^s{->6uw$= za|Y@G;4$z8AR@l1Q$Q7X3ycGw)|AYs`};Nk4g-roH6m8k8DJ$M-T~`?I`Fxt=1>+W zZ`q9iAG??FAg~k>ZFL9GNhZD8GKj%TU;_96R1$o5`qlxaGJvftP~I=P2b@X`G*WCd zBAx@AfJvaXCVv&U*|$MNyrBDXn?OU|2mD-adw||ubR{B|Gg&z|Kph4y15Z=e59rQi z9yke%rPS-d>4@m$v2vwGTG0#YGxe3aqPEo=>g>SKzZllk^`%1p>j#}BBRG(qvKIgV N002ovPDHLkV1jQb?qC1_ literal 0 HcmV?d00001 diff --git a/assets/core/Tor_icon.svg b/assets/core/Tor_icon.svg new file mode 100644 index 0000000..605a895 --- /dev/null +++ b/assets/core/Tor_icon.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml + + + \ No newline at end of file diff --git a/lib/cwtch/cwtch.dart b/lib/cwtch/cwtch.dart index f0f992a..53aa064 100644 --- a/lib/cwtch/cwtch.dart +++ b/lib/cwtch/cwtch.dart @@ -9,6 +9,9 @@ abstract class Cwtch { // ignore: non_constant_identifier_names void LoadProfiles(String pass); + // ignore: non_constant_identifier_names + void ResetTor(); + // todo: remove these // ignore: non_constant_identifier_names void SendProfileEvent(String onion, String jsonEvent); diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index 30cdde6..449dba1 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'dart:developer'; +import 'package:flutter_app/torstatus.dart'; + import '../errorHandler.dart'; import '../model.dart'; import '../settings.dart'; @@ -11,11 +13,13 @@ class CwtchNotifier { ProfileListState profileCN; Settings settings; ErrorHandler error; + TorStatus torStatus; - CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN) { + CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN) { profileCN = pcn; settings = settingsCN; error = errorCN; + torStatus = torStatusCN; } void handleMessage(String type, dynamic data) { @@ -75,6 +79,7 @@ class CwtchNotifier { break; case "ACNStatus": print("acn status: $data"); + torStatus.handleUpdate(int.parse(data["Progress"]), data["Status"]); break; default: print("unhandled event: $type"); diff --git a/lib/cwtch/ffi.dart b/lib/cwtch/ffi.dart index b1c2699..b23e29c 100644 --- a/lib/cwtch/ffi.dart +++ b/lib/cwtch/ffi.dart @@ -297,4 +297,11 @@ class CwtchFfi implements Cwtch { final u3 = message.toNativeUtf8(); SendMessage(u1, u1.length, u2, u2.length, u3, u3.length); } + + @override + void ResetTor() { + var resetTor = library.lookup>("c_ResetTor"); + final ResetTor = resetTor.asFunction(); + ResetTor(); + } } diff --git a/lib/cwtch/gomobile.dart b/lib/cwtch/gomobile.dart index f168d61..52aa807 100644 --- a/lib/cwtch/gomobile.dart +++ b/lib/cwtch/gomobile.dart @@ -142,4 +142,10 @@ class CwtchGomobile implements Cwtch { void SendMessage(String profileOnion, String contactHandle, String message) { cwtchPlatform.invokeMethod("SendMessage", {"ProfileOnion": profileOnion, "handle": contactHandle, "message": message}); } + + @override + // ignore: non_constant_identifier_names + void ResetTor() { + cwtchPlatform.invokeMethod("ResetTor", {}); + } } diff --git a/lib/main.dart b/lib/main.dart index 3a259ed..ce8e71c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter_app/cwtch/gomobile.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app/errorHandler.dart'; import 'package:flutter_app/settings.dart'; +import 'package:flutter_app/torstatus.dart'; import 'package:flutter_app/views/triplecolview.dart'; import 'package:provider/provider.dart'; import 'cwtch/cwtch.dart'; @@ -18,6 +19,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; var globalSettings = Settings(Locale("en", ''), Opaque.dark); var globalErrorHandler = ErrorHandler(); +var globalTorStatus = TorStatus(); void main() { LicenseRegistry.addLicense(() => licenses()); @@ -48,7 +50,7 @@ class FlwtchState extends State { cwtchInit = false; profs = ProfileListState(); - var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler); + var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus); if (Platform.isAndroid) { cwtch = CwtchGomobile(cwtchNotifier); @@ -65,6 +67,7 @@ class FlwtchState extends State { appStatus = AppModel(cwtch: cwtch); } + ChangeNotifierProvider getTorStatusProvider() => ChangeNotifierProvider.value(value: globalTorStatus); ChangeNotifierProvider getErrorHandlerProvider() => ChangeNotifierProvider.value(value: globalErrorHandler); ChangeNotifierProvider getSettingsProvider() => ChangeNotifierProvider.value(value: globalSettings); Provider getFlwtchStateProvider() => Provider(create: (_) => this); @@ -75,7 +78,7 @@ class FlwtchState extends State { //appStatus = AppModel(cwtch: cwtch); return MultiProvider( - providers: [getFlwtchStateProvider(), getProfileListProvider(), getSettingsProvider(), getErrorHandlerProvider()], + providers: [getFlwtchStateProvider(), getProfileListProvider(), getSettingsProvider(), getErrorHandlerProvider(), getTorStatusProvider()], builder: (context, widget) { Provider.of(context).initPackageInfo(); return Consumer( diff --git a/lib/torstatus.dart b/lib/torstatus.dart new file mode 100644 index 0000000..076d3e1 --- /dev/null +++ b/lib/torstatus.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class TorStatus extends ChangeNotifier { + int progress; + String status; + bool connected; + + /// Called by the event bus. + handleUpdate(int new_progress, String new_status) { + if (progress == 100) { + connected = true; + } else { + connected = false; + } + + progress = new_progress; + status = new_status; + + notifyListeners(); + } +} diff --git a/lib/views/profilemgrview.dart b/lib/views/profilemgrview.dart index e7015e5..53631b6 100644 --- a/lib/views/profilemgrview.dart +++ b/lib/views/profilemgrview.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_app/settings.dart'; +import 'package:flutter_app/views/torstatusview.dart'; import 'package:flutter_app/widgets/passwordfield.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_app/widgets/profilerow.dart'; @@ -34,6 +35,13 @@ class _ProfileMgrViewState extends State { appBar: AppBar( title: Text(AppLocalizations.of(context).titleManageProfiles), actions: [ + IconButton( + icon: Image( + image: AssetImage("assets/core/Tor_icon.png"), + filterQuality: FilterQuality.low, + isAntiAlias: false, + ), + onPressed: _pushTorStatus), IconButton(icon: Icon(Icons.bug_report_outlined), onPressed: _setLoggingLevelDebug), IconButton( icon: Icon(Icons.lock_open), @@ -75,6 +83,17 @@ class _ProfileMgrViewState extends State { )); } + void _pushTorStatus() { + Navigator.of(context).push(MaterialPageRoute( + builder: (BuildContext context) { + return Provider( + create: (_) => Provider.of(context, listen: false), + child: TorStatusView(), + ); + }, + )); + } + void _pushAddEditProfile({onion: ""}) { Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) { diff --git a/lib/views/torstatusview.dart b/lib/views/torstatusview.dart new file mode 100644 index 0000000..bef0767 --- /dev/null +++ b/lib/views/torstatusview.dart @@ -0,0 +1,63 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_app/settings.dart'; +import 'package:flutter_app/torstatus.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +import '../main.dart'; + +/// Tor Status View provides all info on Tor network state and the (future) ability to configure the network in a variety +/// of ways (restart, enable bridges, enable pluggable transports etc) +class TorStatusView extends StatefulWidget { + @override + _TorStatusView createState() => _TorStatusView(); +} + +class _TorStatusView extends State { + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Tor Network Status"), + ), + body: _buildSettingsList(), + ); + } + + Widget _buildSettingsList() { + return Consumer(builder: (context, torStatus, child) { + return LayoutBuilder(builder: (BuildContext context, BoxConstraints viewportConstraints) { + return Scrollbar( + isAlwaysShown: true, + child: SingleChildScrollView( + clipBehavior: Clip.antiAlias, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Column(children: [ + ListTile( + leading: Image( + image: AssetImage("assets/core/Tor_icon.png"), + ), + title: Text("Tor Status"), + subtitle: Text(torStatus.progress == 100 ? AppLocalizations.of(context).networkStatusOnline : torStatus.status), + trailing: ElevatedButton( + child: Text("Reset"), + onPressed: () { + Provider.of(context, listen: false).cwtch.ResetTor(); + }, + ), + ) + ])))); + }); + }); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 3343752..c70e4d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -83,6 +83,7 @@ flutter: assets: - assets/ + - assets/core/ - assets/profiles/ # To add custom fonts to your application, add a fonts section here, diff --git a/test/profileimage_init.png b/test/profileimage_init.png new file mode 100644 index 0000000000000000000000000000000000000000..a7f5369fbb2798cff8f5c79a5be0d11bee29f9a3 GIT binary patch literal 6704 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 literal 0 HcmV?d00001 diff --git a/test/profileimage_test.dart b/test/profileimage_test.dart new file mode 100644 index 0000000..bea67be --- /dev/null +++ b/test/profileimage_test.dart @@ -0,0 +1,72 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_app/opaque.dart'; +import 'package:flutter_app/settings.dart'; +import 'package:flutter_app/widgets/cwtchlabel.dart'; +import 'package:flutter_app/widgets/profileimage.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:provider/provider.dart'; + +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark); +var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light); +ChangeNotifierProvider getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark); + +String file(String slug) { + return "profileimage_" + slug + ".png"; +} + +void main() { + + testWidgets('ProfileImage widget test', (WidgetTester tester) async { + tester.binding.window.physicalSizeTestValue = Size(200, 200); + // await tester.pumpWidget(MultiProvider( + // providers:[getSettingsEnglishDark()], + // child: Directionality(textDirection: TextDirection.ltr, child: CwtchLabel(label: testingStr)) + // )); + + Widget testWidget = ProfileImage( + imagePath: "profiles/001-centaur.png", + badgeTextColor: settingsEnglishDark.theme.portraitProfileBadgeTextColor(), + badgeColor: settingsEnglishDark.theme.portraitProfileBadgeColor(), + maskOut: false, + border: settingsEnglishDark.theme.portraitOfflineBorderColor(), + diameter: 64.0, + badgeCount: 10, + ); + + Widget testHarness = MultiProvider( + providers:[getSettingsEnglishDark()], + builder: (context, child) { return MaterialApp( + locale: Provider.of(context).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + title: 'Test', + theme: mkThemeData(Provider.of(context)), + home: Card(child: testWidget), + );} + ); + + // Verify that our counter starts at 0. + //expect(find.text(testingStr), findsOneWidget); + //expect(find.text('1'), findsNothing); + + await tester.pumpWidget(testHarness); + await expectLater(find.byWidget(testHarness), matchesGoldenFile(file('init'))); + + // Tap the '+' icon and trigger a frame. + // await tester.tap(find.byIcon(Icons.add)); + // await tester.pump(); + // + // // Verify that our counter has incremented. + // expect(find.text('0'), findsNothing); + // expect(find.text('1'), findsOneWidget); + }); +}