diff --git a/assets/core/Tor_icon.png b/assets/core/Tor_icon.png
new file mode 100644
index 0000000..0c03273
Binary files /dev/null and b/assets/core/Tor_icon.png differ
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 @@
+
+
+
+
\ 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 0000000..a7f5369
Binary files /dev/null and b/test/profileimage_init.png differ
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);
+ });
+}