Unlock Profiles #4

Merged
dan merged 5 commits from unlock into android 2021-01-28 20:25:08 +00:00
9 changed files with 125 additions and 54 deletions

12
ARCH.md Normal file
View File

@ -0,0 +1,12 @@
# State Management
We use a MultiProvider to distribute state to the underlying widgets. Right now there are 2 top
level Providers: FlwtchState (the app) and OpaqueTheme.
## Theme
OpaqueTheme extends ChangeProvider. SetLight and SetDark are functions that call notifyListeners()
ChangeNotiferProvider is used to package OpaqueTheme into a provider which is a top level
provider (as every widget in the app needs to be re-rendered on a theme switch).

View File

@ -71,11 +71,11 @@ class MainActivity: FlutterActivity() {
Log.i("MainActivity.kt", "got event chan: " + eventbus_chan + " launching corouting...")
GlobalScope.launch(Dispatchers.IO) {
while(true) {
val jsonEvent = Cwtch.getAppBusEvent()
Log.i("MainActivity.kt", "got appbusEvent: " + jsonEvent)
launch(Dispatchers.Main) {
eventbus_chan.invokeMethod("AppbusEvent", jsonEvent)
}
//val jsonEvent = Cwtch.getAppBusEvent()
// Log.i("MainActivity.kt", "got appbusEvent: " + jsonEvent)
// launch(Dispatchers.Main) {
// //eventbus_chan.invokeMethod("AppbusEvent", jsonEvent)
//}
}
}
@ -90,8 +90,12 @@ class MainActivity: FlutterActivity() {
val pass = (call.argument("pass") as? String) ?: "";
Cwtch.createProfile(nick, pass)
}
"LoadProfiles" -> {
val pass = (call.argument("pass") as? String) ?: "";
Cwtch.loadProfiles(pass)
}
"GetProfiles" -> result.success(Cwtch.getProfiles())
"ACNEvents" -> result.success(Cwtch.acnEvents())
// "ACNEvents" -> result.success(Cwtch.acnEvents())
"ContactEvents" -> result.success(Cwtch.contactEvents())
"GetContacts" -> {
val onion = (call.argument("profile") as? String) ?: "";

View File

@ -3,6 +3,7 @@ abstract class Cwtch {
void SelectProfile(String onion);
void CreateProfile(String nick, String pass);
void LoadProfiles(String pass);
Future<String> ACNEvents();
Future<String> ContactEvents();

View File

@ -18,6 +18,9 @@ typedef VoidFromStringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>
typedef access_cwtch_eventbus_function = Void Function();
typedef NextEventFn = void Function();
typedef string_to_void_function = Void Function(Pointer<Utf8> str, Int32 length);
typedef StringFn = void Function(Pointer<Utf8> dir, int);
typedef get_json_blob_void_function = Pointer<Utf8> Function();
typedef GetJsonBlobVoidFn = Pointer<Utf8> Function();
@ -80,6 +83,14 @@ class CwtchFfi implements Cwtch {
CreateProfile(Utf8.toUtf8(nick), nick.length, Utf8.toUtf8(pass), pass.length);
}
// ignore: non_constant_identifier_names
void LoadProfiles(String pass) {
var loadProfileC = library.lookup<NativeFunction<string_to_void_function>>("c_LoadProfiles");
// ignore: non_constant_identifier_names
final LoadProfiles = loadProfileC.asFunction<StringFn>();
LoadProfiles(Utf8.toUtf8(pass), pass.length);
}
Future<String> ACNEvents() async {
var acnEventsC = library.lookup<NativeFunction<acn_events_function>>(
"c_ACNEvents");

View File

@ -32,8 +32,8 @@ class CwtchGomobile implements Cwtch {
androidHomeDirectory = getApplicationDocumentsDirectory();
androidLibraryDir = appInfoPlatform.invokeMethod('getNativeLibDir');
final appbusEventChannel = MethodChannel(appbusEventChannelName);
appbusEventChannel.setMethodCallHandler(this._handleAppbusEvent);
// final appbusEventChannel = MethodChannel(appbusEventChannelName);
// appbusEventChannel.setMethodCallHandler(this._handleAppbusEvent);
}
Future<void> Start() async {
@ -57,6 +57,10 @@ class CwtchGomobile implements Cwtch {
cwtchPlatform.invokeMethod("CreateProfile", {"nick": nick, "pass": pass});
}
void LoadProfiles(String pass) {
cwtchPlatform.invokeMethod("LoadProfiles", {"pass": pass});
}
Future<String> ACNEvents() {
return cwtchPlatform.invokeMethod("ACNEvents");
}
@ -87,5 +91,4 @@ class CwtchGomobile implements Cwtch {
return cwtchPlatform.invokeMethod("GetMessage", {"profile" : profile, "contact": handle, "start": start, "end": end});
}
}

View File

@ -32,17 +32,17 @@ class FlwtchState extends State<Flwtch> {
// mergenotes: ui stuff
ProfileModel selectedProfile;
String selectedConversation = "";
var columns = [1];// default or 'single column' mode
var columns = [1]; // default or 'single column' mode
//var columns = [1, 1, 2];
AppModel appStatus;
HashMap<String, ProfileModel> profiles;
@override
initState() {
super.initState();
cwtchInit = false;
profiles = new HashMap<String, ProfileModel>();
print("FlwtchState.initState()");
@ -80,32 +80,55 @@ class FlwtchState extends State<Flwtch> {
});
}
ChangeNotifierProvider<OpaqueTheme> getOpaqueProvider() {
return ChangeNotifierProvider(create: (context) => OpaqueTheme(Opaque.dark));
}
Provider<FlwtchState> getFlwtchStateProvider() {
return Provider<FlwtchState>(
create: (_) => this,
);
}
@override
Widget build(BuildContext context) {
appStatus = AppModel(cwtch: cwtch);
final newTextTheme = Theme.of(context).textTheme.apply(
bodyColor: Opaque.current().mainTextColor(),
displayColor: Opaque.current().mainTextColor(),
);
bodyColor: Opaque.current().mainTextColor(),
displayColor: Opaque.current().mainTextColor(),
);
print("FlwtchState.build() cwtchInit: $cwtchInit");
return Provider<FlwtchState>(
create: (_) => this,
child: MaterialApp(
title: 'Cwtch',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.red,
primaryColor: Opaque.current().backgroundMainColor(),
canvasColor: Opaque.current().backgroundPaneColor(),
accentColor: Opaque.current().defaultButtonColor(),
buttonColor: Opaque.current().defaultButtonColor(),
textTheme: newTextTheme,
),
// from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(),
// from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(),
home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ProfileMgrView()) : SplashView(),
));
return MultiProvider(
providers: [getFlwtchStateProvider(), getOpaqueProvider()],
builder: (context, widget) { return MaterialApp(
title: 'Cwtch',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: Colors.red,
primaryColor: Provider.of<OpaqueTheme>(context)
.current()
.backgroundMainColor(),
canvasColor: Provider.of<OpaqueTheme>(context)
.current()
.backgroundPaneColor(),
accentColor: Provider.of<OpaqueTheme>(context)
.current()
.defaultButtonColor(),
buttonColor: Provider.of<OpaqueTheme>(context)
.current()
.defaultButtonColor(),
textTheme: newTextTheme,
),
// from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(),
// from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(),
home: cwtchInit == true
? (columns.length == 3
? TripleColumnView()
: ProfileMgrView())
: SplashView(),
);},
);
}
}
}

View File

@ -5,6 +5,8 @@
import 'dart:ui';
import 'dart:core';
import 'package:flutter/material.dart';
abstract class OpaqueThemeType {
static final Color red = Color(0xFFFF0000);
Color backgroundMainColor(){return red;}
@ -421,9 +423,12 @@ class Opaque extends OpaqueThemeType {
int chatPaneMinSize() { return chatPaneMinSizeBase[p[scale]]; }
int doublePaneMinSize() { return sidePaneMinSize() + chatPaneMinSize(); }
static OpaqueThemeType _current;
static final OpaqueThemeType dark = CwtchDark();
static final OpaqueThemeType light = CwtchLight();
static OpaqueThemeType current() { return dark; }
static void setDark() { _current = dark; }
static void setLight() { _current = light; }
static OpaqueThemeType current() { if (_current == null) {setDark();} return _current; }
int scale = 2;
@ -507,3 +512,15 @@ int scale = 2;
}
/// TODO: Wire into libCwtch saveSettings / updateTheme etc...
class OpaqueTheme extends ChangeNotifier {
OpaqueThemeType theme;
void setDark() { theme = Opaque.dark; notifyListeners();}
void setLight() { theme = Opaque.light; notifyListeners();}
OpaqueThemeType current() { return theme; }
OpaqueTheme(this.theme);
}

View File

@ -1,4 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_app/main.dart';
import 'package:flutter_app/opaque.dart';
import 'package:provider/provider.dart';
class GlobalSettingsView extends StatefulWidget {
@override
@ -35,7 +38,18 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
},
),
Text("Zoom"),
Text("Theme"),
SwitchListTile(
title: const Text('Theme'),
value: Provider.of<OpaqueTheme>(context).current() == Opaque.light,
onChanged: (bool value) {
if (value) {
Provider.of<OpaqueTheme>(context, listen: false).setLight();
} else {
Provider.of<OpaqueTheme>(context, listen: false).setDark();
}
},
secondary: const Icon(Icons.lightbulb_outline),
),
Text("Experiments enabled"),
Text("Text magnification reference"),
Text("Acknowledgements"),

View File

@ -19,7 +19,6 @@ class ProfileMgrView extends StatefulWidget {
}
class _ProfileMgrViewState extends State<ProfileMgrView> {
HashMap<String, ProfileModel> _profiles;
final ctrlrPassword = TextEditingController();
@override
@ -30,24 +29,6 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
@override
Widget build(BuildContext context) {
if (_profiles == null) {
_profiles = new HashMap<String, ProfileModel>();
}
if (_profiles.length < 1) {
Provider.of<FlwtchState>(context).cwtch.GetProfiles().then((profilesJson) {
jsonDecode(profilesJson).forEach((profile) {
ProfileModel profile1 = new ProfileModel();
profile1.onion = profile['onion'];
profile1.nickname = profile['name'];
profile1.creationDate = "4 jan 2020";
profile1.contacts = new HashMap<String, ContactModel>();
profile1.imagePath = profile['imagePath'];
_profiles.putIfAbsent(profile1.onion, () => profile1);
});
});
}
return Scaffold (
appBar: AppBar(
@ -129,7 +110,12 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
ElevatedButton(
child: const Text('Unlock'),
onPressed: () {
setState(() => _profiles.clear());
Provider
.of<FlwtchState>(context, listen: false)
.cwtch
.LoadProfiles(ctrlrPassword.value.text);
Provider.of<FlwtchState>(context, listen: false)
.loadProfiles();
Navigator.pop(context);
},
),