From 4db201624cb10355eb42d56e99d336f1af69f280 Mon Sep 17 00:00:00 2001 From: Simeon5566 Date: Wed, 4 Jan 2023 14:03:07 +0100 Subject: [PATCH 1/9] added device list to cloud service screen --- lib/objects/cloud_service_api.dart | 10 +++++ lib/screens/cloud_service_ui.dart | 71 ++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/lib/objects/cloud_service_api.dart b/lib/objects/cloud_service_api.dart index d702888..aaae794 100644 --- a/lib/objects/cloud_service_api.dart +++ b/lib/objects/cloud_service_api.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:html'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart'; @@ -46,6 +47,7 @@ class CloudServiceAPI { static late String password; static late Map headers; static bool initState = false; + static List devices = List.empty(growable: true); CloudServiceAPI(){ if(!initState) { @@ -65,6 +67,7 @@ class CloudServiceAPI { 'content-type': 'application/json', 'accept': 'application/json', }; + await updateDeviceList(); log("init Config $headers"); } void reloadConfig() { @@ -80,6 +83,10 @@ class CloudServiceAPI { Response r = await get(url, headers: headers); return json.decode(r.body) as List; } + Future updateDeviceList() async { + devices = await getDevices(); + debugPrint("devices: ${devices.elementAt(0)}"); + } Future> getDeviceInfo(deviceID) async { Uri url = Uri.https(address, '/api/devices/$deviceID'); Response r = await get(url, headers: headers); @@ -128,4 +135,7 @@ class CloudServiceAPI { password = input; reloadConfig(); } + List getLoadedDevices(){ + return devices; + } } \ No newline at end of file diff --git a/lib/screens/cloud_service_ui.dart b/lib/screens/cloud_service_ui.dart index 76825d5..b99619a 100644 --- a/lib/screens/cloud_service_ui.dart +++ b/lib/screens/cloud_service_ui.dart @@ -14,18 +14,25 @@ class CloudService extends StatefulWidget { State createState() => _CloudService(); } -Future> readJson() async { - final data = await rootBundle.loadString('config/credentials.json'); - var data_ = json.decode(data) as Map; - debugPrint(data_.toString()); - return data_; -} + class _CloudService extends State{ late final Map credentials; CloudServiceAPI cloudServiceAPI = CloudServiceAPI(); - static SharedPreferences? preferencesInstance; + int _selectedIndex = 0; + + void _onItemTapped(int index){ + setState(() { + _selectedIndex = index; + if(_selectedIndex == 0){ + cloudServiceAPI.updateDeviceList(); + } + if(_selectedIndex == 1){ + // implementation missing + } + }); + } @override Widget build(BuildContext context) { @@ -33,17 +40,45 @@ class _CloudService extends State{ appBar: AppBar( title: const Text("Cloud Service"), ), - body: Center( - child: TextButton( - onPressed: () async{ - var respond1 = await cloudServiceAPI.getDevices(); - debugPrint('Devices: ${respond1.toString()}'); - var respond2 = await cloudServiceAPI.getInformation(); - debugPrint('Information: ${respond2.toString()}'); - var respond3 = await cloudServiceAPI.createDevice('1', 'asdas', 'sdwe1'); - debugPrint('CreateDevice: ${respond3.toString()}'); - }, child: const Text("Example"), - ), + body: Column( + children: [ + Expanded(child: ListView.separated( + itemCount: cloudServiceAPI.getLoadedDevices().length, + itemBuilder: (BuildContext context, int index) { + return Container( + height: 100, + color: Colors.lightBlueAccent[100], + child: Center(child: Text("Id: ${cloudServiceAPI.getLoadedDevices()[index]['id']} \n" + "endpoint: ${cloudServiceAPI.getLoadedDevices()[index]['endpoint']} \n" + "status: ${cloudServiceAPI.getLoadedDevices()[index]['endpoint']} \n" + "connectionState: ${cloudServiceAPI.getLoadedDevices()[index]['connectionState']} \n" + "lastActivityTime: ${cloudServiceAPI.getLoadedDevices()[index]['lastActivityTime']}"),), + ); + }, separatorBuilder: (BuildContext context, int index) => const Divider( + thickness: 0, + indent: 0, + height: 5, + color: Colors.white, + ),) + ) + ], + ), + bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + items: const[ + BottomNavigationBarItem( + icon: Icon(Icons.refresh), + label: 'Reload', + ), + BottomNavigationBarItem( + icon: Icon(Icons.search), + label: 'Search', + ), + ], + currentIndex: _selectedIndex, + selectedItemColor: Colors.lightBlueAccent, + onTap: _onItemTapped, + ), drawer: const Sidebar(), ); From 259985f8cc3f414ad1a0a48328cccbecc48c6460 Mon Sep 17 00:00:00 2001 From: Simeon5566 Date: Thu, 5 Jan 2023 17:33:50 +0100 Subject: [PATCH 2/9] added device option panel and implement search function --- lib/objects/cloud_service_api.dart | 24 +++++++--- lib/screens/main_screen.dart | 2 + lib/screens/registered_devices_screen.dart | 53 ++++++++++++++++++++-- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/lib/objects/cloud_service_api.dart b/lib/objects/cloud_service_api.dart index f016c06..61282e2 100644 --- a/lib/objects/cloud_service_api.dart +++ b/lib/objects/cloud_service_api.dart @@ -23,7 +23,7 @@ class CloudServiceAPI { static String _password = ""; static late Map _headers; static bool _initState = false; - static List devices = List.empty(growable: true); + static List devices = List.empty(growable: true); CloudServiceAPI() { if (!_initState) { @@ -72,14 +72,16 @@ class CloudServiceAPI { DeviceInfoModel deviceInfoModel = DeviceInfoModel.fromJson(jsonObject); return deviceInfoModel; } + Future> getDevicesInfo() async { + updateDeviceList(); + List deviceInfoModelList = List.empty(growable: true); + for(DeviceModel tmp in devices){ + deviceInfoModelList.add(await getDeviceInfo(tmp.id)); + } + return deviceInfoModelList; + } Future updateDeviceList() async { devices = await getDevices(); - debugPrint("devices: ${devices.elementAt(0)}"); - } - Future> getDeviceInfo(deviceID) async { - Uri url = Uri.https(address, '/api/devices/$deviceID'); - Response r = await get(url, headers: headers); - return json.decode(r.body) as Map; } Future> getInformation() async { @@ -87,6 +89,14 @@ class CloudServiceAPI { Response response = await get(url, headers: _headers); return json.decode(response.body) as Map; } + Future deleteDevice(String clientId) async { + Uri url = Uri.https(_address, '/api/devices/$clientId'); + Response response = await delete(url, headers: _headers); + if(response.statusCode == 204){ + return true; + } + return false; + } Future createDevice(String id, String primaryThumbprint, String secondaryThumbprint) async { Uri url = Uri.https(_address, '/api/devices'); diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 9d47f57..1e69733 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import '../objects/cloud_service_api.dart'; import '../objects/create_material_color.dart'; import '../widgets/sidebar.dart'; @@ -33,6 +34,7 @@ class HomeScreen extends StatefulWidget { } class _HomeScreen extends State { + final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI(); @override void initState() { super.initState(); diff --git a/lib/screens/registered_devices_screen.dart b/lib/screens/registered_devices_screen.dart index cbcd408..c676ad8 100644 --- a/lib/screens/registered_devices_screen.dart +++ b/lib/screens/registered_devices_screen.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -63,6 +64,21 @@ class _RegisteredDevicesScreen extends State { super.initState(); } + Future searchDevice(String clientId) async { + List searchResult = List.empty(growable: true); + for(DeviceInfoModel tmp in _registeredInfoDevices!){ + if(clientId == ""){ + // _fetchRegisteredDevices(); + // break; + // missing implementation + } + if(tmp.id.contains(clientId)){ + searchResult.add(tmp); + } + } + _registeredInfoDevices = searchResult; + } + @override Widget build(BuildContext context) { return Scaffold( @@ -99,13 +115,13 @@ class _RegisteredDevicesScreen extends State { obscureText: false, decoration: InputDecoration( border: const OutlineInputBorder(), - hintText: 'Enter the server password', + hintText: 'Search for Devices', labelText: "Search", suffixIcon: IconButton( icon: const Icon(Icons.search), onPressed: () { setState(() { - // TODO Suchfunktion + searchDevice(_searchText); }); }, ), @@ -130,8 +146,39 @@ class _RegisteredDevicesScreen extends State { DeviceInfoModel entry = _registeredInfoDevices![index]; //DeviceInfoModel entryInfo = await _cloudServiceAPI.getDeviceInfo(entry.id); return CustomListTile( - title: "${entry.id} \nentrypoint: ${entry.endpoint} \nstatus: ${entry.status} \nconnection: ${entry.connectionState} \nlast activity: ${entry.lastActivityTime}", + title: entry.id, icon: Icons.devices, + onTap: () => showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: const Text('Device Info'), + content: Text("${entry.id} " + "\nentrypoint: ${entry.endpoint} " + "\nstatus: ${entry.status} " + "\nconnection: ${entry.connectionState} " + "\nlast activity: ${entry.lastActivityTime}" + "\nprimaryThumbprint: ${entry.primaryThumbprint}"), + backgroundColor: Colors.white70, + actions: [ + TextButton(onPressed: () => Navigator.pop(context, 'Cancel'), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + _cloudServiceAPI.deleteDevice(entry.id); + Navigator.pop(context, 'delete device'); + }, + child: const Text('Delete Device'), + ), + ], + ), + ) + /* + Navigator.push(context, + MaterialPageRoute( + builder: (context) => DeviceOptions(deviceInfoModel: entry))); + */ + ); }, ), From f588e61fe7a37eae23a6a4cc8e076c80e8a5b501 Mon Sep 17 00:00:00 2001 From: Simeon5566 Date: Thu, 5 Jan 2023 17:43:25 +0100 Subject: [PATCH 3/9] removed Cloud Service screen from menue --- lib/screens/cloud_service_ui.dart | 90 ------------------------------- lib/widgets/sidebar.dart | 8 --- test/widget_test.dart | 2 +- 3 files changed, 1 insertion(+), 99 deletions(-) delete mode 100644 lib/screens/cloud_service_ui.dart diff --git a/lib/screens/cloud_service_ui.dart b/lib/screens/cloud_service_ui.dart deleted file mode 100644 index 02f03aa..0000000 --- a/lib/screens/cloud_service_ui.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import '../objects/cloud_service_api.dart'; -import '../widgets/sidebar.dart'; - -Future> readJson() async { - final String data = await rootBundle.loadString('config/credentials.json'); - final Map dataMap = json.decode(data); - log("loaded json input $dataMap"); - return dataMap; -} - -class CloudService extends StatefulWidget { - const CloudService({Key? key}) : super(key: key); - - @override - State createState() => _CloudService(); -} - -class _CloudService extends State { - late final Map credentials; - CloudServiceAPI cloudServiceAPI = CloudServiceAPI(); - int _selectedIndex = 0; - - void _onItemTapped(int index){ - setState(() { - _selectedIndex = index; - if(_selectedIndex == 0){ - cloudServiceAPI.updateDeviceList(); - } - if(_selectedIndex == 1){ - // implementation missing - } - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text("Cloud Service"), - ), - body: Column( - children: [ - Expanded(child: ListView.separated( - itemCount: cloudServiceAPI.getLoadedDevices().length, - itemBuilder: (BuildContext context, int index) { - return Container( - height: 100, - color: Colors.lightBlueAccent[100], - child: Center(child: Text("Id: ${cloudServiceAPI.getLoadedDevices()[index]['id']} \n" - "endpoint: ${cloudServiceAPI.getLoadedDevices()[index]['endpoint']} \n" - "status: ${cloudServiceAPI.getLoadedDevices()[index]['endpoint']} \n" - "connectionState: ${cloudServiceAPI.getLoadedDevices()[index]['connectionState']} \n" - "lastActivityTime: ${cloudServiceAPI.getLoadedDevices()[index]['lastActivityTime']}"),), - ); - }, separatorBuilder: (BuildContext context, int index) => const Divider( - thickness: 0, - indent: 0, - height: 5, - color: Colors.white, - ),) - ) - ], - ), - bottomNavigationBar: BottomNavigationBar( - type: BottomNavigationBarType.fixed, - items: const[ - BottomNavigationBarItem( - icon: Icon(Icons.refresh), - label: 'Reload', - ), - BottomNavigationBarItem( - icon: Icon(Icons.search), - label: 'Search', - ), - ], - currentIndex: _selectedIndex, - selectedItemColor: Colors.lightBlueAccent, - onTap: _onItemTapped, - ), - drawer: const Sidebar(), - ); - } -} diff --git a/lib/widgets/sidebar.dart b/lib/widgets/sidebar.dart index 2bebf8e..87d477c 100644 --- a/lib/widgets/sidebar.dart +++ b/lib/widgets/sidebar.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../screens/bluetooth_screen.dart'; -import '../screens/cloud_service_ui.dart'; import '../screens/main_screen.dart'; import '../screens/registered_devices_screen.dart'; import '../screens/settings.dart'; @@ -50,13 +49,6 @@ class Sidebar extends StatelessWidget { Navigator.push(context, MaterialPageRoute(builder: (context) => const Settings())); }, ), - ListTile( - leading: const Icon(Icons.cloud), - title: const Text("Cloud Service"), - onTap: () { - Navigator.push(context, MaterialPageRoute(builder: (context) => const CloudService())); - }, - ), ], ), ); diff --git a/test/widget_test.dart b/test/widget_test.dart index 7e9ed70..1cd8719 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -14,7 +14,7 @@ import 'package:flutter_provisioning_for_iot/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MainPage()); + await tester.pumpWidget(const MainScreen()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); From ba6749455f338b3e200070db1eb5d73e1074e3aa Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sat, 7 Jan 2023 15:36:28 +0100 Subject: [PATCH 4/9] remove Theme Settings --- lib/screens/settings.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index d06e859..ccf9f23 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -105,10 +105,6 @@ class _SettingsState extends State { ), ], ), - const SingleSection( - title: "Theme Settings", - children: [], - ) ]), ) ]), From f635a4bae3605ec9a8f5efb1ad9c0bbc00cbb3cb Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sat, 7 Jan 2023 16:37:22 +0100 Subject: [PATCH 5/9] fixing issue #5 --- .github/workflows/build-app.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index 8caa2b9..e83de93 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -56,7 +56,7 @@ jobs: name: Android APK path: | build/app/outputs/flutter-apk/app-release.apk - build/app/outputs/flutter-apk/app-release.sha1 + build/app/outputs/flutter-apk/app-release.apk.sha1 - name: Upload APK as release uses: "marvinpinto/action-automatic-releases@latest" @@ -67,4 +67,4 @@ jobs: title: "Android APK" files: | build/app/outputs/flutter-apk/app-release.apk - build/app/outputs/flutter-apk/app-release.sha1 + build/app/outputs/flutter-apk/app-release.apk.sha1 From dc20c8c1716da874ef1803f5f3837f4c352f2d2f Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sat, 7 Jan 2023 16:38:23 +0100 Subject: [PATCH 6/9] improved ui and code warnings --- lib/objects/bluetooth_object.dart | 8 ++--- lib/screens/bluetooth_device_settings.dart | 42 ++++++++++------------ lib/screens/bluetooth_screen.dart | 24 +++++++++---- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/lib/objects/bluetooth_object.dart b/lib/objects/bluetooth_object.dart index 5e12759..ccd2693 100644 --- a/lib/objects/bluetooth_object.dart +++ b/lib/objects/bluetooth_object.dart @@ -16,15 +16,15 @@ class BluetoothObject { String id = ""; String _primaryThumbprint = ""; - String _secondaryThumbprint = ""; + final String _secondaryThumbprint = ""; late BluetoothConnection? _connection; late Stream _connectionStream; late StreamSubscription _connectionStreamSubscription; - CloudServiceAPI _cloudServiceAPI = CloudServiceAPI(); + final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI(); - late Uint8List _messageBufferBits; + //late Uint8List _messageBufferBits; late String _messageBufferChars = ""; bool _isDisconnecting = false; @@ -222,8 +222,6 @@ class BluetoothObject { Future sendData(String output) async { if (_connection == null) return; bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output); - // String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?"; - // String output = "myDevice123";// !§%&()=?#!?"; if (!nameAvailable) return; id = output; _connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n"))); diff --git a/lib/screens/bluetooth_device_settings.dart b/lib/screens/bluetooth_device_settings.dart index 1c0ba49..6306d64 100644 --- a/lib/screens/bluetooth_device_settings.dart +++ b/lib/screens/bluetooth_device_settings.dart @@ -14,8 +14,7 @@ class BluetoothDeviceSettings extends StatefulWidget { class BluetoothDeviceSettingsState extends State { late BluetoothObject _bluetoothObject; - - String _messageBuffer = ""; + String _textInput = ""; // 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ @override void initState() { @@ -23,9 +22,6 @@ class BluetoothDeviceSettingsState extends State { _bluetoothObject = widget.bluetoothObject; } - String _textInput = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - String _textOuput = ""; - ButtonStyle buttonStyle = ElevatedButton.styleFrom( foregroundColor: Colors.black, backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color) @@ -59,7 +55,7 @@ class BluetoothDeviceSettingsState extends State { ], ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0); Expanded( @@ -85,25 +81,23 @@ class BluetoothDeviceSettingsState extends State { )) ])), const Divider(), - ElevatedButton( - onPressed: () async { - _bluetoothObject.sendData(_textInput); - }, - style: buttonStyle, - child: const Text("Send Data"), - ), - ElevatedButton( - onPressed: () async { - setState(() { - _textOuput = ""; - }); - }, - style: buttonStyle, - child: const Text("Clear Output"), - ), - Text(_textOuput), Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), + child: + SizedBox( + width: double.infinity, + child: + ElevatedButton( + onPressed: () async { + _bluetoothObject.sendData(_textInput); + }, + style: buttonStyle, + child: const Text("Send Data"), + ), + ) + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), child: TextField( decoration: const InputDecoration( border: OutlineInputBorder(), diff --git a/lib/screens/bluetooth_screen.dart b/lib/screens/bluetooth_screen.dart index 449556f..469f5dc 100644 --- a/lib/screens/bluetooth_screen.dart +++ b/lib/screens/bluetooth_screen.dart @@ -50,7 +50,7 @@ class _BluetoothScreen extends State { _activeObject = null; _streamSubscription = null; if (widget.start) { - _startDiscovery(); + _initAsync(); } } @@ -63,8 +63,13 @@ class _BluetoothScreen extends State { debugPrint("called dispose"); } + Future _initAsync() async { + await _enablePermissions(); + await _enableBluetooth(); + await _startDiscovery(); + } + Future _enablePermissions() async { - debugPrint("Test"); PermissionStatus bluetoothScan = await Permission.bluetoothScan.request(); PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request(); bool granted = bluetoothScan.isGranted && bluetoothConnect.isGranted; @@ -74,7 +79,14 @@ class _BluetoothScreen extends State { } Future _enableBluetooth() async { - if (!_enabledPermissions) { + if (_enabledBluetooth) { + return; + } + BluetoothState state = await FlutterBluetoothSerial.instance.state; + if (state == BluetoothState.STATE_ON) { + setState(() { + _enabledBluetooth = true; + }); return; } bool? enabled = await FlutterBluetoothSerial.instance.requestEnable(); @@ -85,7 +97,8 @@ class _BluetoothScreen extends State { } Future _startDiscovery() async { - if (!_enabledPermissions) { + debugPrint("enabled: $_enabledPermissions"); + if (!_enabledPermissions && !_enabledBluetooth) { return; } setState(() => _isDiscovering = true); @@ -137,9 +150,6 @@ class _BluetoothScreen extends State { _activeObject!.isConnected ? await bluetoothObject.disconnectDevice() : await bluetoothObject.connectDevice(); } - //stty -F /dev/service 19200 parenb -parodd -cstopb cs8 - //cat /dev/service | xargs -n 1 /home/script/automaticcloud.sh - @override Widget build(BuildContext context) { return Scaffold( From d6c9520704d74c26b74d5dc7c7f697eb205d0fdf Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sat, 7 Jan 2023 16:45:30 +0100 Subject: [PATCH 7/9] purged more warnings --- lib/screens/main_screen.dart | 2 -- lib/screens/registered_devices_screen.dart | 1 - test/widget_test.dart | 2 -- 3 files changed, 5 deletions(-) diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 1e69733..9d47f57 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import '../objects/cloud_service_api.dart'; import '../objects/create_material_color.dart'; import '../widgets/sidebar.dart'; @@ -34,7 +33,6 @@ class HomeScreen extends StatefulWidget { } class _HomeScreen extends State { - final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI(); @override void initState() { super.initState(); diff --git a/lib/screens/registered_devices_screen.dart b/lib/screens/registered_devices_screen.dart index c676ad8..6672062 100644 --- a/lib/screens/registered_devices_screen.dart +++ b/lib/screens/registered_devices_screen.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/test/widget_test.dart b/test/widget_test.dart index 1cd8719..f2ddb50 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -9,8 +9,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_provisioning_for_iot/screens/main_screen.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter_provisioning_for_iot/main.dart'; - void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. From 079bc1bdadd4d4b2885e82412f1ef3ed27a53d15 Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sat, 7 Jan 2023 16:47:34 +0100 Subject: [PATCH 8/9] updated dependencies --- pubspec.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 098534f..d05bdf2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: "direct main" description: name: app_settings - sha256: "7a5b880e2dd41dba8877108180380a1d28d874c231f7c0f9022127a4061b88e1" + sha256: "66715a323ac36d6c8201035ba678777c0d2ea869e4d7064300d95af10c3bb8cb" url: "https://pub.dev" source: hosted - version: "4.1.8" + version: "4.2.0" async: dependency: transitive description: @@ -340,18 +340,18 @@ packages: dependency: transitive description: name: shared_preferences_linux - sha256: "28aefc1261746e7bad3d09799496054beb84e8c4ffcdfed7734e17b4ada459a5" + sha256: fbc3cd6826896b66a5f576b025e4f344f780c84ea7f8203097a353370607a2c8 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos - sha256: fbb94bf296576f49be37a1496d5951796211a8db0aa22cc0d68c46440dad808c + sha256: "81b6a60b2d27020eb0fc41f4cebc91353047309967901a79ee8203e40c42ed46" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "2.0.5" shared_preferences_platform_interface: dependency: transitive description: @@ -372,10 +372,10 @@ packages: dependency: transitive description: name: shared_preferences_windows - sha256: "97f7ab9a7da96d9cf19581f5de520ceb529548498bd6b5e0ccd02d68a0d15eba" + sha256: "07c274c2115d4d5e4280622abb09f0980e2c5b1fcdc98ae9f59a3bad5bfc1f26" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" sky_engine: dependency: transitive description: flutter @@ -449,10 +449,10 @@ packages: dependency: transitive description: name: win32 - sha256: ca121dbbadb3e43b449053feab0cdf3f2bff93b107cacf0290e3d29f717374b6 + sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" xdg_directories: dependency: transitive description: From f8a1c3854074e68dc6ad0fb61d742f3754fbdac9 Mon Sep 17 00:00:00 2001 From: YamiDoesDev Date: Sun, 8 Jan 2023 02:50:52 +0100 Subject: [PATCH 9/9] finishing touches for a working version --- android/build.gradle | 2 +- lib/objects/bluetooth_device_entry.dart | 4 +- lib/objects/bluetooth_object.dart | 150 ++++++--------------- lib/objects/cloud_service_api.dart | 2 +- lib/screens/bluetooth_device_settings.dart | 70 ++++++---- lib/screens/bluetooth_screen.dart | 66 ++++----- lib/screens/registered_devices_screen.dart | 16 +-- lib/screens/settings.dart | 2 +- lib/widgets/custom_list_tile.dart | 4 +- 9 files changed, 127 insertions(+), 189 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 83ae220..0582685 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.7.20' repositories { google() mavenCentral() diff --git a/lib/objects/bluetooth_device_entry.dart b/lib/objects/bluetooth_device_entry.dart index 3a3f11e..d1727bd 100644 --- a/lib/objects/bluetooth_device_entry.dart +++ b/lib/objects/bluetooth_device_entry.dart @@ -17,7 +17,9 @@ class BluetoothDeviceEntry extends ListTile { onTap: onTap, onLongPress: onLongPress, enabled: enabled, - leading: Icon(bluetoothObject.isConnected ? Icons.bluetooth_connected : Icons.bluetooth), + leading: bluetoothObject.isConnected + ? const Icon(Icons.bluetooth_connected, color: Colors.blue) + : const Icon(Icons.bluetooth), title: Text(bluetoothObject.name), subtitle: Text(bluetoothObject.address.toString()), trailing: Row( diff --git a/lib/objects/bluetooth_object.dart b/lib/objects/bluetooth_object.dart index ccd2693..9eef610 100644 --- a/lib/objects/bluetooth_object.dart +++ b/lib/objects/bluetooth_object.dart @@ -21,6 +21,7 @@ class BluetoothObject { late BluetoothConnection? _connection; late Stream _connectionStream; late StreamSubscription _connectionStreamSubscription; + late BuildContext _context; final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI(); @@ -69,7 +70,7 @@ class BluetoothObject { _connection = null; } - Future bondDevice() async { + Future bondDevice(BuildContext context) async { try { bool bonded = false; if (_device.isBonded) { @@ -81,22 +82,7 @@ class BluetoothObject { bonded = (await FlutterBluetoothSerial.instance.bondDeviceAtAddress(_address))!; debugPrint('Bonding with $_address has ${bonded ? 'succed' : 'failed'}.'); } - /*setState(() { - _discoveryResults[_discoveryResults.indexOf( - result)] = - BluetoothDiscoveryResult( - device: BluetoothDevice( - name: device.name ?? '', - address: address, - type: device.type, - bondState: bonded - ? BluetoothBondState.bonded - : BluetoothBondState.none, - ), - rssi: result.rssi); - });*/ } catch (ex) { - /* showDialog( context: context, builder: (BuildContext context) { @@ -114,7 +100,7 @@ class BluetoothObject { ], ); }, - );*/ + ); } } @@ -133,12 +119,15 @@ class BluetoothObject { _connection = null; } - _connection = await BluetoothConnection.toAddress(_address); - debugPrint("Connected to the device"); - - _connectionStream = _connection!.input!; - _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen); - _connectionStreamSubscription.onDone(_connectionOnDone); + try { + _connection = await BluetoothConnection.toAddress(_address); + debugPrint("Connected to the device"); + _connectionStream = _connection!.input!; + _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen); + _connectionStreamSubscription.onDone(_connectionOnDone); + } catch (ex) { + debugPrint("$ex"); + } } void _connectionOnDone() { @@ -151,111 +140,58 @@ class BluetoothObject { Future _connectionOnListen(Uint8List data) async { final String dataDecoded = const AsciiDecoder().convert(data); - - debugPrint("received: $data"); debugPrint("received decoded: $dataDecoded"); - _messageBufferChars += dataDecoded; - if (data[data.length - 1] == 10) { - debugPrint("received buffer: $_messageBufferChars"); - int spaceIndex = _messageBufferChars.indexOf(" "); - String firstParameter = ""; - String secondParameter = ""; - try { - firstParameter = _messageBufferChars.substring(0, spaceIndex); - secondParameter = _messageBufferChars.substring(spaceIndex + 1, _messageBufferChars.length - 2); - } catch (ex) { - debugPrint(ex.toString()); - } - debugPrint("we still go on!"); + if (!(data[data.length - 1] == 10)) { + return; + } + if (!_messageBufferChars.contains(" ")) { _messageBufferChars = ""; - debugPrint("first: $firstParameter"); - debugPrint("second: $secondParameter"); - if (firstParameter == "fingerprint") { - debugPrint("received final buffer: $secondParameter"); - _primaryThumbprint = secondParameter; - debugPrint("{ _secondaryThumbprint: $_secondaryThumbprint }"); - debugPrint("{ id: $id, _primaryThumbprint: $_primaryThumbprint, _secondaryThumbprint: $_secondaryThumbprint }"); - await _registerDevice(); - } + return; } - /* - // Allocate buffer for parsed data - int backspacesCounter = 0; - for (var byte in data) { - if (byte == 8 || byte == 127) { - backspacesCounter++; - } + List input = _messageBufferChars.split(" "); + if (input[0] == "fingerprint") { + _primaryThumbprint = input[1].trim(); + debugPrint("_primaryThumbprint: ${const AsciiEncoder().convert(_primaryThumbprint)}"); + debugPrint("id: ${const AsciiEncoder().convert(id)}"); + debugPrint("_secondaryThumbprint: $_secondaryThumbprint"); + await _registerDevice(); } - Uint8List buffer = Uint8List(data.length - backspacesCounter); - int bufferIndex = buffer.length; - - // Apply backspace control character - backspacesCounter = 0; - for (int i = data.length - 1; i >= 0; i--) { - if (data[i] == 8 || data[i] == 127) { - backspacesCounter++; - } else { - if (backspacesCounter > 0) { - backspacesCounter--; - } else { - buffer[--bufferIndex] = data[i]; - } - } - } - - // Create message if there is new line character - String dataString = String.fromCharCodes(buffer); - int index = buffer.indexOf(13); - if (~index != 0) { - _messageBuffer = dataString.substring(index); - } else { - _messageBuffer = (backspacesCounter > 0 - ? _messageBuffer.substring(0, _messageBuffer.length - backspacesCounter) - : _messageBuffer + dataString); - } - */ + _messageBufferChars = ""; } - Future sendData(String output) async { + Future sendData(BuildContext context, String output) async { if (_connection == null) return; bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output); if (!nameAvailable) return; id = output; _connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n"))); await _connection!.output.allSent; + _context = context; debugPrint("sent: $output"); } Future _registerDevice() async { bool registered = false; registered = await _cloudServiceAPI.createDevice(id, _primaryThumbprint, ""); + _confirmRegistration(registered); + } - String statusText = - registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden"; - Fluttertoast.showToast( - msg: statusText, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIosWeb: 2, - backgroundColor: Colors.grey[200], - textColor: Colors.black, - fontSize: 16.0); + void _confirmRegistration(bool registered) { + showDialog( + context: _context, + builder: (BuildContext context) => AlertDialog( + title: const Text('Device Info'), + content: Text(registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden"), + backgroundColor: Colors.white, + actions: [ + TextButton(onPressed: () => Navigator.pop(context, 'Cancel'), + child: const Text('OK'), + ), + ], + ), + ); } } - -/*setState(() { - _discoveryResults[_discoveryResults.indexOf( - result)] = - BluetoothDiscoveryResult( - device: BluetoothDevice( - name: device.name ?? '', - address: address, - type: device.type, - isConnected: connected, - ), - rssi: result.rssi); - }); - */ diff --git a/lib/objects/cloud_service_api.dart b/lib/objects/cloud_service_api.dart index 61282e2..579d200 100644 --- a/lib/objects/cloud_service_api.dart +++ b/lib/objects/cloud_service_api.dart @@ -133,7 +133,7 @@ class CloudServiceAPI { String statusText = status ? "die eingegebene ID ist verfügbar" : "die eingegebene ID ist nicht verfügbar"; Fluttertoast.showToast( msg: statusText, - toastLength: Toast.LENGTH_SHORT, + toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.grey[200], diff --git a/lib/screens/bluetooth_device_settings.dart b/lib/screens/bluetooth_device_settings.dart index 6306d64..2896357 100644 --- a/lib/screens/bluetooth_device_settings.dart +++ b/lib/screens/bluetooth_device_settings.dart @@ -14,6 +14,7 @@ class BluetoothDeviceSettings extends StatefulWidget { class BluetoothDeviceSettingsState extends State { late BluetoothObject _bluetoothObject; + bool _isRegistering = false; String _textInput = ""; // 0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ @override @@ -49,8 +50,8 @@ class BluetoothDeviceSettingsState extends State { ), ListTile( title: const Text("Device Connection State"), - subtitle: - Text(_bluetoothObject.isConnected ? "ConnectionState.DISCONECTED" : "ConnectionState.CONNECED"), + subtitle: Text( + _bluetoothObject.isConnected ? "ConnectionState.DISCONNECTED" : "ConnectionState.CONNECTED"), ), ], ), @@ -81,33 +82,48 @@ class BluetoothDeviceSettingsState extends State { )) ])), const Divider(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), - child: - SizedBox( - width: double.infinity, - child: - ElevatedButton( - onPressed: () async { - _bluetoothObject.sendData(_textInput); - }, - style: buttonStyle, - child: const Text("Send Data"), - ), - ) - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: TextField( - decoration: const InputDecoration( - border: OutlineInputBorder(), - hintText: 'Enter the device name', + SingleSection(title: "Cloud Registration", children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 0.0), + child: TextFormField( + obscureText: false, + decoration: InputDecoration( + border: const OutlineInputBorder(), + hintText: 'Enter the device name', + labelText: "Device Name", + filled: false, + suffixIcon: _isRegistering + ? FittedBox( + child: Container( + height: 10, + width: 10, + margin: const EdgeInsets.all(8.0), + child: const CircularProgressIndicator( + strokeWidth: 2.0, + ), + ), + ) // : const I + : IconButton( + icon: const Icon(Icons.login), + onPressed: () async { + FocusScopeNode currentFocus = FocusScope.of(context); + currentFocus.unfocus(); + setState(() { + _isRegistering = true; + }); + await _bluetoothObject.sendData(context, _textInput); + setState(() { + _isRegistering = false; + }); + }, + )), + keyboardType: TextInputType.text, + onChanged: (String newValue) { + _textInput = newValue; + }, ), - onChanged: (String newValue) { - _textInput = newValue; - }, ), - ), + ]), ]), ]), )); diff --git a/lib/screens/bluetooth_screen.dart b/lib/screens/bluetooth_screen.dart index 469f5dc..7b00614 100644 --- a/lib/screens/bluetooth_screen.dart +++ b/lib/screens/bluetooth_screen.dart @@ -54,21 +54,31 @@ class _BluetoothScreen extends State { } } - @override - void dispose() { - // Avoid memory leak (`setState` after dispose) and cancel discovery - _streamSubscription?.cancel(); - _activeObject?.disconnectDevice(); - super.dispose(); - debugPrint("called dispose"); - } - Future _initAsync() async { await _enablePermissions(); await _enableBluetooth(); await _startDiscovery(); } + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and cancel discovery + debugPrint("called dispose"); + _disposeAsync(); + super.dispose(); + } + + Future _disposeAsync() async { + await _streamSubscription?.cancel(); + await _activeObject?.disconnectDevice(); + List bondedDevices = await FlutterBluetoothSerial.instance.getBondedDevices(); + debugPrint(bondedDevices.toString()); + super.dispose(); + debugPrint("called dispose"); + } + + + Future _enablePermissions() async { PermissionStatus bluetoothScan = await Permission.bluetoothScan.request(); PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request(); @@ -183,6 +193,7 @@ class _BluetoothScreen extends State { CustomListTile( title: _enabledBluetooth ? "Bluetooth Enabled" : "Please Enable Bluetooth (click me)", icon: _enabledBluetooth ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded, + iconColor: _enabledPermissions ? Colors.green : Colors.red, onTap: () async { await _enableBluetooth(); }, @@ -190,6 +201,7 @@ class _BluetoothScreen extends State { CustomListTile( title: _enabledPermissions ? "Permissions Granted" : "Please Grant Permissions (click me)", icon: _enabledPermissions ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded, + iconColor: _enabledPermissions ? Colors.green : Colors.red, onTap: () async { await _enablePermissions(); }, @@ -203,38 +215,6 @@ class _BluetoothScreen extends State { ), ], ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - // const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0); - Expanded( - child: ElevatedButton( - onPressed: () async { - /*await Permission.bluetoothConnect.request(); - await Permission.bluetoothScan.request(); - if (await Permission.bluetoothScan.request().isGranted) { - // Either the permission was already granted before or the user just granted it. - debugPrint("Location Permission is granted"); - } else { - debugPrint("Location Permission is denied."); - }*/ - }, - style: buttonStyle, - child: const Text("Get BT Permissions"), - ), - ), - const SizedBox(width: 16.0), - Expanded( - child: ElevatedButton( - onPressed: () async { - _restartDiscovery(); - }, - style: buttonStyle, - child: const Text("Restart Scan"), - ), - ) - ]), - ), const Divider(), SingleSection( title: "Bluetooth Devices", @@ -262,9 +242,11 @@ class _BluetoothScreen extends State { bluetoothObject: bluetoothObject, onTap: () async { await _toggleConnection(bluetoothObject); + await _restartDiscovery(); }, onLongPress: () async { - await bluetoothObject.bondDevice(); + await bluetoothObject.bondDevice(context); + await _restartDiscovery(); }, ); }, diff --git a/lib/screens/registered_devices_screen.dart b/lib/screens/registered_devices_screen.dart index 6672062..7037b17 100644 --- a/lib/screens/registered_devices_screen.dart +++ b/lib/screens/registered_devices_screen.dart @@ -124,7 +124,7 @@ class _RegisteredDevicesScreen extends State { }); }, ), - filled: true, + filled: false, ), keyboardType: TextInputType.text, onChanged: (String newValue) { @@ -151,13 +151,13 @@ class _RegisteredDevicesScreen extends State { context: context, builder: (BuildContext context) => AlertDialog( title: const Text('Device Info'), - content: Text("${entry.id} " - "\nentrypoint: ${entry.endpoint} " - "\nstatus: ${entry.status} " - "\nconnection: ${entry.connectionState} " - "\nlast activity: ${entry.lastActivityTime}" - "\nprimaryThumbprint: ${entry.primaryThumbprint}"), - backgroundColor: Colors.white70, + content: Text("ID: ${entry.id} " + "\n\nentrypoint: ${entry.endpoint} " + "\n\nstatus: ${entry.status} " + "\n\nconnection: ${entry.connectionState} " + "\n\nlast activity: ${entry.lastActivityTime}" + "\n\nprimaryThumbprint: ${entry.primaryThumbprint}"), + backgroundColor: Colors.white, actions: [ TextButton(onPressed: () => Navigator.pop(context, 'Cancel'), child: const Text('Cancel'), diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index ccf9f23..86ed486 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -85,6 +85,7 @@ class _SettingsState extends State { border: const OutlineInputBorder(), hintText: 'Enter the server password', labelText: "Password", + filled: false, suffixIcon: IconButton( icon: Icon(_passwordVisible ? Icons.visibility : Icons.visibility_off), onPressed: () { @@ -93,7 +94,6 @@ class _SettingsState extends State { }); }, ), - filled: true, ), initialValue: Settings.password, keyboardType: TextInputType.text, diff --git a/lib/widgets/custom_list_tile.dart b/lib/widgets/custom_list_tile.dart index ec38f58..a79c49c 100644 --- a/lib/widgets/custom_list_tile.dart +++ b/lib/widgets/custom_list_tile.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; class CustomListTile extends StatelessWidget { final String title; final IconData icon; + final Color? iconColor; final VoidCallback? onTap; //final Widget? trailing; @@ -11,6 +12,7 @@ class CustomListTile extends StatelessWidget { Key? key, required this.title, required this.icon, + this.iconColor, this.onTap, //this.trailing, }) : super(key: key); @@ -19,7 +21,7 @@ class CustomListTile extends StatelessWidget { Widget build(BuildContext context) { return ListTile( title: Text(title), - leading: Icon(icon), + leading: Icon(icon, color: iconColor), onTap: onTap, //trailing: trailing, );