| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | import 'dart:convert'; | 
					
						
							|  |  |  | import 'dart:io'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter/services.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/common.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/common/shared_state.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/common/widgets/dialog.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/models/model.dart'; | 
					
						
							|  |  |  | import 'package:flutter_hbb/models/platform_model.dart'; | 
					
						
							|  |  |  | import 'package:get/get.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-04 19:43:39 +08:00
										 |  |  | bool isEditOsPassword = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | class TTextMenu { | 
					
						
							|  |  |  |   final Widget child; | 
					
						
							|  |  |  |   final VoidCallback onPressed; | 
					
						
							|  |  |  |   Widget? trailingIcon; | 
					
						
							|  |  |  |   bool divider; | 
					
						
							|  |  |  |   TTextMenu( | 
					
						
							|  |  |  |       {required this.child, | 
					
						
							|  |  |  |       required this.onPressed, | 
					
						
							|  |  |  |       this.trailingIcon, | 
					
						
							|  |  |  |       this.divider = false}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TRadioMenu<T> { | 
					
						
							|  |  |  |   final Widget child; | 
					
						
							|  |  |  |   final T value; | 
					
						
							|  |  |  |   final T groupValue; | 
					
						
							|  |  |  |   final ValueChanged<T?>? onChanged; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TRadioMenu( | 
					
						
							|  |  |  |       {required this.child, | 
					
						
							|  |  |  |       required this.value, | 
					
						
							|  |  |  |       required this.groupValue, | 
					
						
							|  |  |  |       required this.onChanged}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TToggleMenu { | 
					
						
							|  |  |  |   final Widget child; | 
					
						
							|  |  |  |   final bool value; | 
					
						
							|  |  |  |   final ValueChanged<bool?>? onChanged; | 
					
						
							|  |  |  |   TToggleMenu( | 
					
						
							|  |  |  |       {required this.child, required this.value, required this.onChanged}); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  | handleOsPasswordEditIcon( | 
					
						
							|  |  |  |     SessionID sessionId, OverlayDialogManager dialogManager) { | 
					
						
							|  |  |  |   isEditOsPassword = true; | 
					
						
							|  |  |  |   showSetOSPassword(sessionId, false, dialogManager, null, () => isEditOsPassword = false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | handleOsPasswordAction( | 
					
						
							|  |  |  |     SessionID sessionId, OverlayDialogManager dialogManager) async { | 
					
						
							|  |  |  |   if (isEditOsPassword) { | 
					
						
							|  |  |  |     isEditOsPassword = false; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   final password = | 
					
						
							|  |  |  |       await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ?? | 
					
						
							|  |  |  |           ''; | 
					
						
							|  |  |  |   if (password.isEmpty) { | 
					
						
							|  |  |  |     showSetOSPassword(sessionId, true, dialogManager, password, () => isEditOsPassword = false); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     bind.sessionInputOsPassword(sessionId: sessionId, value: password); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | List<TTextMenu> toolbarControls(BuildContext context, String id, FFI ffi) { | 
					
						
							|  |  |  |   final ffiModel = ffi.ffiModel; | 
					
						
							|  |  |  |   final pi = ffiModel.pi; | 
					
						
							|  |  |  |   final perms = ffiModel.permissions; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final sessionId = ffi.sessionId; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   List<TTextMenu> v = []; | 
					
						
							|  |  |  |   // elevation
 | 
					
						
							|  |  |  |   if (ffi.elevationModel.showRequestMenu) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Request Elevation')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => | 
					
						
							|  |  |  |               showRequestElevationDialog(sessionId, ffi.dialogManager)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // osAccount / osPassword
 | 
					
						
							|  |  |  |   v.add( | 
					
						
							|  |  |  |     TTextMenu( | 
					
						
							| 
									
										
										
										
											2023-07-04 20:26:43 +08:00
										 |  |  |       child: Row(children: [ | 
					
						
							|  |  |  |         Text(translate(pi.is_headless ? 'OS Account' : 'OS Password')), | 
					
						
							|  |  |  |         Offstage( | 
					
						
							|  |  |  |           offstage: isDesktop, | 
					
						
							|  |  |  |           child: Icon(Icons.edit, color: MyTheme.accent).marginOnly(left: 12), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |       ]), | 
					
						
							|  |  |  |       trailingIcon: Transform.scale( | 
					
						
							|  |  |  |         scale: 0.8, | 
					
						
							|  |  |  |         child: InkWell( | 
					
						
							|  |  |  |           onTap: () => pi.is_headless | 
					
						
							|  |  |  |               ? showSetOSAccount(sessionId, ffi.dialogManager) | 
					
						
							|  |  |  |               : handleOsPasswordEditIcon(sessionId, ffi.dialogManager), | 
					
						
							|  |  |  |           child: Icon(Icons.edit), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |       onPressed: () => pi.is_headless | 
					
						
							|  |  |  |           ? showSetOSAccount(sessionId, ffi.dialogManager) | 
					
						
							|  |  |  |           : handleOsPasswordAction(sessionId, ffi.dialogManager), | 
					
						
							|  |  |  |     ), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   ); | 
					
						
							|  |  |  |   // paste
 | 
					
						
							|  |  |  |   if (isMobile && perms['keyboard'] != false && perms['clipboard'] != false) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Paste')), | 
					
						
							|  |  |  |         onPressed: () async { | 
					
						
							|  |  |  |           ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); | 
					
						
							|  |  |  |           if (data != null && data.text != null) { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |             bind.sessionInputString( | 
					
						
							|  |  |  |                 sessionId: sessionId, value: data.text ?? ""); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |           } | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // reset canvas
 | 
					
						
							|  |  |  |   if (isMobile) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Reset canvas')), | 
					
						
							|  |  |  |         onPressed: () => ffi.cursorModel.reset())); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // transferFile
 | 
					
						
							|  |  |  |   if (isDesktop) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Transfer File')), | 
					
						
							|  |  |  |           onPressed: () => connect(context, id, isFileTransfer: true)), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // tcpTunneling
 | 
					
						
							|  |  |  |   if (isDesktop) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('TCP Tunneling')), | 
					
						
							|  |  |  |           onPressed: () => connect(context, id, isTcpTunneling: true)), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // note
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   if (bind | 
					
						
							|  |  |  |       .sessionGetAuditServerSync(sessionId: sessionId, typ: "conn") | 
					
						
							|  |  |  |       .isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Note')), | 
					
						
							| 
									
										
										
										
											2023-07-01 09:33:48 +08:00
										 |  |  |           onPressed: () => showAuditDialog(ffi)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // divider
 | 
					
						
							|  |  |  |   if (isDesktop) { | 
					
						
							|  |  |  |     v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // ctrlAltDel
 | 
					
						
							|  |  |  |   if (!ffiModel.viewOnly && | 
					
						
							|  |  |  |       ffiModel.keyboard && | 
					
						
							|  |  |  |       (pi.platform == kPeerPlatformLinux || pi.sasEnabled)) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text('${translate("Insert")} Ctrl + Alt + Del'), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => bind.sessionCtrlAltDel(sessionId: sessionId)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // restart
 | 
					
						
							|  |  |  |   if (perms['restart'] != false && | 
					
						
							|  |  |  |       (pi.platform == kPeerPlatformLinux || | 
					
						
							|  |  |  |           pi.platform == kPeerPlatformWindows || | 
					
						
							|  |  |  |           pi.platform == kPeerPlatformMacOS)) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Restart Remote Device')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => | 
					
						
							|  |  |  |               showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // insertLock
 | 
					
						
							|  |  |  |   if (!ffiModel.viewOnly && ffi.ffiModel.keyboard) { | 
					
						
							|  |  |  |     v.add( | 
					
						
							|  |  |  |       TTextMenu( | 
					
						
							|  |  |  |           child: Text(translate('Insert Lock')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           onPressed: () => bind.sessionLockScreen(sessionId: sessionId)), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // blockUserInput
 | 
					
						
							|  |  |  |   if (ffi.ffiModel.keyboard && | 
					
						
							|  |  |  |       pi.platform == kPeerPlatformWindows) // privacy-mode != true ??
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Obx(() => Text(translate( | 
					
						
							|  |  |  |             '${BlockInputState.find(id).value ? 'Unb' : 'B'}lock user input'))), | 
					
						
							|  |  |  |         onPressed: () { | 
					
						
							|  |  |  |           RxBool blockInput = BlockInputState.find(id); | 
					
						
							|  |  |  |           bind.sessionToggleOption( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |               sessionId: sessionId, | 
					
						
							|  |  |  |               value: '${blockInput.value ? 'un' : ''}block-input'); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |           blockInput.value = !blockInput.value; | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // switchSides
 | 
					
						
							|  |  |  |   if (isDesktop && | 
					
						
							|  |  |  |       ffiModel.keyboard && | 
					
						
							|  |  |  |       pi.platform != kPeerPlatformAndroid && | 
					
						
							|  |  |  |       pi.platform != kPeerPlatformMacOS && | 
					
						
							|  |  |  |       version_cmp(pi.version, '1.2.0') >= 0) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Switch Sides')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         onPressed: () => | 
					
						
							|  |  |  |             showConfirmSwitchSidesDialog(sessionId, id, ffi.dialogManager))); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // refresh
 | 
					
						
							|  |  |  |   if (pi.version.isNotEmpty) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Text(translate('Refresh')), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         onPressed: () => bind.sessionRefresh(sessionId: sessionId))); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // record
 | 
					
						
							|  |  |  |   var codecFormat = ffi.qualityMonitorModel.data.codecFormat; | 
					
						
							|  |  |  |   if (!isDesktop && | 
					
						
							|  |  |  |       (ffi.recordingModel.start || | 
					
						
							|  |  |  |           (perms["recording"] != false && | 
					
						
							|  |  |  |               (codecFormat == "VP8" || codecFormat == "VP9")))) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |         child: Row( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Text(translate(ffi.recordingModel.start | 
					
						
							|  |  |  |                 ? 'Stop session recording' | 
					
						
							|  |  |  |                 : 'Start session recording')), | 
					
						
							|  |  |  |             Padding( | 
					
						
							|  |  |  |               padding: EdgeInsets.only(left: 12), | 
					
						
							|  |  |  |               child: Icon( | 
					
						
							|  |  |  |                   ffi.recordingModel.start | 
					
						
							|  |  |  |                       ? Icons.pause_circle_filled | 
					
						
							|  |  |  |                       : Icons.videocam_outlined, | 
					
						
							|  |  |  |                   color: MyTheme.accent), | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         onPressed: () => ffi.recordingModel.toggle())); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-19 14:39:22 +08:00
										 |  |  |   // fingerprint
 | 
					
						
							|  |  |  |   if (!isDesktop) { | 
					
						
							|  |  |  |     v.add(TTextMenu( | 
					
						
							|  |  |  |       child: Text(translate('Copy Fingerprint')), | 
					
						
							|  |  |  |       onPressed: () => onCopyFingerprint(FingerprintState.find(id).value), | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   return v; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<List<TRadioMenu<String>>> toolbarViewStyle( | 
					
						
							|  |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final groupValue = | 
					
						
							|  |  |  |       await bind.sessionGetViewStyle(sessionId: ffi.sessionId) ?? ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   void onChanged(String? value) async { | 
					
						
							|  |  |  |     if (value == null) return; | 
					
						
							|  |  |  |     bind | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         .sessionSetViewStyle(sessionId: ffi.sessionId, value: value) | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         .then((_) => ffi.canvasModel.updateViewStyle()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return [ | 
					
						
							|  |  |  |     TRadioMenu<String>( | 
					
						
							|  |  |  |         child: Text(translate('Scale original')), | 
					
						
							|  |  |  |         value: kRemoteViewStyleOriginal, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: onChanged), | 
					
						
							|  |  |  |     TRadioMenu<String>( | 
					
						
							|  |  |  |         child: Text(translate('Scale adaptive')), | 
					
						
							|  |  |  |         value: kRemoteViewStyleAdaptive, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: onChanged) | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<List<TRadioMenu<String>>> toolbarImageQuality( | 
					
						
							|  |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final groupValue = | 
					
						
							|  |  |  |       await bind.sessionGetImageQuality(sessionId: ffi.sessionId) ?? ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   onChanged(String? value) async { | 
					
						
							|  |  |  |     if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     await bind.sessionSetImageQuality(sessionId: ffi.sessionId, value: value); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return [ | 
					
						
							|  |  |  |     TRadioMenu<String>( | 
					
						
							|  |  |  |         child: Text(translate('Good image quality')), | 
					
						
							|  |  |  |         value: kRemoteImageQualityBest, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: onChanged), | 
					
						
							|  |  |  |     TRadioMenu<String>( | 
					
						
							|  |  |  |         child: Text(translate('Balanced')), | 
					
						
							|  |  |  |         value: kRemoteImageQualityBalanced, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: onChanged), | 
					
						
							|  |  |  |     TRadioMenu<String>( | 
					
						
							|  |  |  |         child: Text(translate('Optimize reaction time')), | 
					
						
							|  |  |  |         value: kRemoteImageQualityLow, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: onChanged), | 
					
						
							|  |  |  |     TRadioMenu<String>( | 
					
						
							|  |  |  |       child: Text(translate('Custom')), | 
					
						
							|  |  |  |       value: kRemoteImageQualityCustom, | 
					
						
							|  |  |  |       groupValue: groupValue, | 
					
						
							|  |  |  |       onChanged: (value) { | 
					
						
							|  |  |  |         onChanged(value); | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         customImageQualityDialog(ffi.sessionId, id, ffi); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<List<TRadioMenu<String>>> toolbarCodec( | 
					
						
							|  |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final sessionId = ffi.sessionId; | 
					
						
							|  |  |  |   final alternativeCodecs = | 
					
						
							|  |  |  |       await bind.sessionAlternativeCodecs(sessionId: sessionId); | 
					
						
							|  |  |  |   final groupValue = await bind.sessionGetOption( | 
					
						
							|  |  |  |           sessionId: sessionId, arg: 'codec-preference') ?? | 
					
						
							|  |  |  |       ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   final List<bool> codecs = []; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     final Map codecsJson = jsonDecode(alternativeCodecs); | 
					
						
							|  |  |  |     final vp8 = codecsJson['vp8'] ?? false; | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     final av1 = codecsJson['av1'] ?? false; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     final h264 = codecsJson['h264'] ?? false; | 
					
						
							|  |  |  |     final h265 = codecsJson['h265'] ?? false; | 
					
						
							|  |  |  |     codecs.add(vp8); | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     codecs.add(av1); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     codecs.add(h264); | 
					
						
							|  |  |  |     codecs.add(h265); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     debugPrint("Show Codec Preference err=$e"); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |   final visible = | 
					
						
							|  |  |  |       codecs.length == 4 && (codecs[0] || codecs[1] || codecs[2] || codecs[3]); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   if (!visible) return []; | 
					
						
							|  |  |  |   onChanged(String? value) async { | 
					
						
							|  |  |  |     if (value == null) return; | 
					
						
							|  |  |  |     await bind.sessionPeerOption( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         sessionId: sessionId, name: 'codec-preference', value: value); | 
					
						
							|  |  |  |     bind.sessionChangePreferCodec(sessionId: sessionId); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TRadioMenu<String> radio(String label, String value, bool enabled) { | 
					
						
							|  |  |  |     return TRadioMenu<String>( | 
					
						
							|  |  |  |         child: Text(translate(label)), | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         groupValue: groupValue, | 
					
						
							|  |  |  |         onChanged: enabled ? onChanged : null); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return [ | 
					
						
							|  |  |  |     radio('Auto', 'auto', true), | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     if (codecs[0]) radio('VP8', 'vp8', codecs[0]), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     radio('VP9', 'vp9', true), | 
					
						
							| 
									
										
										
										
											2023-05-08 20:35:24 +08:00
										 |  |  |     if (codecs[1]) radio('AV1', 'av1', codecs[1]), | 
					
						
							|  |  |  |     if (codecs[2]) radio('H264', 'h264', codecs[2]), | 
					
						
							|  |  |  |     if (codecs[3]) radio('H265', 'h265', codecs[3]), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   ]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Future<List<TToggleMenu>> toolbarDisplayToggle( | 
					
						
							|  |  |  |     BuildContext context, String id, FFI ffi) async { | 
					
						
							|  |  |  |   List<TToggleMenu> v = []; | 
					
						
							|  |  |  |   final ffiModel = ffi.ffiModel; | 
					
						
							|  |  |  |   final pi = ffiModel.pi; | 
					
						
							|  |  |  |   final perms = ffiModel.permissions; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final sessionId = ffi.sessionId; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // show remote cursor
 | 
					
						
							|  |  |  |   if (pi.platform != kPeerPlatformAndroid && | 
					
						
							|  |  |  |       !ffi.canvasModel.cursorEmbedded && | 
					
						
							|  |  |  |       !pi.is_wayland) { | 
					
						
							|  |  |  |     final state = ShowRemoteCursorState.find(id); | 
					
						
							|  |  |  |     final enabled = !ffiModel.viewOnly; | 
					
						
							|  |  |  |     final option = 'show-remote-cursor'; | 
					
						
							|  |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         child: Text(translate('Show remote cursor')), | 
					
						
							|  |  |  |         value: state.value, | 
					
						
							|  |  |  |         onChanged: enabled | 
					
						
							|  |  |  |             ? (value) async { | 
					
						
							|  |  |  |                 if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |                 await bind.sessionToggleOption( | 
					
						
							|  |  |  |                     sessionId: sessionId, value: option); | 
					
						
							|  |  |  |                 state.value = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |                     sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             : null)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // zoom cursor
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   final viewStyle = await bind.sessionGetViewStyle(sessionId: sessionId) ?? ''; | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |   if (!isMobile && | 
					
						
							|  |  |  |       pi.platform != kPeerPlatformAndroid && | 
					
						
							|  |  |  |       viewStyle != kRemoteViewStyleOriginal) { | 
					
						
							|  |  |  |     final option = 'zoom-cursor'; | 
					
						
							|  |  |  |     final peerState = PeerBoolOption.find(id, option); | 
					
						
							|  |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |       child: Text(translate('Zoom cursor')), | 
					
						
							|  |  |  |       value: peerState.value, | 
					
						
							|  |  |  |       onChanged: (value) async { | 
					
						
							|  |  |  |         if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         await bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |         peerState.value = | 
					
						
							|  |  |  |             bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |     )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // show quality monitor
 | 
					
						
							|  |  |  |   final option = 'show-quality-monitor'; | 
					
						
							|  |  |  |   v.add(TToggleMenu( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |       value: bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option), | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       onChanged: (value) async { | 
					
						
							|  |  |  |         if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         await bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							|  |  |  |         ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |       }, | 
					
						
							|  |  |  |       child: Text(translate('Show quality monitor')))); | 
					
						
							|  |  |  |   // mute
 | 
					
						
							|  |  |  |   if (perms['audio'] != false) { | 
					
						
							|  |  |  |     final option = 'disable-audio'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: (value) { | 
					
						
							|  |  |  |           if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Mute')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // file copy and paste
 | 
					
						
							|  |  |  |   if (Platform.isWindows && | 
					
						
							|  |  |  |       pi.platform == kPeerPlatformWindows && | 
					
						
							|  |  |  |       perms['file'] != false) { | 
					
						
							|  |  |  |     final option = 'enable-file-transfer'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: (value) { | 
					
						
							|  |  |  |           if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Allow file copy and paste')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // disable clipboard
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard && perms['clipboard'] != false) { | 
					
						
							|  |  |  |     final enabled = !ffiModel.viewOnly; | 
					
						
							|  |  |  |     final option = 'disable-clipboard'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     var value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     if (ffiModel.viewOnly) value = true; | 
					
						
							|  |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: enabled | 
					
						
							|  |  |  |             ? (value) { | 
					
						
							|  |  |  |                 if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |                 bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             : null, | 
					
						
							|  |  |  |         child: Text(translate('Disable clipboard')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // lock after session end
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard) { | 
					
						
							|  |  |  |     final option = 'lock-after-session-end'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: (value) { | 
					
						
							|  |  |  |           if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Lock after session end')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // privacy mode
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard && pi.features.privacyMode) { | 
					
						
							|  |  |  |     final option = 'privacy-mode'; | 
					
						
							|  |  |  |     final rxValue = PrivacyModeState.find(id); | 
					
						
							|  |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: rxValue.value, | 
					
						
							|  |  |  |         onChanged: (value) { | 
					
						
							|  |  |  |           if (value == null) return; | 
					
						
							|  |  |  |           if (ffiModel.pi.currentDisplay != 0) { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |             msgBox(sessionId, 'custom-nook-nocancel-hasclose', 'info', | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |                 'Please switch to Display 1 first', '', ffi.dialogManager); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Privacy mode')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // swap key
 | 
					
						
							|  |  |  |   if (ffiModel.keyboard && | 
					
						
							|  |  |  |       ((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) || | 
					
						
							|  |  |  |           (!Platform.isMacOS && pi.platform == kPeerPlatformMacOS))) { | 
					
						
							|  |  |  |     final option = 'allow_swap_key'; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     final value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     v.add(TToggleMenu( | 
					
						
							|  |  |  |         value: value, | 
					
						
							|  |  |  |         onChanged: (value) { | 
					
						
							|  |  |  |           if (value == null) return; | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           bind.sessionToggleOption(sessionId: sessionId, value: option); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |         }, | 
					
						
							|  |  |  |         child: Text(translate('Swap control-command key')))); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return v; | 
					
						
							|  |  |  | } |