// Copyright (C) 2025, uvok cheetah // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . import 'dart:io'; import 'package:uvok_epaper_badge/control/scanner_controller.dart'; import 'package:uvok_epaper_badge/widgets/device_details.dart'; import 'package:uvok_epaper_badge/widgets/device_scan_select.dart'; import 'package:uvok_epaper_badge/model/device/device.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:uvok_epaper_badge/model/connection/device_connection.dart'; import 'package:uvok_epaper_badge/model/device_connection_factory.dart'; var logger = Logger(); class ScanPage extends StatefulWidget { const ScanPage({super.key, required this.title, required this.deviceScanner}); // Original doc: Fields in a Widget subclass are always marked "final". final String title; final ScannerController deviceScanner; @override State createState() => _ScanPageState(); } class _ScanPageState extends State { Device? selectedDevice; void _doConnect() async { final Device? dev = selectedDevice; if (dev == null) return; final DeviceConnection connection = DeviceConnectionFactory.createConnection(dev); await Navigator.push( context, MaterialPageRoute( builder: (context) => DeviceDetailsScreen(device: dev, deviceConnection: connection), ), ); } void _doScan() async { setState(() { selectedDevice = null; }); await getPermissions(); await widget.deviceScanner.startScan(); } Future getPermissions() async { // avoid spamming log with "unsupported" messages. if (Platform.isLinux) return; try { var status = await Permission.bluetooth.request(); logger.i("New BLE permission status: $status"); status = await Permission.bluetoothScan.request(); logger.i("New BLE scan permission status: $status"); status = await Permission.bluetoothConnect.request(); logger.i("New BLE connect permission status: $status"); } catch (e) { logger.e(e.toString()); } } @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: Center( child: Column( // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" // action in the IDE, or press "p" in the console), to see the // wireframe for each widget. mainAxisAlignment: MainAxisAlignment.center, spacing: 24, children: [ SizedBox(height: 15), StreamBuilder( stream: widget.deviceScanner.availabilityStream, builder: (saCtx, scanAvailability) { return StreamBuilder( stream: widget.deviceScanner.statusStream, initialData: ScanStatus.idle, builder: (ssCtx, scanStatus) { final bool isScanning = scanStatus.data == ScanStatus.scanning; final bool scanAvailable = scanAvailability.data == ScanAvailability.available; final bool allowConnect = selectedDevice == null || isScanning || !scanAvailable; Text scanBtn; if (!scanAvailable) { scanBtn = Text("No available adapter."); } else if (isScanning) { scanBtn = Text("Scanning..."); } else { scanBtn = Text("Start scan"); } return Row( mainAxisAlignment: MainAxisAlignment.center, spacing: 15.0, children: [ ElevatedButton( onPressed: isScanning || !scanAvailable ? null : _doScan, child: scanBtn, ), ElevatedButton( onPressed: allowConnect ? null : _doConnect, child: Text("Connect"), ), ], ); }, ); }, ), StreamBuilder( stream: widget.deviceScanner.scanResultsStream, initialData: [], builder: (srCtx, scanResults) { return DeviceScanSelection( items: scanResults.data ?? [], onItemSelected: (item) { setState(() => selectedDevice = item); }, ); }, ), ], ), ), ); } }