| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							|  |  |  | import 'dart:convert'; | 
					
						
							|  |  |  | import 'dart:io'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:image/image.dart' as img; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | import 'package:image_picker/image_picker.dart'; | 
					
						
							|  |  |  | import 'package:qr_code_scanner/qr_code_scanner.dart'; | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  | import 'package:zxing2/qrcode.dart'; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-24 23:33:00 +08:00
										 |  |  | import '../../common.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  | import '../../models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ScanPage extends StatefulWidget { | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   _ScanPageState createState() => _ScanPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _ScanPageState extends State<ScanPage> { | 
					
						
							|  |  |  |   QRViewController? controller; | 
					
						
							|  |  |  |   final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // In order to get hot reload to work we need to pause the camera if the platform
 | 
					
						
							|  |  |  |   // is android, or resume the camera if the platform is iOS.
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void reassemble() { | 
					
						
							|  |  |  |     super.reassemble(); | 
					
						
							|  |  |  |     if (isAndroid) { | 
					
						
							|  |  |  |       controller!.pauseCamera(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     controller!.resumeCamera(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     return Scaffold( | 
					
						
							|  |  |  |         appBar: AppBar( | 
					
						
							|  |  |  |           title: const Text('Scan QR'), | 
					
						
							|  |  |  |           actions: [ | 
					
						
							|  |  |  |             IconButton( | 
					
						
							|  |  |  |                 color: Colors.white, | 
					
						
							|  |  |  |                 icon: Icon(Icons.image_search), | 
					
						
							|  |  |  |                 iconSize: 32.0, | 
					
						
							|  |  |  |                 onPressed: () async { | 
					
						
							|  |  |  |                   final ImagePicker _picker = ImagePicker(); | 
					
						
							|  |  |  |                   final XFile? file = | 
					
						
							|  |  |  |                       await _picker.pickImage(source: ImageSource.gallery); | 
					
						
							|  |  |  |                   if (file != null) { | 
					
						
							|  |  |  |                     var image = img.decodeNamedImage( | 
					
						
							|  |  |  |                         File(file.path).readAsBytesSync(), file.path)!; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     LuminanceSource source = RGBLuminanceSource( | 
					
						
							|  |  |  |                         image.width, | 
					
						
							|  |  |  |                         image.height, | 
					
						
							|  |  |  |                         image | 
					
						
							|  |  |  |                             .getBytes(format: img.Format.abgr) | 
					
						
							|  |  |  |                             .buffer | 
					
						
							|  |  |  |                             .asInt32List()); | 
					
						
							|  |  |  |                     var bitmap = BinaryBitmap(HybridBinarizer(source)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     var reader = QRCodeReader(); | 
					
						
							|  |  |  |                     try { | 
					
						
							|  |  |  |                       var result = reader.decode(bitmap); | 
					
						
							|  |  |  |                       showServerSettingFromQr(result.text); | 
					
						
							|  |  |  |                     } catch (e) { | 
					
						
							| 
									
										
										
										
											2022-08-15 14:39:31 +08:00
										 |  |  |                       showToast('No QR code found'); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                     } | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 }), | 
					
						
							|  |  |  |             IconButton( | 
					
						
							|  |  |  |                 color: Colors.yellow, | 
					
						
							|  |  |  |                 icon: Icon(Icons.flash_on), | 
					
						
							|  |  |  |                 iconSize: 32.0, | 
					
						
							|  |  |  |                 onPressed: () async { | 
					
						
							|  |  |  |                   await controller?.toggleFlash(); | 
					
						
							|  |  |  |                 }), | 
					
						
							|  |  |  |             IconButton( | 
					
						
							|  |  |  |               color: Colors.white, | 
					
						
							|  |  |  |               icon: Icon(Icons.switch_camera), | 
					
						
							|  |  |  |               iconSize: 32.0, | 
					
						
							|  |  |  |               onPressed: () async { | 
					
						
							|  |  |  |                 await controller?.flipCamera(); | 
					
						
							|  |  |  |               }, | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         body: _buildQrView(context)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildQrView(BuildContext context) { | 
					
						
							|  |  |  |     // For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
 | 
					
						
							|  |  |  |     var scanArea = (MediaQuery.of(context).size.width < 400 || | 
					
						
							|  |  |  |             MediaQuery.of(context).size.height < 400) | 
					
						
							|  |  |  |         ? 150.0 | 
					
						
							|  |  |  |         : 300.0; | 
					
						
							|  |  |  |     // To ensure the Scanner view is properly sizes after rotation
 | 
					
						
							|  |  |  |     // we need to listen for Flutter SizeChanged notification and update controller
 | 
					
						
							|  |  |  |     return QRView( | 
					
						
							|  |  |  |       key: qrKey, | 
					
						
							|  |  |  |       onQRViewCreated: _onQRViewCreated, | 
					
						
							|  |  |  |       overlay: QrScannerOverlayShape( | 
					
						
							|  |  |  |           borderColor: Colors.red, | 
					
						
							|  |  |  |           borderRadius: 10, | 
					
						
							|  |  |  |           borderLength: 30, | 
					
						
							|  |  |  |           borderWidth: 10, | 
					
						
							|  |  |  |           cutOutSize: scanArea), | 
					
						
							|  |  |  |       onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _onQRViewCreated(QRViewController controller) { | 
					
						
							|  |  |  |     setState(() { | 
					
						
							|  |  |  |       this.controller = controller; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     controller.scannedDataStream.listen((scanData) { | 
					
						
							|  |  |  |       if (scanData.code != null) { | 
					
						
							|  |  |  |         showServerSettingFromQr(scanData.code!); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) { | 
					
						
							|  |  |  |     if (!p) { | 
					
						
							| 
									
										
										
										
											2022-08-15 14:39:31 +08:00
										 |  |  |       showToast('No permission'); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							|  |  |  |     controller?.dispose(); | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void showServerSettingFromQr(String data) async { | 
					
						
							| 
									
										
										
										
											2022-08-16 21:27:21 +08:00
										 |  |  |     closeConnection(); | 
					
						
							| 
									
										
										
										
											2022-04-18 17:01:45 +08:00
										 |  |  |     await controller?.pauseCamera(); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |     if (!data.startsWith('config=')) { | 
					
						
							| 
									
										
										
										
											2022-08-15 14:39:31 +08:00
										 |  |  |       showToast('Invalid QR code'); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       Map<String, dynamic> values = json.decode(data.substring(7)); | 
					
						
							|  |  |  |       var host = values['host'] != null ? values['host'] as String : ''; | 
					
						
							|  |  |  |       var key = values['key'] != null ? values['key'] as String : ''; | 
					
						
							|  |  |  |       var api = values['api'] != null ? values['api'] as String : ''; | 
					
						
							| 
									
										
										
										
											2022-04-18 17:01:45 +08:00
										 |  |  |       Timer(Duration(milliseconds: 60), () { | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |         showServerSettingsWithValue(host, '', key, api, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2022-04-18 17:01:45 +08:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2022-08-15 14:39:31 +08:00
										 |  |  |       showToast('Invalid QR code'); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  | void showServerSettingsWithValue(String id, String relay, String key, | 
					
						
							|  |  |  |     String api, OverlayDialogManager dialogManager) async { | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |   Map<String, dynamic> oldOptions = jsonDecode(await bind.mainGetOptions()); | 
					
						
							|  |  |  |   String id0 = oldOptions['custom-rendezvous-server'] ?? ""; | 
					
						
							|  |  |  |   String relay0 = oldOptions['relay-server'] ?? ""; | 
					
						
							|  |  |  |   String api0 = oldOptions['api-server'] ?? ""; | 
					
						
							|  |  |  |   String key0 = oldOptions['key'] ?? ""; | 
					
						
							|  |  |  |   var isInProgress = false; | 
					
						
							|  |  |  |   final idController = TextEditingController(text: id); | 
					
						
							|  |  |  |   final relayController = TextEditingController(text: relay); | 
					
						
							|  |  |  |   final apiController = TextEditingController(text: api); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   String? idServerMsg; | 
					
						
							|  |  |  |   String? relayServerMsg; | 
					
						
							|  |  |  |   String? apiServerMsg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |   dialogManager.show((setState, close) { | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |     Future<bool> validate() async { | 
					
						
							|  |  |  |       if (idController.text != id) { | 
					
						
							|  |  |  |         final res = await validateAsync(idController.text); | 
					
						
							|  |  |  |         setState(() => idServerMsg = res); | 
					
						
							|  |  |  |         if (idServerMsg != null) return false; | 
					
						
							|  |  |  |         id = idController.text; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (relayController.text != relay) { | 
					
						
							|  |  |  |         relayServerMsg = await validateAsync(relayController.text); | 
					
						
							|  |  |  |         if (relayServerMsg != null) return false; | 
					
						
							|  |  |  |         relay = relayController.text; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (apiController.text != relay) { | 
					
						
							|  |  |  |         apiServerMsg = await validateAsync(apiController.text); | 
					
						
							|  |  |  |         if (apiServerMsg != null) return false; | 
					
						
							|  |  |  |         api = apiController.text; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |     return CustomAlertDialog( | 
					
						
							|  |  |  |       title: Text(translate('ID/Relay Server')), | 
					
						
							|  |  |  |       content: Form( | 
					
						
							|  |  |  |           child: Column( | 
					
						
							|  |  |  |               mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |               children: <Widget>[ | 
					
						
							|  |  |  |                     TextFormField( | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                       controller: idController, | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                       decoration: InputDecoration( | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                           labelText: translate('ID Server'), | 
					
						
							|  |  |  |                           errorText: idServerMsg), | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                     ) | 
					
						
							|  |  |  |                   ] + | 
					
						
							|  |  |  |                   (isAndroid | 
					
						
							|  |  |  |                       ? [ | 
					
						
							|  |  |  |                           TextFormField( | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                             controller: relayController, | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                             decoration: InputDecoration( | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                                 labelText: translate('Relay Server'), | 
					
						
							|  |  |  |                                 errorText: relayServerMsg), | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                           ) | 
					
						
							|  |  |  |                         ] | 
					
						
							|  |  |  |                       : []) + | 
					
						
							|  |  |  |                   [ | 
					
						
							|  |  |  |                     TextFormField( | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                       controller: apiController, | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                       decoration: InputDecoration( | 
					
						
							|  |  |  |                         labelText: translate('API Server'), | 
					
						
							|  |  |  |                       ), | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                       autovalidateMode: AutovalidateMode.onUserInteraction, | 
					
						
							|  |  |  |                       validator: (v) { | 
					
						
							|  |  |  |                         if (v != null && v.length > 0) { | 
					
						
							|  |  |  |                           if (!(v.startsWith('http://') || | 
					
						
							|  |  |  |                               v.startsWith("https://"))) { | 
					
						
							|  |  |  |                             return translate("invalid_http"); | 
					
						
							|  |  |  |                           } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         return apiServerMsg; | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                       }, | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     TextFormField( | 
					
						
							|  |  |  |                       initialValue: key, | 
					
						
							|  |  |  |                       decoration: InputDecoration( | 
					
						
							|  |  |  |                         labelText: 'Key', | 
					
						
							|  |  |  |                       ), | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                       onChanged: (String? value) { | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                         if (value != null) key = value.trim(); | 
					
						
							|  |  |  |                       }, | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                     Offstage( | 
					
						
							|  |  |  |                         offstage: !isInProgress, | 
					
						
							|  |  |  |                         child: LinearProgressIndicator()) | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |                   ])), | 
					
						
							|  |  |  |       actions: [ | 
					
						
							|  |  |  |         TextButton( | 
					
						
							|  |  |  |           style: flatButtonStyle, | 
					
						
							|  |  |  |           onPressed: () { | 
					
						
							|  |  |  |             close(); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           child: Text(translate('Cancel')), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         TextButton( | 
					
						
							|  |  |  |           style: flatButtonStyle, | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |           onPressed: () async { | 
					
						
							|  |  |  |             setState(() { | 
					
						
							|  |  |  |               idServerMsg = null; | 
					
						
							|  |  |  |               relayServerMsg = null; | 
					
						
							|  |  |  |               apiServerMsg = null; | 
					
						
							|  |  |  |               isInProgress = true; | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             if (await validate()) { | 
					
						
							|  |  |  |               if (id != id0) { | 
					
						
							|  |  |  |                 bind.mainSetOption(key: "custom-rendezvous-server", value: id); | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |               if (relay != relay0) { | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                 bind.mainSetOption(key: "relay-server", value: relay); | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |               if (key != key0) bind.mainSetOption(key: "key", value: key); | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |               if (api != api0) { | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |                 bind.mainSetOption(key: "api-server", value: api); | 
					
						
							| 
									
										
										
										
											2022-10-09 19:41:50 +09:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |               close(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |             setState(() { | 
					
						
							|  |  |  |               isInProgress = false; | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |           }, | 
					
						
							|  |  |  |           child: Text(translate('OK')), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-04-21 10:02:47 +08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  | Future<String?> validateAsync(String value) async { | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |   value = value.trim(); | 
					
						
							|  |  |  |   if (value.isEmpty) { | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-08 17:53:51 +08:00
										 |  |  |   final res = await bind.mainTestIfValidServer(server: value); | 
					
						
							| 
									
										
										
										
											2022-04-15 17:50:15 +08:00
										 |  |  |   return res.isEmpty ? null : res; | 
					
						
							|  |  |  | } |