Update Gradle, replace QR code reader library (#162)
I ran the Gradle Upgrade Assistant to get us on the latest version of gradle, but two of our dependencies didn't support it.
- https://pub.dev/documentation/package_info/latest/
- https://github.com/AmolGangadhare/flutter_barcode_scanner
`pacakge_info` is officially deprecated and replaced by `package_info_plus`, which is what I've swapped to here.
A bigger change was switching to https://github.com/juliansteenbakker/mobile_scanner. It does seem to work a bit better than the other one, and does not throw an error now when cancelling the QR code collection, as it did before. I've tested on android in the simulator, and iOS with an actual device.
To test adding a cert:
1. Create a CA on your computer with `nebula-cert ca -name test-mobile`. This will create a `ca.crt` and `ca.key`
2. Tap the + button in the mobile app to add a site
3. Tap the "Certificate" row
4. Copy the public key to a file on your computer like `test.pub`
5. Create a signed cert with `nebula-cert sign -name test-mobile -ip 192.168.0.20/24 -in-pub test.pub`
6. Create a QR code for it: `nebula-cert print -out-qr "qr.png" -path ./test-mobile.crt`
7. In Android studio, in the "Running devices" tab, open the simulator's extended controls:
<img width="509" alt="Android Studio 2024-09-23 13 08 08" src="https://github.com/user-attachments/assets/c1f8288e-374c-457c-942a-4109240102ab">
8. Choose the Camera option, and add the qr code image to the wall of the virtual scene
<img width="679" alt="image" src="https://github.com/user-attachments/assets/bafaa9af-72e4-4444-9704-9876c53c883c">
9. Back in the app, when you choose QR Code and "Scan a QR code`, the virtual scene should open. Hold shift, then move your mouse to look around. Turn around 180 degrees, and walk forward into the other room (can go through the walls) using the `w` key. When you get the QR code into the white border, the scanner should close and apply the certificate settings. If you use a nonsense QR code, or a QR code with a non-matching key, or a CA QR code, you should get an error message when it scans.
The process for scanning a CA qr code is similar.
1. Run `nebula-cert print -out-qr "qr-ca.png" -path ./ca.crt`
2. Replace the QR in the extended controls
3. Tap CA when adding a site
4. The rest of the process is the same as above.
iOS is similar, except you'll need to use a real device, as the simulator does not include a virtual scene like Android does.
2024-09-24 11:25:09 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:mobile_scanner/mobile_scanner.dart';
|
|
|
|
|
|
|
|
class ScanQRScreen extends StatefulWidget {
|
|
|
|
@override
|
|
|
|
State<ScanQRScreen> createState() => _ScanQRScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _ScanQRScreenState extends State<ScanQRScreen> {
|
|
|
|
final MobileScannerController cameraController = MobileScannerController(
|
|
|
|
autoStart: true,
|
|
|
|
formats: [BarcodeFormat.qrCode],
|
|
|
|
);
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final scanWindow = Rect.fromCenter(
|
|
|
|
center: MediaQuery.sizeOf(context).center(Offset.zero),
|
|
|
|
width: 250,
|
|
|
|
height: 250,
|
|
|
|
);
|
|
|
|
|
|
|
|
return Scaffold(
|
|
|
|
appBar: AppBar(title: const Text('Scan QR')),
|
|
|
|
backgroundColor: Colors.black,
|
|
|
|
body: Stack(fit: StackFit.expand, children: [
|
|
|
|
Center(
|
|
|
|
child: MobileScanner(
|
|
|
|
fit: BoxFit.contain,
|
|
|
|
controller: cameraController,
|
|
|
|
scanWindow: scanWindow,
|
|
|
|
onDetect: (BarcodeCapture barcodes) {
|
|
|
|
var barcode = barcodes.barcodes.firstOrNull;
|
|
|
|
if (barcode != null && mounted) {
|
|
|
|
cameraController.stop().then((_) {
|
|
|
|
Navigator.pop(context, barcode.rawValue);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
ValueListenableBuilder(
|
|
|
|
valueListenable: cameraController,
|
|
|
|
builder: (context, value, child) {
|
|
|
|
if (!value.isInitialized || !value.isRunning || value.error != null) {
|
|
|
|
return const SizedBox();
|
|
|
|
}
|
|
|
|
|
|
|
|
return CustomPaint(
|
|
|
|
painter: ScannerOverlay(scanWindow: scanWindow),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.all(16.0),
|
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
|
|
children: [
|
|
|
|
ToggleFlashlightButton(controller: cameraController),
|
|
|
|
SwitchCameraButton(controller: cameraController),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ScannerOverlay extends CustomPainter {
|
|
|
|
const ScannerOverlay({
|
|
|
|
required this.scanWindow,
|
|
|
|
this.borderRadius = 12.0,
|
|
|
|
});
|
|
|
|
|
|
|
|
final Rect scanWindow;
|
|
|
|
final double borderRadius;
|
|
|
|
|
|
|
|
@override
|
|
|
|
void paint(Canvas canvas, Size size) {
|
|
|
|
// we need to pass the size to the custom paint widget
|
2024-10-31 14:19:32 +00:00
|
|
|
final backgroundPath = Path()..addRect(Rect.fromLTWH(0, 0, size.width, size.height));
|
Update Gradle, replace QR code reader library (#162)
I ran the Gradle Upgrade Assistant to get us on the latest version of gradle, but two of our dependencies didn't support it.
- https://pub.dev/documentation/package_info/latest/
- https://github.com/AmolGangadhare/flutter_barcode_scanner
`pacakge_info` is officially deprecated and replaced by `package_info_plus`, which is what I've swapped to here.
A bigger change was switching to https://github.com/juliansteenbakker/mobile_scanner. It does seem to work a bit better than the other one, and does not throw an error now when cancelling the QR code collection, as it did before. I've tested on android in the simulator, and iOS with an actual device.
To test adding a cert:
1. Create a CA on your computer with `nebula-cert ca -name test-mobile`. This will create a `ca.crt` and `ca.key`
2. Tap the + button in the mobile app to add a site
3. Tap the "Certificate" row
4. Copy the public key to a file on your computer like `test.pub`
5. Create a signed cert with `nebula-cert sign -name test-mobile -ip 192.168.0.20/24 -in-pub test.pub`
6. Create a QR code for it: `nebula-cert print -out-qr "qr.png" -path ./test-mobile.crt`
7. In Android studio, in the "Running devices" tab, open the simulator's extended controls:
<img width="509" alt="Android Studio 2024-09-23 13 08 08" src="https://github.com/user-attachments/assets/c1f8288e-374c-457c-942a-4109240102ab">
8. Choose the Camera option, and add the qr code image to the wall of the virtual scene
<img width="679" alt="image" src="https://github.com/user-attachments/assets/bafaa9af-72e4-4444-9704-9876c53c883c">
9. Back in the app, when you choose QR Code and "Scan a QR code`, the virtual scene should open. Hold shift, then move your mouse to look around. Turn around 180 degrees, and walk forward into the other room (can go through the walls) using the `w` key. When you get the QR code into the white border, the scanner should close and apply the certificate settings. If you use a nonsense QR code, or a QR code with a non-matching key, or a CA QR code, you should get an error message when it scans.
The process for scanning a CA qr code is similar.
1. Run `nebula-cert print -out-qr "qr-ca.png" -path ./ca.crt`
2. Replace the QR in the extended controls
3. Tap CA when adding a site
4. The rest of the process is the same as above.
iOS is similar, except you'll need to use a real device, as the simulator does not include a virtual scene like Android does.
2024-09-24 11:25:09 +00:00
|
|
|
|
|
|
|
final cutoutPath = Path()
|
|
|
|
..addRRect(
|
|
|
|
RRect.fromRectAndCorners(
|
|
|
|
scanWindow,
|
|
|
|
topLeft: Radius.circular(borderRadius),
|
|
|
|
topRight: Radius.circular(borderRadius),
|
|
|
|
bottomLeft: Radius.circular(borderRadius),
|
|
|
|
bottomRight: Radius.circular(borderRadius),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
|
|
|
final backgroundPaint = Paint()
|
|
|
|
..color = Colors.black.withOpacity(0.5)
|
|
|
|
..style = PaintingStyle.fill
|
2024-10-31 14:19:32 +00:00
|
|
|
..blendMode = BlendMode.srcOver;
|
Update Gradle, replace QR code reader library (#162)
I ran the Gradle Upgrade Assistant to get us on the latest version of gradle, but two of our dependencies didn't support it.
- https://pub.dev/documentation/package_info/latest/
- https://github.com/AmolGangadhare/flutter_barcode_scanner
`pacakge_info` is officially deprecated and replaced by `package_info_plus`, which is what I've swapped to here.
A bigger change was switching to https://github.com/juliansteenbakker/mobile_scanner. It does seem to work a bit better than the other one, and does not throw an error now when cancelling the QR code collection, as it did before. I've tested on android in the simulator, and iOS with an actual device.
To test adding a cert:
1. Create a CA on your computer with `nebula-cert ca -name test-mobile`. This will create a `ca.crt` and `ca.key`
2. Tap the + button in the mobile app to add a site
3. Tap the "Certificate" row
4. Copy the public key to a file on your computer like `test.pub`
5. Create a signed cert with `nebula-cert sign -name test-mobile -ip 192.168.0.20/24 -in-pub test.pub`
6. Create a QR code for it: `nebula-cert print -out-qr "qr.png" -path ./test-mobile.crt`
7. In Android studio, in the "Running devices" tab, open the simulator's extended controls:
<img width="509" alt="Android Studio 2024-09-23 13 08 08" src="https://github.com/user-attachments/assets/c1f8288e-374c-457c-942a-4109240102ab">
8. Choose the Camera option, and add the qr code image to the wall of the virtual scene
<img width="679" alt="image" src="https://github.com/user-attachments/assets/bafaa9af-72e4-4444-9704-9876c53c883c">
9. Back in the app, when you choose QR Code and "Scan a QR code`, the virtual scene should open. Hold shift, then move your mouse to look around. Turn around 180 degrees, and walk forward into the other room (can go through the walls) using the `w` key. When you get the QR code into the white border, the scanner should close and apply the certificate settings. If you use a nonsense QR code, or a QR code with a non-matching key, or a CA QR code, you should get an error message when it scans.
The process for scanning a CA qr code is similar.
1. Run `nebula-cert print -out-qr "qr-ca.png" -path ./ca.crt`
2. Replace the QR in the extended controls
3. Tap CA when adding a site
4. The rest of the process is the same as above.
iOS is similar, except you'll need to use a real device, as the simulator does not include a virtual scene like Android does.
2024-09-24 11:25:09 +00:00
|
|
|
|
|
|
|
final backgroundWithCutout = Path.combine(
|
|
|
|
PathOperation.difference,
|
|
|
|
backgroundPath,
|
|
|
|
cutoutPath,
|
|
|
|
);
|
|
|
|
|
|
|
|
final borderPaint = Paint()
|
|
|
|
..color = Colors.white
|
|
|
|
..style = PaintingStyle.stroke
|
|
|
|
..strokeWidth = 4.0;
|
|
|
|
|
|
|
|
final borderRect = RRect.fromRectAndCorners(
|
|
|
|
scanWindow,
|
|
|
|
topLeft: Radius.circular(borderRadius),
|
|
|
|
topRight: Radius.circular(borderRadius),
|
|
|
|
bottomLeft: Radius.circular(borderRadius),
|
|
|
|
bottomRight: Radius.circular(borderRadius),
|
|
|
|
);
|
|
|
|
|
|
|
|
// First, draw the background,
|
|
|
|
// with a cutout area that is a bit larger than the scan window.
|
|
|
|
// Finally, draw the scan window itself.
|
|
|
|
canvas.drawPath(backgroundWithCutout, backgroundPaint);
|
|
|
|
canvas.drawRRect(borderRect, borderPaint);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
bool shouldRepaint(ScannerOverlay oldDelegate) {
|
|
|
|
return scanWindow != oldDelegate.scanWindow || borderRadius != oldDelegate.borderRadius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class SwitchCameraButton extends StatelessWidget {
|
|
|
|
const SwitchCameraButton({required this.controller, super.key});
|
|
|
|
|
|
|
|
final MobileScannerController controller;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return ValueListenableBuilder(
|
|
|
|
valueListenable: controller,
|
|
|
|
builder: (context, state, child) {
|
|
|
|
if (!state.isInitialized || !state.isRunning) {
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
}
|
|
|
|
|
|
|
|
final int? availableCameras = state.availableCameras;
|
|
|
|
|
|
|
|
if (availableCameras != null && availableCameras < 2) {
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
}
|
|
|
|
|
|
|
|
final Widget icon;
|
|
|
|
|
|
|
|
switch (state.cameraDirection) {
|
|
|
|
case CameraFacing.front:
|
|
|
|
icon = const Icon(Icons.camera_front);
|
|
|
|
case CameraFacing.back:
|
|
|
|
icon = const Icon(Icons.camera_rear);
|
|
|
|
}
|
|
|
|
|
|
|
|
return IconButton(
|
|
|
|
iconSize: 32.0,
|
|
|
|
color: Colors.white,
|
|
|
|
icon: icon,
|
|
|
|
onPressed: () async {
|
|
|
|
await controller.switchCamera();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ToggleFlashlightButton extends StatelessWidget {
|
|
|
|
const ToggleFlashlightButton({required this.controller, super.key});
|
|
|
|
|
|
|
|
final MobileScannerController controller;
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return ValueListenableBuilder(
|
|
|
|
valueListenable: controller,
|
|
|
|
builder: (context, controllerState, child) {
|
|
|
|
if (!controllerState.isInitialized || !controllerState.isRunning) {
|
|
|
|
return const SizedBox.shrink();
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (controllerState.torchState) {
|
|
|
|
case TorchState.auto:
|
|
|
|
return IconButton(
|
|
|
|
color: Colors.white,
|
|
|
|
iconSize: 32.0,
|
|
|
|
icon: const Icon(Icons.flash_auto),
|
|
|
|
onPressed: () async {
|
|
|
|
await controller.toggleTorch();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
case TorchState.off:
|
|
|
|
return IconButton(
|
|
|
|
color: Colors.white,
|
|
|
|
iconSize: 32.0,
|
|
|
|
icon: const Icon(Icons.flash_off),
|
|
|
|
onPressed: () async {
|
|
|
|
await controller.toggleTorch();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
case TorchState.on:
|
|
|
|
return IconButton(
|
|
|
|
color: Colors.white,
|
|
|
|
iconSize: 32.0,
|
|
|
|
icon: const Icon(Icons.flash_on),
|
|
|
|
onPressed: () async {
|
|
|
|
await controller.toggleTorch();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
case TorchState.unavailable:
|
|
|
|
return const SizedBox.square(
|
|
|
|
dimension: 48.0,
|
|
|
|
child: Icon(
|
|
|
|
Icons.no_flash,
|
|
|
|
size: 32.0,
|
|
|
|
color: Colors.grey,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|