refactor: make multi FFI object && initial flutter multi sessions support

Signed-off-by: Kingtous <kingtous@qq.com>
This commit is contained in:
kingtous
2022-06-13 21:07:26 +08:00
committed by Kingtous
parent bce72ea84b
commit 985c616ca6
22 changed files with 976 additions and 840 deletions

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/chat_model.dart';
import 'package:provider/provider.dart';
import '../../models/model.dart';
import 'home_page.dart';
@@ -20,7 +21,7 @@ class ChatPage extends StatelessWidget implements PageShape {
PopupMenuButton<int>(
icon: Icon(Icons.group),
itemBuilder: (context) {
final chatModel = FFI.chatModel;
final chatModel = gFFI.chatModel;
return chatModel.messages.entries.map((entry) {
final id = entry.key;
final user = entry.value.chatUser;
@@ -31,14 +32,14 @@ class ChatPage extends StatelessWidget implements PageShape {
}).toList();
},
onSelected: (id) {
FFI.chatModel.changeCurrentID(id);
gFFI.chatModel.changeCurrentID(id);
})
];
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: FFI.chatModel,
value: gFFI.chatModel,
child: Container(
color: MyTheme.grayBg,
child: Consumer<ChatModel>(builder: (context, chatModel, child) {

View File

@@ -1,14 +1,16 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/mobile/pages/file_manager_page.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:async';
import '../../common.dart';
import '../../models/model.dart';
import 'home_page.dart';
import 'remote_page.dart';
import 'settings_page.dart';
import 'scan_page.dart';
import 'settings_page.dart';
/// Connection page for connecting to a remote peer.
class ConnectionPage extends StatefulWidget implements PageShape {
@@ -41,7 +43,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
super.initState();
if (isAndroid) {
Timer(Duration(seconds: 5), () {
_updateUrl = FFI.getByName('software_update_url');
_updateUrl = gFFI.getByName('software_update_url');
if (_updateUrl.isNotEmpty) setState(() {});
});
}
@@ -50,7 +52,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
@override
Widget build(BuildContext context) {
Provider.of<FfiModel>(context);
if (_idController.text.isEmpty) _idController.text = FFI.getId();
if (_idController.text.isEmpty) _idController.text = gFFI.getId();
return SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@@ -220,7 +222,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
width = size.width / n - 2 * space;
}
final cards = <Widget>[];
var peers = FFI.peers();
var peers = gFFI.peers();
peers.forEach((p) {
cards.add(Container(
width: width,
@@ -278,7 +280,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
elevation: 8,
);
if (value == 'remove') {
setState(() => FFI.setByName('remove', '$id'));
setState(() => gFFI.setByName('remove', '$id'));
() async {
removePreference(id);
}();

View File

@@ -1,11 +1,12 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:provider/provider.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:wakelock/wakelock.dart';
import 'package:toggle_switch/toggle_switch.dart';
import 'package:wakelock/wakelock.dart';
import '../../common.dart';
import '../../models/model.dart';
@@ -20,22 +21,22 @@ class FileManagerPage extends StatefulWidget {
}
class _FileManagerPageState extends State<FileManagerPage> {
final model = FFI.fileModel;
final model = gFFI.fileModel;
final _selectedItems = SelectedItems();
final _breadCrumbScroller = ScrollController();
@override
void initState() {
super.initState();
FFI.connect(widget.id, isFileTransfer: true);
FFI.ffiModel.updateEventListener(widget.id);
gFFI.connect(widget.id, isFileTransfer: true);
gFFI.ffiModel.updateEventListener(widget.id);
Wakelock.enable();
}
@override
void dispose() {
model.onClose();
FFI.close();
gFFI.close();
SmartDialog.dismiss();
Wakelock.disable();
super.dispose();
@@ -43,7 +44,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
@override
Widget build(BuildContext context) => ChangeNotifierProvider.value(
value: FFI.fileModel,
value: gFFI.fileModel,
child: Consumer<FileModel>(builder: (_context, _model, _child) {
return WillPopScope(
onWillPop: () async {

View File

@@ -46,7 +46,7 @@ class _RemotePageState extends State<RemotePage> {
@override
void initState() {
super.initState();
FFI.connect(widget.id);
gFFI.connect(widget.id);
WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
showLoading(translate('Connecting...'));
@@ -55,18 +55,18 @@ class _RemotePageState extends State<RemotePage> {
});
Wakelock.enable();
_physicalFocusNode.requestFocus();
FFI.ffiModel.updateEventListener(widget.id);
FFI.listenToMouse(true);
gFFI.ffiModel.updateEventListener(widget.id);
gFFI.listenToMouse(true);
}
@override
void dispose() {
hideMobileActionsOverlay();
FFI.listenToMouse(false);
FFI.invokeMethod("enable_soft_keyboard", true);
gFFI.listenToMouse(false);
gFFI.invokeMethod("enable_soft_keyboard", true);
_mobileFocusNode.dispose();
_physicalFocusNode.dispose();
FFI.close();
gFFI.close();
_interval?.cancel();
_timer?.cancel();
SmartDialog.dismiss();
@@ -77,7 +77,7 @@ class _RemotePageState extends State<RemotePage> {
}
void resetTool() {
FFI.resetModifiers();
gFFI.resetModifiers();
}
bool isKeyboardShown() {
@@ -96,8 +96,8 @@ class _RemotePageState extends State<RemotePage> {
overlays: []);
// [pi.version.isNotEmpty] -> check ready or not,avoid login without soft-keyboard
if (chatWindowOverlayEntry == null &&
FFI.ffiModel.pi.version.isNotEmpty) {
FFI.invokeMethod("enable_soft_keyboard", false);
gFFI.ffiModel.pi.version.isNotEmpty) {
gFFI.invokeMethod("enable_soft_keyboard", false);
}
}
});
@@ -129,12 +129,12 @@ class _RemotePageState extends State<RemotePage> {
newValue[common] == oldValue[common];
++common);
for (i = 0; i < oldValue.length - common; ++i) {
FFI.inputKey('VK_BACK');
gFFI.inputKey('VK_BACK');
}
if (newValue.length > common) {
var s = newValue.substring(common);
if (s.length > 1) {
FFI.setByName('input_string', s);
gFFI.setByName('input_string', s);
} else {
inputChar(s);
}
@@ -152,7 +152,7 @@ class _RemotePageState extends State<RemotePage> {
// ?
} else if (newValue.length < oldValue.length) {
final char = 'VK_BACK';
FFI.inputKey(char);
gFFI.inputKey(char);
} else {
final content = newValue.substring(oldValue.length);
if (content.length > 1) {
@@ -168,11 +168,11 @@ class _RemotePageState extends State<RemotePage> {
content == '' ||
content == '【】')) {
// can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input
FFI.setByName('input_string', content);
gFFI.setByName('input_string', content);
openKeyboard();
return;
}
FFI.setByName('input_string', content);
gFFI.setByName('input_string', content);
} else {
inputChar(content);
}
@@ -185,11 +185,11 @@ class _RemotePageState extends State<RemotePage> {
} else if (char == ' ') {
char = 'VK_SPACE';
}
FFI.inputKey(char);
gFFI.inputKey(char);
}
void openKeyboard() {
FFI.invokeMethod("enable_soft_keyboard", true);
gFFI.invokeMethod("enable_soft_keyboard", true);
// destroy first, so that our _value trick can work
_value = initText;
setState(() => _showEdit = false);
@@ -212,7 +212,7 @@ class _RemotePageState extends State<RemotePage> {
final label = _logicalKeyMap[e.logicalKey.keyId] ??
_physicalKeyMap[e.physicalKey.usbHidUsage] ??
e.logicalKey.keyLabel;
FFI.inputKey(label, down: down, press: press ?? false);
gFFI.inputKey(label, down: down, press: press ?? false);
}
@override
@@ -220,7 +220,7 @@ class _RemotePageState extends State<RemotePage> {
final pi = Provider.of<FfiModel>(context).pi;
final hideKeyboard = isKeyboardShown() && _showEdit;
final showActionButton = !_showBar || hideKeyboard;
final keyboard = FFI.ffiModel.permissions['keyboard'] != false;
final keyboard = gFFI.ffiModel.permissions['keyboard'] != false;
return WillPopScope(
onWillPop: () async {
@@ -230,7 +230,7 @@ class _RemotePageState extends State<RemotePage> {
child: getRawPointerAndKeyBody(
keyboard,
Scaffold(
// resizeToAvoidBottomInset: true,
// resizeToAvoidBottomInset: true,
floatingActionButton: !showActionButton
? null
: FloatingActionButton(
@@ -241,14 +241,14 @@ class _RemotePageState extends State<RemotePage> {
onPressed: () {
setState(() {
if (hideKeyboard) {
_showEdit = false;
FFI.invokeMethod("enable_soft_keyboard", false);
_mobileFocusNode.unfocus();
_physicalFocusNode.requestFocus();
} else {
_showBar = !_showBar;
}
});
_showEdit = false;
gFFI.invokeMethod("enable_soft_keyboard", false);
_mobileFocusNode.unfocus();
_physicalFocusNode.requestFocus();
} else {
_showBar = !_showBar;
}
});
}),
bottomNavigationBar: _showBar && pi.displays.length > 0
? getBottomAppBar(keyboard)
@@ -282,7 +282,7 @@ class _RemotePageState extends State<RemotePage> {
});
}
if (_isPhysicalMouse) {
FFI.handleMouse(getEvent(e, 'mousemove'));
gFFI.handleMouse(getEvent(e, 'mousemove'));
}
},
onPointerDown: (e) {
@@ -294,19 +294,19 @@ class _RemotePageState extends State<RemotePage> {
}
}
if (_isPhysicalMouse) {
FFI.handleMouse(getEvent(e, 'mousedown'));
gFFI.handleMouse(getEvent(e, 'mousedown'));
}
},
onPointerUp: (e) {
if (e.kind != ui.PointerDeviceKind.mouse) return;
if (_isPhysicalMouse) {
FFI.handleMouse(getEvent(e, 'mouseup'));
gFFI.handleMouse(getEvent(e, 'mouseup'));
}
},
onPointerMove: (e) {
if (e.kind != ui.PointerDeviceKind.mouse) return;
if (_isPhysicalMouse) {
FFI.handleMouse(getEvent(e, 'mousemove'));
gFFI.handleMouse(getEvent(e, 'mousemove'));
}
},
onPointerSignal: (e) {
@@ -319,7 +319,7 @@ class _RemotePageState extends State<RemotePage> {
if (dy > 0)
dy = -1;
else if (dy < 0) dy = 1;
FFI.setByName(
gFFI.setByName(
'send_mouse', '{"type": "wheel", "x": "$dx", "y": "$dy"}');
}
},
@@ -337,14 +337,14 @@ class _RemotePageState extends State<RemotePage> {
if (e.repeat) {
sendRawKey(e, press: true);
} else {
if (e.isAltPressed && !FFI.alt) {
FFI.alt = true;
} else if (e.isControlPressed && !FFI.ctrl) {
FFI.ctrl = true;
} else if (e.isShiftPressed && !FFI.shift) {
FFI.shift = true;
} else if (e.isMetaPressed && !FFI.command) {
FFI.command = true;
if (e.isAltPressed && !gFFI.alt) {
gFFI.alt = true;
} else if (e.isControlPressed && !gFFI.ctrl) {
gFFI.ctrl = true;
} else if (e.isShiftPressed && !gFFI.shift) {
gFFI.shift = true;
} else if (e.isMetaPressed && !gFFI.command) {
gFFI.command = true;
}
sendRawKey(e, down: true);
}
@@ -353,16 +353,16 @@ class _RemotePageState extends State<RemotePage> {
if (!_showEdit && e is RawKeyUpEvent) {
if (key == LogicalKeyboardKey.altLeft ||
key == LogicalKeyboardKey.altRight) {
FFI.alt = false;
gFFI.alt = false;
} else if (key == LogicalKeyboardKey.controlLeft ||
key == LogicalKeyboardKey.controlRight) {
FFI.ctrl = false;
gFFI.ctrl = false;
} else if (key == LogicalKeyboardKey.shiftRight ||
key == LogicalKeyboardKey.shiftLeft) {
FFI.shift = false;
gFFI.shift = false;
} else if (key == LogicalKeyboardKey.metaLeft ||
key == LogicalKeyboardKey.metaRight) {
FFI.command = false;
gFFI.command = false;
}
sendRawKey(e);
}
@@ -401,32 +401,32 @@ class _RemotePageState extends State<RemotePage> {
] +
(isWebDesktop
? []
: FFI.ffiModel.isPeerAndroid
? [
IconButton(
color: Colors.white,
icon: Icon(Icons.build),
onPressed: () {
if (mobileActionsOverlayEntry == null) {
showMobileActionsOverlay();
} else {
hideMobileActionsOverlay();
}
},
)
]
: [
IconButton(
color: Colors.white,
icon: Icon(Icons.keyboard),
onPressed: openKeyboard),
IconButton(
color: Colors.white,
icon: Icon(FFI.ffiModel.touchMode
? Icons.touch_app
: Icons.mouse),
onPressed: changeTouchMode,
),
: gFFI.ffiModel.isPeerAndroid
? [
IconButton(
color: Colors.white,
icon: Icon(Icons.build),
onPressed: () {
if (mobileActionsOverlayEntry == null) {
showMobileActionsOverlay();
} else {
hideMobileActionsOverlay();
}
},
)
]
: [
IconButton(
color: Colors.white,
icon: Icon(Icons.keyboard),
onPressed: openKeyboard),
IconButton(
color: Colors.white,
icon: Icon(gFFI.ffiModel.touchMode
? Icons.touch_app
: Icons.mouse),
onPressed: changeTouchMode,
),
]) +
(isWeb
? []
@@ -435,10 +435,10 @@ class _RemotePageState extends State<RemotePage> {
color: Colors.white,
icon: Icon(Icons.message),
onPressed: () {
FFI.chatModel
.changeCurrentID(ChatModel.clientModeID);
toggleChatOverlay();
},
gFFI.chatModel
.changeCurrentID(ChatModel.clientModeID);
toggleChatOverlay();
},
)
]) +
[
@@ -473,91 +473,91 @@ class _RemotePageState extends State<RemotePage> {
/// HoldDrag -> left drag
Widget getBodyForMobileWithGesture() {
final touchMode = FFI.ffiModel.touchMode;
final touchMode = gFFI.ffiModel.touchMode;
return getMixinGestureDetector(
child: getBodyForMobile(),
onTapUp: (d) {
if (touchMode) {
FFI.cursorModel.touch(
gFFI.cursorModel.touch(
d.localPosition.dx, d.localPosition.dy, MouseButtons.left);
} else {
FFI.tap(MouseButtons.left);
gFFI.tap(MouseButtons.left);
}
},
onDoubleTapDown: (d) {
if (touchMode) {
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
}
},
onDoubleTap: () {
FFI.tap(MouseButtons.left);
FFI.tap(MouseButtons.left);
gFFI.tap(MouseButtons.left);
gFFI.tap(MouseButtons.left);
},
onLongPressDown: (d) {
if (touchMode) {
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
}
},
onLongPress: () {
FFI.tap(MouseButtons.right);
gFFI.tap(MouseButtons.right);
},
onDoubleFinerTap: (d) {
if (!touchMode) {
FFI.tap(MouseButtons.right);
gFFI.tap(MouseButtons.right);
}
},
onHoldDragStart: (d) {
if (!touchMode) {
FFI.sendMouse('down', MouseButtons.left);
gFFI.sendMouse('down', MouseButtons.left);
}
},
onHoldDragUpdate: (d) {
if (!touchMode) {
FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, touchMode);
gFFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, touchMode);
}
},
onHoldDragEnd: (_) {
if (!touchMode) {
FFI.sendMouse('up', MouseButtons.left);
gFFI.sendMouse('up', MouseButtons.left);
}
},
onOneFingerPanStart: (d) {
if (touchMode) {
FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
FFI.sendMouse('down', MouseButtons.left);
gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
gFFI.sendMouse('down', MouseButtons.left);
}
},
onOneFingerPanUpdate: (d) {
FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, touchMode);
gFFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, touchMode);
},
onOneFingerPanEnd: (d) {
if (touchMode) {
FFI.sendMouse('up', MouseButtons.left);
gFFI.sendMouse('up', MouseButtons.left);
}
},
// scale + pan event
onTwoFingerScaleUpdate: (d) {
FFI.canvasModel.updateScale(d.scale / _scale);
gFFI.canvasModel.updateScale(d.scale / _scale);
_scale = d.scale;
FFI.canvasModel.panX(d.focalPointDelta.dx);
FFI.canvasModel.panY(d.focalPointDelta.dy);
gFFI.canvasModel.panX(d.focalPointDelta.dx);
gFFI.canvasModel.panY(d.focalPointDelta.dy);
},
onTwoFingerScaleEnd: (d) {
_scale = 1;
FFI.setByName('peer_option', '{"name": "view-style", "value": ""}');
gFFI.setByName('peer_option', '{"name": "view-style", "value": ""}');
},
onThreeFingerVerticalDragUpdate: FFI.ffiModel.isPeerAndroid
onThreeFingerVerticalDragUpdate: gFFI.ffiModel.isPeerAndroid
? null
: (d) {
_mouseScrollIntegral += d.delta.dy / 4;
if (_mouseScrollIntegral > 1) {
FFI.scroll(1);
_mouseScrollIntegral = 0;
} else if (_mouseScrollIntegral < -1) {
FFI.scroll(-1);
_mouseScrollIntegral = 0;
}
});
_mouseScrollIntegral += d.delta.dy / 4;
if (_mouseScrollIntegral > 1) {
gFFI.scroll(1);
_mouseScrollIntegral = 0;
} else if (_mouseScrollIntegral < -1) {
gFFI.scroll(-1);
_mouseScrollIntegral = 0;
}
});
}
Widget getBodyForMobile() {
@@ -591,7 +591,7 @@ class _RemotePageState extends State<RemotePage> {
Widget getBodyForDesktopWithListener(bool keyboard) {
var paints = <Widget>[ImagePaint()];
if (keyboard ||
FFI.getByName('toggle_option', 'show-remote-cursor') == 'true') {
gFFI.getByName('toggle_option', 'show-remote-cursor') == 'true') {
paints.add(CursorPaint());
}
return Container(
@@ -605,10 +605,10 @@ class _RemotePageState extends State<RemotePage> {
out['type'] = type;
out['x'] = evt.position.dx;
out['y'] = evt.position.dy;
if (FFI.alt) out['alt'] = 'true';
if (FFI.shift) out['shift'] = 'true';
if (FFI.ctrl) out['ctrl'] = 'true';
if (FFI.command) out['command'] = 'true';
if (gFFI.alt) out['alt'] = 'true';
if (gFFI.shift) out['shift'] = 'true';
if (gFFI.ctrl) out['ctrl'] = 'true';
if (gFFI.command) out['command'] = 'true';
out['buttons'] = evt
.buttons; // left button: 1, right button: 2, middle button: 4, 1 | 2 = 3 (left + right)
if (evt.buttons != 0) {
@@ -624,8 +624,8 @@ class _RemotePageState extends State<RemotePage> {
final x = 120.0;
final y = size.height;
final more = <PopupMenuItem<String>>[];
final pi = FFI.ffiModel.pi;
final perms = FFI.ffiModel.permissions;
final pi = gFFI.ffiModel.pi;
final perms = gFFI.ffiModel.permissions;
if (pi.version.isNotEmpty) {
more.add(PopupMenuItem<String>(
child: Text(translate('Refresh')), value: 'refresh'));
@@ -633,16 +633,16 @@ class _RemotePageState extends State<RemotePage> {
more.add(PopupMenuItem<String>(
child: Row(
children: ([
Container(width: 100.0, child: Text(translate('OS Password'))),
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
showSetOSPassword(false);
},
child: Icon(Icons.edit, color: MyTheme.accent),
)
])),
Container(width: 100.0, child: Text(translate('OS Password'))),
TextButton(
style: flatButtonStyle,
onPressed: () {
Navigator.pop(context);
showSetOSPassword(false);
},
child: Icon(Icons.edit, color: MyTheme.accent),
)
])),
value: 'enter_os_password'));
if (!isWebDesktop) {
if (perms['keyboard'] != false && perms['clipboard'] != false) {
@@ -661,10 +661,10 @@ class _RemotePageState extends State<RemotePage> {
more.add(PopupMenuItem<String>(
child: Text(translate('Insert Lock')), value: 'lock'));
if (pi.platform == 'Windows' &&
FFI.getByName('toggle_option', 'privacy-mode') != 'true') {
gFFI.getByName('toggle_option', 'privacy-mode') != 'true') {
more.add(PopupMenuItem<String>(
child: Text(translate(
(FFI.ffiModel.inputBlocked ? 'Unb' : 'B') + 'lock user input')),
child: Text(translate((gFFI.ffiModel.inputBlocked ? 'Unb' : 'B') +
'lock user input')),
value: 'block-input'));
}
}
@@ -676,31 +676,31 @@ class _RemotePageState extends State<RemotePage> {
elevation: 8,
);
if (value == 'cad') {
FFI.setByName('ctrl_alt_del');
gFFI.setByName('ctrl_alt_del');
} else if (value == 'lock') {
FFI.setByName('lock_screen');
gFFI.setByName('lock_screen');
} else if (value == 'block-input') {
FFI.setByName('toggle_option',
(FFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
FFI.ffiModel.inputBlocked = !FFI.ffiModel.inputBlocked;
gFFI.setByName('toggle_option',
(gFFI.ffiModel.inputBlocked ? 'un' : '') + 'block-input');
gFFI.ffiModel.inputBlocked = !gFFI.ffiModel.inputBlocked;
} else if (value == 'refresh') {
FFI.setByName('refresh');
gFFI.setByName('refresh');
} else if (value == 'paste') {
() async {
ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain);
if (data != null && data.text != null) {
FFI.setByName('input_string', '${data.text}');
gFFI.setByName('input_string', '${data.text}');
}
}();
} else if (value == 'enter_os_password') {
var password = FFI.getByName('peer_option', "os-password");
var password = gFFI.getByName('peer_option', "os-password");
if (password != "") {
FFI.setByName('input_os_password', password);
gFFI.setByName('input_os_password', password);
} else {
showSetOSPassword(true);
}
} else if (value == 'reset_canvas') {
FFI.cursorModel.reset();
gFFI.cursorModel.reset();
}
}();
}
@@ -719,11 +719,11 @@ class _RemotePageState extends State<RemotePage> {
return SingleChildScrollView(
padding: EdgeInsets.symmetric(vertical: 10),
child: GestureHelp(
touchMode: FFI.ffiModel.touchMode,
touchMode: gFFI.ffiModel.touchMode,
onTouchModeChange: (t) {
FFI.ffiModel.toggleTouchMode();
final v = FFI.ffiModel.touchMode ? 'Y' : '';
FFI.setByName('peer_option',
gFFI.ffiModel.toggleTouchMode();
final v = gFFI.ffiModel.touchMode ? 'Y' : '';
gFFI.setByName('peer_option',
'{"name": "touch-mode", "value": "$v"}');
}));
}));
@@ -752,24 +752,24 @@ class _RemotePageState extends State<RemotePage> {
child: icon != null
? Icon(icon, size: 17, color: Colors.white)
: Text(translate(text),
style: TextStyle(color: Colors.white, fontSize: 11)),
style: TextStyle(color: Colors.white, fontSize: 11)),
onPressed: onPressed);
};
final pi = FFI.ffiModel.pi;
final pi = gFFI.ffiModel.pi;
final isMac = pi.platform == "Mac OS";
final modifiers = <Widget>[
wrap('Ctrl ', () {
setState(() => FFI.ctrl = !FFI.ctrl);
}, FFI.ctrl),
setState(() => gFFI.ctrl = !gFFI.ctrl);
}, gFFI.ctrl),
wrap(' Alt ', () {
setState(() => FFI.alt = !FFI.alt);
}, FFI.alt),
setState(() => gFFI.alt = !gFFI.alt);
}, gFFI.alt),
wrap('Shift', () {
setState(() => FFI.shift = !FFI.shift);
}, FFI.shift),
setState(() => gFFI.shift = !gFFI.shift);
}, gFFI.shift),
wrap(isMac ? ' Cmd ' : ' Win ', () {
setState(() => FFI.command = !FFI.command);
}, FFI.command),
setState(() => gFFI.command = !gFFI.command);
}, gFFI.command),
];
final keys = <Widget>[
wrap(
@@ -801,44 +801,44 @@ class _RemotePageState extends State<RemotePage> {
for (var i = 1; i <= 12; ++i) {
final name = 'F' + i.toString();
fn.add(wrap(name, () {
FFI.inputKey('VK_' + name);
gFFI.inputKey('VK_' + name);
}));
}
final more = <Widget>[
SizedBox(width: 9999),
wrap('Esc', () {
FFI.inputKey('VK_ESCAPE');
gFFI.inputKey('VK_ESCAPE');
}),
wrap('Tab', () {
FFI.inputKey('VK_TAB');
gFFI.inputKey('VK_TAB');
}),
wrap('Home', () {
FFI.inputKey('VK_HOME');
gFFI.inputKey('VK_HOME');
}),
wrap('End', () {
FFI.inputKey('VK_END');
gFFI.inputKey('VK_END');
}),
wrap('Del', () {
FFI.inputKey('VK_DELETE');
gFFI.inputKey('VK_DELETE');
}),
wrap('PgUp', () {
FFI.inputKey('VK_PRIOR');
gFFI.inputKey('VK_PRIOR');
}),
wrap('PgDn', () {
FFI.inputKey('VK_NEXT');
gFFI.inputKey('VK_NEXT');
}),
SizedBox(width: 9999),
wrap('', () {
FFI.inputKey('VK_LEFT');
gFFI.inputKey('VK_LEFT');
}, false, Icons.keyboard_arrow_left),
wrap('', () {
FFI.inputKey('VK_UP');
gFFI.inputKey('VK_UP');
}, false, Icons.keyboard_arrow_up),
wrap('', () {
FFI.inputKey('VK_DOWN');
gFFI.inputKey('VK_DOWN');
}, false, Icons.keyboard_arrow_down),
wrap('', () {
FFI.inputKey('VK_RIGHT');
gFFI.inputKey('VK_RIGHT');
}, false, Icons.keyboard_arrow_right),
wrap(isMac ? 'Cmd+C' : 'Ctrl+C', () {
sendPrompt(isMac, 'VK_C');
@@ -871,7 +871,7 @@ class ImagePaint extends StatelessWidget {
Widget build(BuildContext context) {
final m = Provider.of<ImageModel>(context);
final c = Provider.of<CanvasModel>(context);
final adjust = FFI.cursorModel.adjustForKeyboard();
final adjust = gFFI.cursorModel.adjustForKeyboard();
var s = c.scale;
return CustomPaint(
painter: new ImagePainter(
@@ -885,7 +885,7 @@ class CursorPaint extends StatelessWidget {
Widget build(BuildContext context) {
final m = Provider.of<CursorModel>(context);
final c = Provider.of<CanvasModel>(context);
final adjust = FFI.cursorModel.adjustForKeyboard();
final adjust = gFFI.cursorModel.adjustForKeyboard();
var s = c.scale;
return CustomPaint(
painter: new ImagePainter(
@@ -925,10 +925,10 @@ class ImagePainter extends CustomPainter {
CheckboxListTile getToggle(void Function(void Function()) setState, option, name) {
return CheckboxListTile(
value: FFI.getByName('toggle_option', option) == 'true',
value: gFFI.getByName('toggle_option', option) == 'true',
onChanged: (v) {
setState(() {
FFI.setByName('toggle_option', option);
gFFI.setByName('toggle_option', option);
});
},
dense: true,
@@ -948,12 +948,12 @@ RadioListTile<String> getRadio(String name, String toValue, String curValue,
}
void showOptions() {
String quality = FFI.getByName('image_quality');
String quality = gFFI.getByName('image_quality');
if (quality == '') quality = 'balanced';
String viewStyle = FFI.getByName('peer_option', 'view-style');
String viewStyle = gFFI.getByName('peer_option', 'view-style');
var displays = <Widget>[];
final pi = FFI.ffiModel.pi;
final image = FFI.ffiModel.getConnectionImage();
final pi = gFFI.ffiModel.pi;
final image = gFFI.ffiModel.getConnectionImage();
if (image != null)
displays.add(Padding(padding: const EdgeInsets.only(top: 8), child: image));
if (pi.displays.length > 1) {
@@ -963,7 +963,7 @@ void showOptions() {
children.add(InkWell(
onTap: () {
if (i == cur) return;
FFI.setByName('switch_display', i.toString());
gFFI.setByName('switch_display', i.toString());
SmartDialog.dismiss();
},
child: Ink(
@@ -987,7 +987,7 @@ void showOptions() {
if (displays.isNotEmpty) {
displays.add(Divider(color: MyTheme.border));
}
final perms = FFI.ffiModel.permissions;
final perms = gFFI.ffiModel.permissions;
DialogManager.show((setState, close) {
final more = <Widget>[];
@@ -1007,16 +1007,16 @@ void showOptions() {
if (value == null) return;
setState(() {
quality = value;
FFI.setByName('image_quality', value);
gFFI.setByName('image_quality', value);
});
};
var setViewStyle = (String? value) {
if (value == null) return;
setState(() {
viewStyle = value;
FFI.setByName(
gFFI.setByName(
'peer_option', '{"name": "view-style", "value": "$value"}');
FFI.canvasModel.updateViewStyle();
gFFI.canvasModel.updateViewStyle();
});
};
return CustomAlertDialog(
@@ -1044,8 +1044,8 @@ void showOptions() {
void showSetOSPassword(bool login) {
final controller = TextEditingController();
var password = FFI.getByName('peer_option', "os-password");
var autoLogin = FFI.getByName('peer_option', "auto-login") != "";
var password = gFFI.getByName('peer_option', "os-password");
var autoLogin = gFFI.getByName('peer_option', "auto-login") != "";
controller.text = password;
DialogManager.show((setState, close) {
return CustomAlertDialog(
@@ -1078,12 +1078,12 @@ void showSetOSPassword(bool login) {
style: flatButtonStyle,
onPressed: () {
var text = controller.text.trim();
FFI.setByName(
gFFI.setByName(
'peer_option', '{"name": "os-password", "value": "$text"}');
FFI.setByName('peer_option',
gFFI.setByName('peer_option',
'{"name": "auto-login", "value": "${autoLogin ? 'Y' : ''}"}');
if (text != "" && login) {
FFI.setByName('input_os_password', text);
gFFI.setByName('input_os_password', text);
}
close();
},
@@ -1094,17 +1094,17 @@ void showSetOSPassword(bool login) {
}
void sendPrompt(bool isMac, String key) {
final old = isMac ? FFI.command : FFI.ctrl;
final old = isMac ? gFFI.command : gFFI.ctrl;
if (isMac) {
FFI.command = true;
gFFI.command = true;
} else {
FFI.ctrl = true;
gFFI.ctrl = true;
}
FFI.inputKey(key);
gFFI.inputKey(key);
if (isMac) {
FFI.command = old;
gFFI.command = old;
} else {
FFI.ctrl = old;
gFFI.ctrl = old;
}
}

View File

@@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:image_picker/image_picker.dart';
import 'package:image/image.dart' as img;
import 'package:zxing2/qrcode.dart';
import 'dart:io';
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image/image.dart' as img;
import 'package:image_picker/image_picker.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:zxing2/qrcode.dart';
import '../../common.dart';
import '../../models/model.dart';
@@ -153,10 +155,10 @@ class _ScanPageState extends State<ScanPage> {
void showServerSettingsWithValue(
String id, String relay, String key, String api) {
final formKey = GlobalKey<FormState>();
final id0 = FFI.getByName('option', 'custom-rendezvous-server');
final relay0 = FFI.getByName('option', 'relay-server');
final api0 = FFI.getByName('option', 'api-server');
final key0 = FFI.getByName('option', 'key');
final id0 = gFFI.getByName('option', 'custom-rendezvous-server');
final relay0 = gFFI.getByName('option', 'relay-server');
final api0 = gFFI.getByName('option', 'api-server');
final key0 = gFFI.getByName('option', 'key');
DialogManager.show((setState, close) {
return CustomAlertDialog(
title: Text(translate('ID/Relay Server')),
@@ -227,17 +229,17 @@ void showServerSettingsWithValue(
formKey.currentState!.validate()) {
formKey.currentState!.save();
if (id != id0)
FFI.setByName('option',
gFFI.setByName('option',
'{"name": "custom-rendezvous-server", "value": "$id"}');
if (relay != relay0)
FFI.setByName(
gFFI.setByName(
'option', '{"name": "relay-server", "value": "$relay"}');
if (key != key0)
FFI.setByName('option', '{"name": "key", "value": "$key"}');
gFFI.setByName('option', '{"name": "key", "value": "$key"}');
if (api != api0)
FFI.setByName(
gFFI.setByName(
'option', '{"name": "api-server", "value": "$api"}');
FFI.ffiModel.updateUser();
gFFI.ffiModel.updateUser();
close();
}
},
@@ -253,6 +255,6 @@ String? validate(value) {
if (value.isEmpty) {
return null;
}
final res = FFI.getByName('test_if_valid_server', value);
final res = gFFI.getByName('test_if_valid_server', value);
return res.isEmpty ? null : res;
}

View File

@@ -1,13 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
import 'package:flutter_hbb/models/model.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:provider/provider.dart';
import '../../common.dart';
import '../../models/model.dart';
import '../../models/server_model.dart';
import 'home_page.dart';
import '../../models/model.dart';
class ServerPage extends StatelessWidget implements PageShape {
@override
@@ -30,12 +30,12 @@ class ServerPage extends StatelessWidget implements PageShape {
PopupMenuItem(
child: Text(translate("Set your own password")),
value: "changePW",
enabled: FFI.serverModel.isStart,
enabled: gFFI.serverModel.isStart,
),
PopupMenuItem(
child: Text(translate("Refresh random password")),
value: "refreshPW",
enabled: FFI.serverModel.isStart,
enabled: gFFI.serverModel.isStart,
)
];
},
@@ -47,7 +47,7 @@ class ServerPage extends StatelessWidget implements PageShape {
} else if (value == "refreshPW") {
() async {
showLoading(translate("Waiting"));
if (await FFI.serverModel.updatePassword("")) {
if (await gFFI.serverModel.updatePassword("")) {
showSuccess();
} else {
showError();
@@ -62,10 +62,10 @@ class ServerPage extends StatelessWidget implements PageShape {
Widget build(BuildContext context) {
checkService();
return ChangeNotifierProvider.value(
value: FFI.serverModel,
value: gFFI.serverModel,
child: Consumer<ServerModel>(
builder: (context, serverModel, child) => SingleChildScrollView(
controller: FFI.serverModel.controller,
controller: gFFI.serverModel.controller,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
@@ -82,9 +82,9 @@ class ServerPage extends StatelessWidget implements PageShape {
}
void checkService() async {
FFI.invokeMethod("check_service"); // jvm
gFFI.invokeMethod("check_service"); // jvm
// for Android 10/11,MANAGE_EXTERNAL_STORAGE permission from a system setting page
if (PermissionManager.isWaitingFile() && !FFI.serverModel.fileOk) {
if (PermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
PermissionManager.complete("file", await PermissionManager.check("file"));
debugPrint("file permission finished");
}
@@ -96,7 +96,7 @@ class ServerInfo extends StatefulWidget {
}
class _ServerInfoState extends State<ServerInfo> {
final model = FFI.serverModel;
final model = gFFI.serverModel;
var _passwdShow = false;
@override
@@ -327,7 +327,7 @@ class ConnectionManager extends StatelessWidget {
? SizedBox.shrink()
: IconButton(
onPressed: () {
FFI.chatModel
gFFI.chatModel
.changeCurrentID(entry.value.id);
final bar =
navigationBarKey.currentWidget;
@@ -355,8 +355,9 @@ class ConnectionManager extends StatelessWidget {
MaterialStateProperty.all(Colors.red)),
icon: Icon(Icons.close),
onPressed: () {
FFI.setByName("close_conn", entry.key.toString());
FFI.invokeMethod(
gFFI.setByName(
"close_conn", entry.key.toString());
gFFI.invokeMethod(
"cancel_notification", entry.key);
},
label: Text(translate("Close")))
@@ -461,14 +462,14 @@ Widget clientInfo(Client client) {
}
void toAndroidChannelInit() {
FFI.setMethodCallHandler((method, arguments) {
gFFI.setMethodCallHandler((method, arguments) {
debugPrint("flutter got android msg,$method,$arguments");
try {
switch (method) {
case "start_capture":
{
SmartDialog.dismiss();
FFI.serverModel.updateClientState();
gFFI.serverModel.updateClientState();
break;
}
case "on_state_changed":
@@ -476,7 +477,7 @@ void toAndroidChannelInit() {
var name = arguments["name"] as String;
var value = arguments["value"] as String == "true";
debugPrint("from jvm:on_state_changed,$name:$value");
FFI.serverModel.changeStatue(name, value);
gFFI.serverModel.changeStatue(name, value);
break;
}
case "on_android_permission_result":
@@ -488,7 +489,7 @@ void toAndroidChannelInit() {
}
case "on_media_projection_canceled":
{
FFI.serverModel.stopService();
gFFI.serverModel.stopService();
break;
}
}

View File

@@ -1,12 +1,14 @@
import 'package:settings_ui/settings_ui.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:provider/provider.dart';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:settings_ui/settings_ui.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../common.dart';
import '../widgets/dialog.dart';
import '../../models/model.dart';
import '../widgets/dialog.dart';
import 'home_page.dart';
import 'scan_page.dart';
@@ -89,10 +91,10 @@ class _SettingsState extends State<SettingsPage> {
}
void showServerSettings() {
final id = FFI.getByName('option', 'custom-rendezvous-server');
final relay = FFI.getByName('option', 'relay-server');
final api = FFI.getByName('option', 'api-server');
final key = FFI.getByName('option', 'key');
final id = gFFI.getByName('option', 'custom-rendezvous-server');
final relay = gFFI.getByName('option', 'relay-server');
final api = gFFI.getByName('option', 'api-server');
final key = gFFI.getByName('option', 'key');
showServerSettingsWithValue(id, relay, key, api);
}
@@ -145,8 +147,8 @@ fetch('http://localhost:21114/api/login', {
final body = {
'username': name,
'password': pass,
'id': FFI.getByName('server_id'),
'uuid': FFI.getByName('uuid')
'id': gFFI.getByName('server_id'),
'uuid': gFFI.getByName('uuid')
};
try {
final response = await http.post(Uri.parse('${url}/api/login'),
@@ -166,24 +168,25 @@ String parseResp(String body) {
}
final token = data['access_token'];
if (token != null) {
FFI.setByName('option', '{"name": "access_token", "value": "$token"}');
gFFI.setByName('option', '{"name": "access_token", "value": "$token"}');
}
final info = data['user'];
if (info != null) {
final value = json.encode(info);
FFI.setByName('option', json.encode({"name": "user_info", "value": value}));
FFI.ffiModel.updateUser();
gFFI.setByName(
'option', json.encode({"name": "user_info", "value": value}));
gFFI.ffiModel.updateUser();
}
return '';
}
void refreshCurrentUser() async {
final token = FFI.getByName("option", "access_token");
final token = gFFI.getByName("option", "access_token");
if (token == '') return;
final url = getUrl();
final body = {
'id': FFI.getByName('server_id'),
'uuid': FFI.getByName('uuid')
'id': gFFI.getByName('server_id'),
'uuid': gFFI.getByName('uuid')
};
try {
final response = await http.post(Uri.parse('${url}/api/currentUser'),
@@ -204,12 +207,12 @@ void refreshCurrentUser() async {
}
void logout() async {
final token = FFI.getByName("option", "access_token");
final token = gFFI.getByName("option", "access_token");
if (token == '') return;
final url = getUrl();
final body = {
'id': FFI.getByName('server_id'),
'uuid': FFI.getByName('uuid')
'id': gFFI.getByName('server_id'),
'uuid': gFFI.getByName('uuid')
};
try {
await http.post(Uri.parse('${url}/api/logout'),
@@ -225,15 +228,15 @@ void logout() async {
}
void resetToken() {
FFI.setByName('option', '{"name": "access_token", "value": ""}');
FFI.setByName('option', '{"name": "user_info", "value": ""}');
FFI.ffiModel.updateUser();
gFFI.setByName('option', '{"name": "access_token", "value": ""}');
gFFI.setByName('option', '{"name": "user_info", "value": ""}');
gFFI.ffiModel.updateUser();
}
String getUrl() {
var url = FFI.getByName('option', 'api-server');
var url = gFFI.getByName('option', 'api-server');
if (url == '') {
url = FFI.getByName('option', 'custom-rendezvous-server');
url = gFFI.getByName('option', 'custom-rendezvous-server');
if (url != '') {
if (url.contains(':')) {
final tmp = url.split(':');
@@ -323,10 +326,10 @@ void showLogin() {
}
String? getUsername() {
final token = FFI.getByName("option", "access_token");
final token = gFFI.getByName("option", "access_token");
String? username;
if (token != "") {
final info = FFI.getByName("option", "user_info");
final info = gFFI.getByName("option", "user_info");
if (info != "") {
try {
Map<String, dynamic> tmp = json.decode(info);

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import '../../common.dart';
import '../../models/model.dart';
@@ -86,7 +87,7 @@ void updatePasswordDialog() {
? () async {
close();
showLoading(translate("Waiting"));
if (await FFI.serverModel.updatePassword(p0.text)) {
if (await gFFI.serverModel.updatePassword(p0.text)) {
showSuccess();
} else {
showError();
@@ -102,7 +103,7 @@ void updatePasswordDialog() {
void enterPasswordDialog(String id) {
final controller = TextEditingController();
var remember = FFI.getByName('remember', id) == 'true';
var remember = gFFI.getByName('remember', id) == 'true';
DialogManager.show((setState, close) {
return CustomAlertDialog(
title: Text(translate('Password Required')),
@@ -137,7 +138,7 @@ void enterPasswordDialog(String id) {
onPressed: () {
var text = controller.text.trim();
if (text == '') return;
FFI.login(id, text, remember);
gFFI.login(id, text, remember);
close();
showLoading(translate('Logging in...'));
},

View File

@@ -157,7 +157,7 @@ hideChatWindowOverlay() {
toggleChatOverlay() {
if (chatIconOverlayEntry == null || chatWindowOverlayEntry == null) {
FFI.invokeMethod("enable_soft_keyboard", true);
gFFI.invokeMethod("enable_soft_keyboard", true);
showChatIconOverlay();
showChatWindowOverlay();
} else {
@@ -248,12 +248,12 @@ showMobileActionsOverlay() {
position: Offset(left, top),
width: overlayW,
height: overlayH,
onBackPressed: () => FFI.tap(MouseButtons.right),
onHomePressed: () => FFI.tap(MouseButtons.wheel),
onBackPressed: () => gFFI.tap(MouseButtons.right),
onHomePressed: () => gFFI.tap(MouseButtons.wheel),
onRecentPressed: () async {
FFI.sendMouse('down', MouseButtons.wheel);
gFFI.sendMouse('down', MouseButtons.wheel);
await Future.delayed(Duration(milliseconds: 500));
FFI.sendMouse('up', MouseButtons.wheel);
gFFI.sendMouse('up', MouseButtons.wheel);
},
);
});