diff --git a/chameleonultragui/lib/bridge/chameleon.dart b/chameleonultragui/lib/bridge/chameleon.dart index 2e979ff5..b587cea4 100644 --- a/chameleonultragui/lib/bridge/chameleon.dart +++ b/chameleonultragui/lib/bridge/chameleon.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:math'; import 'dart:typed_data'; import 'dart:async'; import 'package:chameleonultragui/helpers/general.dart'; @@ -253,10 +252,10 @@ class ChameleonCommunicator { int dataStatus = 0; int dataLength = 0; List messageQueue = []; - Logger log = Logger(); - Random random = Random(); - ChameleonCommunicator({AbstractSerial? port}) { + final Logger log; + + ChameleonCommunicator(this.log, {AbstractSerial? port}) { if (port != null) { open(port); } diff --git a/chameleonultragui/lib/bridge/dfu.dart b/chameleonultragui/lib/bridge/dfu.dart index 5a5724fe..f93e7095 100644 --- a/chameleonultragui/lib/bridge/dfu.dart +++ b/chameleonultragui/lib/bridge/dfu.dart @@ -135,9 +135,9 @@ class DFUCommunicator { AbstractSerial? _serialInstance; Completer>? responseCompleter; - Logger log = Logger(); + final Logger log; - DFUCommunicator({AbstractSerial? port, bool viaBLE = false}) { + DFUCommunicator(this.log, {AbstractSerial? port, bool viaBLE = false}) { isBLE = viaBLE; if (port != null) { open(port); diff --git a/chameleonultragui/lib/connector/serial_abstract.dart b/chameleonultragui/lib/connector/serial_abstract.dart index dafa31bb..03460955 100644 --- a/chameleonultragui/lib/connector/serial_abstract.dart +++ b/chameleonultragui/lib/connector/serial_abstract.dart @@ -19,7 +19,7 @@ class Chameleon { } class AbstractSerial { - Logger log = Logger(); + late Logger log; ChameleonDevice device = ChameleonDevice.none; bool connected = false; bool isOpen = false; @@ -29,6 +29,8 @@ class AbstractSerial { ConnectionType connectionType = ConnectionType.none; dynamic messageCallback; + AbstractSerial({required this.log}); + Future performConnect() async { return false; } diff --git a/chameleonultragui/lib/connector/serial_android.dart b/chameleonultragui/lib/connector/serial_android.dart index a7c92b49..065e6d30 100644 --- a/chameleonultragui/lib/connector/serial_android.dart +++ b/chameleonultragui/lib/connector/serial_android.dart @@ -8,8 +8,10 @@ import 'package:permission_handler/permission_handler.dart'; // Class combines Android OTG and BLE serial class AndroidSerial extends AbstractSerial { - BLESerial bleSerial = BLESerial(); - MobileSerial mobileSerial = MobileSerial(); + late BLESerial bleSerial = BLESerial(log: log); + late MobileSerial mobileSerial = MobileSerial(log: log); + + AndroidSerial({required super.log}); @override Future performDisconnect() async { diff --git a/chameleonultragui/lib/connector/serial_ble.dart b/chameleonultragui/lib/connector/serial_ble.dart index 1d4d42e4..8177e4b4 100644 --- a/chameleonultragui/lib/connector/serial_ble.dart +++ b/chameleonultragui/lib/connector/serial_ble.dart @@ -26,6 +26,8 @@ class BLESerial extends AbstractSerial { Map chameleonMap = {}; bool inSearch = false; + BLESerial({required super.log}); + @override Future availableDevices() async { if (inSearch) { diff --git a/chameleonultragui/lib/connector/serial_mobile.dart b/chameleonultragui/lib/connector/serial_mobile.dart index 5bde86b7..e29c5d2c 100644 --- a/chameleonultragui/lib/connector/serial_mobile.dart +++ b/chameleonultragui/lib/connector/serial_mobile.dart @@ -10,6 +10,8 @@ class MobileSerial extends AbstractSerial { Map deviceMap = {}; UsbPort? port; + MobileSerial({required super.log}); + @override Future performDisconnect() async { device = ChameleonDevice.none; diff --git a/chameleonultragui/lib/connector/serial_native.dart b/chameleonultragui/lib/connector/serial_native.dart index 5f7cf439..d75167e2 100644 --- a/chameleonultragui/lib/connector/serial_native.dart +++ b/chameleonultragui/lib/connector/serial_native.dart @@ -10,6 +10,8 @@ class NativeSerial extends AbstractSerial { bool checkDFU = true; SerialPortReader? reader; + NativeSerial({required super.log}); + @override Future availableDevices() async { return SerialPort.availablePorts; diff --git a/chameleonultragui/lib/gui/component/developer_list.dart b/chameleonultragui/lib/gui/component/developer_list.dart index b0cd6870..a94f36c2 100644 --- a/chameleonultragui/lib/gui/component/developer_list.dart +++ b/chameleonultragui/lib/gui/component/developer_list.dart @@ -11,7 +11,7 @@ class DeveloperList extends StatelessWidget { return IntrinsicHeight( child: Center( child: Wrap( - alignment: WrapAlignment.center, // Center the items in each line + alignment: WrapAlignment.center, children: List.generate(avatars.length, (index) { final avatar = avatars[index]; return GestureDetector( diff --git a/chameleonultragui/lib/gui/component/slot_changer.dart b/chameleonultragui/lib/gui/component/slot_changer.dart index 2cbdbab2..ad852645 100644 --- a/chameleonultragui/lib/gui/component/slot_changer.dart +++ b/chameleonultragui/lib/gui/component/slot_changer.dart @@ -98,7 +98,7 @@ class SlotChangerState extends State { ], ); } else if (snapshot.hasError) { - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); return Text('${localizations.error}: ${snapshot.error.toString()}'); } else { final slotIcons = snapshot.data; diff --git a/chameleonultragui/lib/gui/menu/chameleon_settings.dart b/chameleonultragui/lib/gui/menu/chameleon_settings.dart index c47cd64b..713fae14 100644 --- a/chameleonultragui/lib/gui/menu/chameleon_settings.dart +++ b/chameleonultragui/lib/gui/menu/chameleon_settings.dart @@ -82,7 +82,7 @@ class ChameleonSettingsState extends State { title: Text(localizations.device_settings), content: const Column(children: [CircularProgressIndicator()])); } else if (snapshot.hasError) { - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); return AlertDialog( title: Text(localizations.device_settings), content: Text( @@ -106,7 +106,7 @@ class ChameleonSettingsState extends State { TextButton( onPressed: () async { await appState.communicator!.enterDFUMode(); - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); Navigator.pop(context, localizations.cancel); appState.changesMade(); }, @@ -121,7 +121,7 @@ class ChameleonSettingsState extends State { Navigator.pop(context, localizations.cancel); var snackBar = SnackBar( content: Text(localizations.downloading_fw( - appState.connector.device == + appState.connector!.device == ChameleonDevice.ultra ? "Ultra" : "Lite")), @@ -334,7 +334,7 @@ class ChameleonSettingsState extends State { TextButton( onPressed: () async { await appState.communicator!.factoryReset(); - await appState.connector + await appState.connector! .performDisconnect(); Navigator.pop( context, localizations.cancel); diff --git a/chameleonultragui/lib/gui/menu/slot_settings.dart b/chameleonultragui/lib/gui/menu/slot_settings.dart index 195e5ab9..8a845969 100644 --- a/chameleonultragui/lib/gui/menu/slot_settings.dart +++ b/chameleonultragui/lib/gui/menu/slot_settings.dart @@ -98,7 +98,7 @@ class SlotSettingsState extends State { content: const SingleChildScrollView( child: Column(children: [CircularProgressIndicator()]))); } else if (snapshot.hasError) { - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); return AlertDialog( title: Text(localizations.slot_settings), content: Text( @@ -183,7 +183,7 @@ class SlotSettingsState extends State { }), const SizedBox(height: 16), Text( - localizations.mifare_clasic_e_s, + localizations.mifare_classic_emulator_settings, textScaleFactor: 1.1, ), const SizedBox(height: 8), diff --git a/chameleonultragui/lib/gui/page/connect.dart b/chameleonultragui/lib/gui/page/connect.dart index 9217a7f5..f9b1899f 100644 --- a/chameleonultragui/lib/gui/page/connect.dart +++ b/chameleonultragui/lib/gui/page/connect.dart @@ -14,13 +14,13 @@ class ConnectPage extends StatelessWidget { @override Widget build(BuildContext context) { - var appState = context.watch(); // Get State + var appState = context.watch(); var localizations = AppLocalizations.of(context)!; return FutureBuilder( - future: - (appState.connector.connected || appState.connector.pendingConnection) - ? Future.value([]) - : appState.connector.availableChameleons(false), + future: (appState.connector!.connected || + appState.connector!.pendingConnection) + ? Future.value([]) + : appState.connector!.availableChameleons(false), builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Scaffold( @@ -29,7 +29,7 @@ class ConnectPage extends StatelessWidget { ), body: const Center(child: CircularProgressIndicator())); } else if (snapshot.hasError) { - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); return Text('${localizations.error}: ${snapshot.error}'); } else { final (result as List) = snapshot.data; @@ -40,7 +40,7 @@ class ConnectPage extends StatelessWidget { ), body: Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, // Center + mainAxisAlignment: MainAxisAlignment.center, children: [ Align( alignment: Alignment.topRight, @@ -117,15 +117,17 @@ class ConnectPage extends StatelessWidget { } else { if (chameleonDevice.type == ConnectionType.ble) { - appState.connector.pendingConnection = true; + appState.connector!.pendingConnection = + true; appState.changesMade(); } - await appState.connector + await appState.connector! .connectSpecificDevice( chameleonDevice.port); appState.communicator = ChameleonCommunicator( + appState.log!, port: appState.connector); - appState.connector.pendingConnection = false; + appState.connector!.pendingConnection = false; appState.changesMade(); } }, diff --git a/chameleonultragui/lib/gui/page/debug.dart b/chameleonultragui/lib/gui/page/debug.dart index b7e43a0c..a6001c91 100644 --- a/chameleonultragui/lib/gui/page/debug.dart +++ b/chameleonultragui/lib/gui/page/debug.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:typed_data'; import 'package:chameleonultragui/connector/serial_abstract.dart'; import 'package:chameleonultragui/helpers/flash.dart'; import 'package:chameleonultragui/helpers/general.dart'; @@ -8,6 +7,7 @@ import 'package:chameleonultragui/recovery/recovery.dart'; import 'package:chameleonultragui/main.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:flutter/services.dart'; // Recovery import 'package:chameleonultragui/recovery/recovery.dart' as recovery; @@ -20,255 +20,305 @@ class DebugPage extends StatelessWidget { @override Widget build(BuildContext context) { - var appState = context.watch(); // Get State + var appState = context.watch(); var localizations = AppLocalizations.of(context)!; - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, // Center - children: [ - Align( - alignment: Alignment.topRight, - child: IconButton( - onPressed: () { - // Disconnect - appState.connector.performDisconnect(); - appState.changesMade(); - }, - icon: const Icon(Icons.close), - ), - ), - const Text( - '🐞 Chameleon Ultra GUI DEBUG MENU 🐞', - textScaleFactor: 2, - ), - Text( - localizations.debug_page_warning, - textScaleFactor: 2, - ), - Text('⚠️ ${localizations.warned} ⚠️', textScaleFactor: 3), - Text('${localizations.platform}: ${Platform.operatingSystem}'), - Text('${localizations.serial_protocol}: ${appState.connector}'), - Text( - '${localizations.chameleon_connected}: ${appState.connector.connected}'), - Text( - '${localizations.chameleon_device_type}: ${appState.connector.device}'), - ElevatedButton( - onPressed: () async { - await appState.communicator!.setReaderDeviceMode(true); - var distance = await appState.communicator!.getMf1NTDistance( - 50, - 0x60, - Uint8List.fromList([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])); - bool found = false; - for (var i = 0; i < 0xFF && !found; i++) { - var nonces = await appState.communicator!.getMf1NestedNonces( - 50, - 0x60, - Uint8List.fromList([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), - 0, - 0x61); - var nested = NestedDart( - uid: distance.uid, - distance: distance.distance, - nt0: nonces.nonces[0].nt, - nt0Enc: nonces.nonces[0].ntEnc, - par0: nonces.nonces[0].parity, - nt1: nonces.nonces[1].nt, - nt1Enc: nonces.nonces[1].ntEnc, - par1: nonces.nonces[1].parity); + return Scaffold( + appBar: AppBar( + title: const Text('🐞'), + ), + body: SingleChildScrollView( + child: Center( + child: Card( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Align( + alignment: Alignment.topRight, + child: IconButton( + onPressed: () { + // Disconnect + appState.connector!.performDisconnect(); + appState.changesMade(); + }, + icon: const Icon(Icons.close), + ), + ), + const Text( + '🐞 Chameleon Ultra GUI DEBUG MENU 🐞', + textScaleFactor: 2, + ), + Text( + localizations.debug_page_warning, + textScaleFactor: 2, + ), + Text('⚠️ ${localizations.warned} ⚠️', textScaleFactor: 3), + Text('${localizations.platform}: ${Platform.operatingSystem}'), + Text('${localizations.serial_protocol}: ${appState.connector}'), + Text( + '${localizations.chameleon_connected}: ${appState.connector!.connected}'), + Text( + '${localizations.chameleon_device_type}: ${appState.connector!.device}'), + ElevatedButton( + onPressed: () async { + await appState.communicator!.setReaderDeviceMode(true); + var distance = await appState.communicator!.getMf1NTDistance( + 50, + 0x60, + Uint8List.fromList([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])); + bool found = false; + for (var i = 0; i < 0xFF && !found; i++) { + var nonces = await appState.communicator! + .getMf1NestedNonces( + 50, + 0x60, + Uint8List.fromList( + [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]), + 0, + 0x61); + var nested = NestedDart( + uid: distance.uid, + distance: distance.distance, + nt0: nonces.nonces[0].nt, + nt0Enc: nonces.nonces[0].ntEnc, + par0: nonces.nonces[0].parity, + nt1: nonces.nonces[1].nt, + nt1Enc: nonces.nonces[1].ntEnc, + par1: nonces.nonces[1].parity); - var keys = await recovery.nested(nested); - if (keys.isNotEmpty) { - appState.log.d("Found keys: $keys. Checking them..."); - for (var key in keys) { - var keyBytes = u64ToBytes(key); - if ((await appState.communicator! - .mf1Auth(0x03, 0x61, keyBytes.sublist(2, 8))) == - true) { - appState.log.i( - "Found valid key! Key ${bytesToHex(keyBytes.sublist(2, 8))}"); - found = true; - break; + var keys = await recovery.nested(nested); + if (keys.isNotEmpty) { + appState.log!.d("Found keys: $keys. Checking them..."); + for (var key in keys) { + var keyBytes = u64ToBytes(key); + if ((await appState.communicator! + .mf1Auth(0x03, 0x61, keyBytes.sublist(2, 8))) == + true) { + appState.log!.i( + "Found valid key! Key ${bytesToHex(keyBytes.sublist(2, 8))}"); + found = true; + break; + } + } + } else { + appState.log!.d("Can't find keys, retrying..."); } } - } else { - appState.log.d("Can't find keys, retrying..."); - } - } - }, - child: Column(children: [ - Text(localizations.nested_attack), - ]), - ), - ElevatedButton( - onPressed: () async { - await appState.communicator!.setReaderDeviceMode(true); - var data = await appState.communicator! - .getMf1Darkside(0x03, 0x61, true, 15); - var darkside = DarksideDart(uid: data.uid, items: []); - bool found = false; + }, + child: Column(children: [ + Text(localizations.nested_attack), + ]), + ), + ElevatedButton( + onPressed: () async { + await appState.communicator!.setReaderDeviceMode(true); + var data = await appState.communicator! + .getMf1Darkside(0x03, 0x61, true, 15); + var darkside = DarksideDart(uid: data.uid, items: []); + bool found = false; - for (var tries = 0; tries < 0xFF && !found; tries++) { - darkside.items.add(DarksideItemDart( - nt1: data.nt1, - ks1: data.ks1, - par: data.par, - nr: data.nr, - ar: data.ar)); - var keys = await recovery.darkside(darkside); - if (keys.isNotEmpty) { - appState.log.d("Found keys: $keys. Checking them..."); - for (var key in keys) { - var keyBytes = u64ToBytes(key); - if ((await appState.communicator! - .mf1Auth(0x03, 0x61, keyBytes.sublist(2, 8))) == - true) { - appState.log.i( - "Found valid key! Key ${bytesToHex(keyBytes.sublist(2, 8))}"); - found = true; - break; + for (var tries = 0; tries < 0xFF && !found; tries++) { + darkside.items.add(DarksideItemDart( + nt1: data.nt1, + ks1: data.ks1, + par: data.par, + nr: data.nr, + ar: data.ar)); + var keys = await recovery.darkside(darkside); + if (keys.isNotEmpty) { + appState.log!.d("Found keys: $keys. Checking them..."); + for (var key in keys) { + var keyBytes = u64ToBytes(key); + if ((await appState.communicator! + .mf1Auth(0x03, 0x61, keyBytes.sublist(2, 8))) == + true) { + appState.log!.i( + "Found valid key! Key ${bytesToHex(keyBytes.sublist(2, 8))}"); + found = true; + break; + } + } + } else { + appState.log!.d("Can't find keys, retrying..."); + data = await appState.communicator! + .getMf1Darkside(0x03, 0x61, false, 15); } } - } else { - appState.log.d("Can't find keys, retrying..."); - data = await appState.communicator! - .getMf1Darkside(0x03, 0x61, false, 15); - } - } - }, - child: Column(children: [ - Text(localizations.darkside_attack), - ]), - ), - ElevatedButton( - onPressed: () async { - await appState.communicator!.setReaderDeviceMode(true); - appState.log.d( - "Reader mode (should be true): ${await appState.communicator!.isReaderDeviceMode()}"); - var card = await appState.communicator!.scan14443aTag(); - appState.log.d('Card UID: ${card.uid}'); - appState.log.d('SAK: ${card.sak}'); - appState.log.d('ATQA: ${card.atqa}'); - await appState.communicator!.setReaderDeviceMode(false); - await appState.communicator!.setMf1AntiCollision(card); - }, - child: Column(children: [ - Text(localizations.copy_uid), - ]), - ), - ElevatedButton( - onPressed: () async { - await appState.communicator! - .setSlotTagName(1, "test", TagFrequency.hf); - var name = await appState.communicator! - .getSlotTagName(1, TagFrequency.hf); - appState.log.d(name); - await appState.communicator! - .setSlotTagName(1, "Hello 变色龙!", TagFrequency.hf); - name = await appState.communicator! - .getSlotTagName(1, TagFrequency.hf); - appState.log.d(name); - }, - child: Column(children: [ - Text(localizations.test_naming), - ]), - ), - ElevatedButton( - onPressed: () async { - var darkside = DarksideDart(uid: 2374329723, items: []); - darkside.items.add(DarksideItemDart( - nt1: 913032415, - ks1: 216745674933338888, - par: 0, - nr: 0, - ar: 0)); - darkside.items.add(DarksideItemDart( - nt1: 913032415, - ks1: 1010230244403446283, - par: 0, - nr: 1, - ar: 0)); - var keys = await recovery.darkside(darkside); - appState.log.d("Darkside output: $keys"); - appState.log.d( - "Self test: valid key exists in list ${keys.contains(0xFFFFFFFFFFFF)}"); - }, - child: Column(children: [ - Text(localizations.test_darkside_lib), - ]), - ), - ElevatedButton( - onPressed: () async { - var nested = NestedDart( - uid: 2374329723, - distance: 613, - nt0: 1999585272, - nt0Enc: 3173333529, - par0: 3, - nt1: 128306861, - nt1Enc: 2363514210, - par1: 7); - var keys = await recovery.nested(nested); - appState.log.d("Nested output: $keys"); - appState.log.d( - "Self test: valid key exists in list ${keys.contains(0xFFFFFFFFFFFF)}"); - }, - child: Column(children: [ - Text(localizations.test_nested_lib), - ]), - ), - ElevatedButton( - onPressed: () async { - Uint8List applicationDat, applicationBin; + }, + child: Column(children: [ + Text(localizations.darkside_attack), + ]), + ), + ElevatedButton( + onPressed: () async { + await appState.communicator!.setReaderDeviceMode(true); + appState.log!.d( + "Reader mode (should be true): ${await appState.communicator!.isReaderDeviceMode()}"); + var card = await appState.communicator!.scan14443aTag(); + appState.log!.d('Card UID: ${card.uid}'); + appState.log!.d('SAK: ${card.sak}'); + appState.log!.d('ATQA: ${card.atqa}'); + await appState.communicator!.setReaderDeviceMode(false); + await appState.communicator!.setMf1AntiCollision(card); + }, + child: Column(children: [ + Text(localizations.copy_uid), + ]), + ), + ElevatedButton( + onPressed: () async { + await appState.communicator! + .setSlotTagName(1, "test", TagFrequency.hf); + var name = await appState.communicator! + .getSlotTagName(1, TagFrequency.hf); + appState.log!.d(name); + await appState.communicator! + .setSlotTagName(1, "Hello 变色龙!", TagFrequency.hf); + name = await appState.communicator! + .getSlotTagName(1, TagFrequency.hf); + appState.log!.d(name); + }, + child: Column(children: [ + Text(localizations.test_naming), + ]), + ), + ElevatedButton( + onPressed: () async { + var darkside = DarksideDart(uid: 2374329723, items: []); + darkside.items.add(DarksideItemDart( + nt1: 913032415, + ks1: 216745674933338888, + par: 0, + nr: 0, + ar: 0)); + darkside.items.add(DarksideItemDart( + nt1: 913032415, + ks1: 1010230244403446283, + par: 0, + nr: 1, + ar: 0)); + var keys = await recovery.darkside(darkside); + appState.log!.d("Darkside output: $keys"); + appState.log!.d( + "Self test: valid key exists in list ${keys.contains(0xFFFFFFFFFFFF)}"); + }, + child: Column(children: [ + Text(localizations.test_darkside_lib), + ]), + ), + ElevatedButton( + onPressed: () async { + var nested = NestedDart( + uid: 2374329723, + distance: 613, + nt0: 1999585272, + nt0Enc: 3173333529, + par0: 3, + nt1: 128306861, + nt1Enc: 2363514210, + par1: 7); + var keys = await recovery.nested(nested); + appState.log!.d("Nested output: $keys"); + appState.log!.d( + "Self test: valid key exists in list ${keys.contains(0xFFFFFFFFFFFF)}"); + }, + child: Column(children: [ + Text(localizations.test_nested_lib), + ]), + ), + ElevatedButton( + onPressed: () async { + Uint8List applicationDat, applicationBin; - Uint8List content = await fetchFirmware(ChameleonDevice.ultra); + Uint8List content = + await fetchFirmware(ChameleonDevice.ultra); - (applicationDat, applicationBin) = await unpackFirmware(content); + (applicationDat, applicationBin) = + await unpackFirmware(content); - flashFile( - appState.communicator, - appState, - applicationDat, - applicationBin, - (progress) => appState.log.d("Flashing: $progress%"), - firmwareZip: content); - }, - child: Column(children: [ - Text('💀 ${localizations.dfu_flash_ultra} 💀'), - ]), - ), - ElevatedButton( - onPressed: () async { - Uint8List applicationDat, applicationBin; + flashFile( + appState.communicator, + appState, + applicationDat, + applicationBin, + (progress) => appState.log!.d("Flashing: $progress%"), + firmwareZip: content); + }, + child: Column(children: [ + Text('💀 ${localizations.dfu_flash_ultra} 💀'), + ]), + ), + ElevatedButton( + onPressed: () async { + Uint8List applicationDat, applicationBin; - Uint8List content = await fetchFirmware(ChameleonDevice.lite); + Uint8List content = await fetchFirmware(ChameleonDevice.lite); - (applicationDat, applicationBin) = await unpackFirmware(content); + (applicationDat, applicationBin) = + await unpackFirmware(content); - flashFile( - appState.communicator, - appState, - applicationDat, - applicationBin, - (progress) => appState.log.d("Flashing: $progress%"), - firmwareZip: content); - }, - child: Column(children: [ - Text('💀 ${localizations.dfu_flash_lite} 💀'), - ]), - ), - const SizedBox(height: 10), - ElevatedButton( - onPressed: () async { - await appState.communicator!.factoryReset(); - }, - child: Column(children: [ - Text( - '✅ ${localizations.safe_option}: ${localizations.restart_chameleon} ✅'), - ]), + flashFile( + appState.communicator, + appState, + applicationDat, + applicationBin, + (progress) => appState.log!.d("Flashing: $progress%"), + firmwareZip: content); + }, + child: Column(children: [ + Text('💀 ${localizations.dfu_flash_lite} 💀'), + ]), + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () async { + await appState.communicator!.factoryReset(); + }, + child: Column(children: [ + Text( + '✅ ${localizations.safe_option}: ${localizations.restart_chameleon} ✅'), + ]), + ), + const SizedBox(height: 30), + ElevatedButton( + onPressed: () async { + appState.sharedPreferencesProvider.setDebugLogging(true); + await appState.connector!.performDisconnect(); + appState.log = null; + appState.connector = null; + appState.changesMade(); + }, + child: const Column(children: [ + Text('Enable production logging'), + ]), + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () async { + appState.sharedPreferencesProvider.setDebugLogging(false); + await appState.connector!.performDisconnect(); + appState.log = null; + appState.connector = null; + appState.changesMade(); + }, + child: const Column(children: [ + Text('Disable production logging'), + ]), + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () async { + await Clipboard.setData(ClipboardData( + text: appState.sharedPreferencesProvider + .getLogLines() + .join("\n"))); + }, + child: const Column(children: [ + Text('Copy logs to clipboard'), + ]), + ), + const SizedBox(height: 10), + ], ), - ], - ), - ); + )))); } } diff --git a/chameleonultragui/lib/gui/page/flashing.dart b/chameleonultragui/lib/gui/page/flashing.dart index 863c8a23..484bc9c4 100644 --- a/chameleonultragui/lib/gui/page/flashing.dart +++ b/chameleonultragui/lib/gui/page/flashing.dart @@ -23,7 +23,7 @@ class FlashingPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( - appState.connector.device == ChameleonDevice.ultra + appState.connector!.device == ChameleonDevice.ultra ? appState.easterEgg ? 'assets/black-ultra-standing-front-flashing.png' : 'assets/black-ultra-standing-front.png' @@ -36,8 +36,8 @@ class FlashingPage extends StatelessWidget { const SizedBox(height: 20), Text( appState.easterEgg - ? 'Your Chameleon ${appState.connector.device == ChameleonDevice.ultra ? 'Ultra' : 'Lite'} is flashing' - : 'Installing firmware on your Chameleon ${appState.connector.device == ChameleonDevice.ultra ? 'Ultra' : 'Lite'}', + ? 'Your Chameleon ${appState.connector!.device == ChameleonDevice.ultra ? 'Ultra' : 'Lite'} is flashing' + : 'Installing firmware on your Chameleon ${appState.connector!.device == ChameleonDevice.ultra ? 'Ultra' : 'Lite'}', style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), textAlign: TextAlign.center, diff --git a/chameleonultragui/lib/gui/page/home.dart b/chameleonultragui/lib/gui/page/home.dart index e2f3c363..28109198 100644 --- a/chameleonultragui/lib/gui/page/home.dart +++ b/chameleonultragui/lib/gui/page/home.dart @@ -32,7 +32,7 @@ class HomePageState extends State { try { usedSlots = await appState.communicator!.getUsedSlots(); } catch (e) { - appState.log.e(e); + appState.log!.e(e); } return ( @@ -131,7 +131,7 @@ class HomePageState extends State { body: const Center(child: CircularProgressIndicator()), ); } else if (snapshot.hasError) { - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); return Text('${localizations.error}: ${snapshot.error.toString()}'); } else { final ( @@ -147,7 +147,7 @@ class HomePageState extends State { ), body: Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, // Center + mainAxisAlignment: MainAxisAlignment.center, children: [ Align( alignment: Alignment.topRight, @@ -161,7 +161,7 @@ class HomePageState extends State { IconButton( onPressed: () { // Disconnect - appState.connector.performDisconnect(); + appState.connector!.performDisconnect(); appState.changesMade(); }, icon: const Icon(Icons.close), @@ -172,9 +172,9 @@ class HomePageState extends State { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - Text(appState.connector.portName, + Text(appState.connector!.portName, style: const TextStyle(fontSize: 20)), - Icon(appState.connector.connectionType == + Icon(appState.connector!.connectionType == ConnectionType.ble ? Icons.bluetooth : Icons.usb), @@ -189,7 +189,7 @@ class HomePageState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "Chameleon ${appState.connector.device == ChameleonDevice.ultra ? "Ultra" : "Lite"}", + "Chameleon ${appState.connector!.device == ChameleonDevice.ultra ? "Ultra" : "Lite"}", style: TextStyle( fontWeight: FontWeight.bold, fontSize: @@ -205,7 +205,7 @@ class HomePageState extends State { child: FractionallySizedBox( widthFactor: 0.4, child: Image.asset( - appState.connector.device == ChameleonDevice.ultra + appState.connector!.device == ChameleonDevice.ultra ? 'assets/black-ultra-standing-front.png' : 'assets/black-lite-standing-front.png', fit: BoxFit.contain, @@ -234,7 +234,7 @@ class HomePageState extends State { try { latestCommit = await latestAvailableCommit( - appState.connector.device); + appState.connector!.device); } catch (e) { if (context.mounted) { ScaffoldMessenger.of(context) @@ -254,7 +254,7 @@ class HomePageState extends State { return; } - appState.log.i("Latest commit: $latestCommit"); + appState.log!.i("Latest commit: $latestCommit"); if (latestCommit.isEmpty) { return; @@ -264,7 +264,7 @@ class HomePageState extends State { context.mounted) { snackBar = SnackBar( content: Text(localizations.up_to_date( - appState.connector.device == + appState.connector!.device == ChameleonDevice.ultra ? "Ultra" : "Lite")), @@ -279,7 +279,7 @@ class HomePageState extends State { } else if (context.mounted) { snackBar = SnackBar( content: Text(localizations.downloading_fw( - appState.connector.device == + appState.connector!.device == ChameleonDevice.ultra ? "Ultra" : "Lite")), diff --git a/chameleonultragui/lib/gui/page/pending_connection.dart b/chameleonultragui/lib/gui/page/pending_connection.dart index b764235c..7b65a01a 100644 --- a/chameleonultragui/lib/gui/page/pending_connection.dart +++ b/chameleonultragui/lib/gui/page/pending_connection.dart @@ -26,7 +26,7 @@ class PendingConnectionPage extends StatelessWidget { localizations.connecting_to_ble, ), const SizedBox(height: 10), - if (!appState.connector.connected) ...[ + if (!appState.connector!.connected) ...[ Text( localizations.default_ble_password, style: const TextStyle(fontWeight: FontWeight.bold), diff --git a/chameleonultragui/lib/gui/page/read_card.dart b/chameleonultragui/lib/gui/page/read_card.dart index 25c3b34f..402a1da6 100644 --- a/chameleonultragui/lib/gui/page/read_card.dart +++ b/chameleonultragui/lib/gui/page/read_card.dart @@ -225,13 +225,14 @@ class ReadCardPageState extends State { ar: data.ar)); var keys = await recovery.darkside(darkside); if (keys.isNotEmpty) { - appState.log.d("Darkside: Found keys: $keys. Checking them..."); + appState.log! + .d("Darkside: Found keys: $keys. Checking them..."); for (var key in keys) { var keyBytes = u64ToBytes(key); await asyncSleep(1); // Let GUI update if ((await appState.communicator! .mf1Auth(0x03, 0x61, keyBytes.sublist(2, 8)))) { - appState.log.i( + appState.log!.i( "Darkside: Found valid key! Key ${bytesToHex(keyBytes.sublist(2, 8))}"); status.validKeys[40] = keyBytes.sublist(2, 8); status.checkMarks[40] = ChameleonKeyCheckmark.found; @@ -240,7 +241,7 @@ class ReadCardPageState extends State { } } } else { - appState.log.d("Can't find keys, retrying..."); + appState.log!.d("Can't find keys, retrying..."); data = await appState.communicator! .getMf1Darkside(0x03, 0x61, false, 15); } @@ -321,7 +322,7 @@ class ReadCardPageState extends State { var keys = await recovery.nested(nested); if (keys.isNotEmpty) { - appState.log.d("Found keys: $keys. Checking them..."); + appState.log!.d("Found keys: $keys. Checking them..."); for (var key in keys) { var keyBytes = u64ToBytes(key); await asyncSleep(1); // Let GUI update @@ -329,7 +330,7 @@ class ReadCardPageState extends State { mfClassicGetSectorTrailerBlockBySector(sector), 0x60 + keyType, keyBytes.sublist(2, 8)))) { - appState.log.i( + appState.log!.i( "Found valid key! Key ${bytesToHex(keyBytes.sublist(2, 8))}"); found = true; status.validKeys[sector + (keyType * 40)] = @@ -341,7 +342,7 @@ class ReadCardPageState extends State { } } } else { - appState.log.e("Can't find keys, retrying..."); + appState.log!.e("Can't find keys, retrying..."); } } } @@ -372,7 +373,7 @@ class ReadCardPageState extends State { if (mifare) { mf1Type = mfClassicGetType(card.atqa, card.sak); } else { - appState.log.e("Not Mifare Classic tag!"); + appState.log!.e("Not Mifare Classic tag!"); return; } @@ -394,7 +395,7 @@ class ReadCardPageState extends State { ...status.selectedDictionary!.keys, ...gMifareClassicKeys ]) { - appState.log + appState.log! .d("Checking $key on sector $sector, key type $keyType"); await asyncSleep(1); // Let GUI update if (await appState.communicator!.mf1Auth( @@ -481,11 +482,11 @@ class ReadCardPageState extends State { block < mfClassicGetBlockCountBySector(sector); block++) { for (var keyType = 0; keyType < 2; keyType++) { - appState.log + appState.log! .d("Dumping sector $sector, block $block with key $keyType"); if (status.validKeys[sector + (keyType * 40)].isEmpty) { - appState.log.w("Skipping missing key"); + appState.log!.w("Skipping missing key"); status.cardData[block + mfClassicGetFirstBlockCountBySector(sector)] = Uint8List(16); continue; @@ -694,10 +695,10 @@ class ReadCardPageState extends State { ], ElevatedButton( onPressed: () async { - if (appState.connector.device == + if (appState.connector!.device == ChameleonDevice.ultra) { await readHFInfo(appState); - } else if (appState.connector.device == + } else if (appState.connector!.device == ChameleonDevice.lite) { showDialog( context: context, @@ -1147,10 +1148,10 @@ class ReadCardPageState extends State { ], ElevatedButton( onPressed: () async { - if (appState.connector.device == + if (appState.connector!.device == ChameleonDevice.ultra) { await readLFInfo(appState); - } else if (appState.connector.device == + } else if (appState.connector!.device == ChameleonDevice.lite) { showDialog( context: context, diff --git a/chameleonultragui/lib/gui/page/settings.dart b/chameleonultragui/lib/gui/page/settings.dart index 317be869..173ca954 100644 --- a/chameleonultragui/lib/gui/page/settings.dart +++ b/chameleonultragui/lib/gui/page/settings.dart @@ -48,7 +48,7 @@ class SettingsMainPageState extends State { @override Widget build(BuildContext context) { - var appState = context.watch(); // Get State + var appState = context.watch(); var localizations = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( @@ -56,7 +56,7 @@ class SettingsMainPageState extends State { ), body: Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, // Center + mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 10), Text(localizations.sidebar_expansion, @@ -118,24 +118,6 @@ class SettingsMainPageState extends State { .setTheme(ThemeMode.light); } appState.changesMade(); - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: Text(localizations.restart_required), - content: Center( - child: Text(localizations.take_effects, - style: - const TextStyle(fontWeight: FontWeight.bold)), - ), - actions: [ - TextButton( - onPressed: () => - Navigator.pop(context, localizations.ok), - child: Text(localizations.ok), - ), - ], - ), - ); }), const SizedBox(height: 10), Text( @@ -152,23 +134,6 @@ class SettingsMainPageState extends State { onChanged: (value) { appState.sharedPreferencesProvider.setThemeColor(value ?? 0); appState.changesMade(); - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: Text(localizations.restart_required), - content: Center( - child: Text(localizations.take_effects, - style: const TextStyle(fontWeight: FontWeight.bold)), - ), - actions: [ - TextButton( - onPressed: () => - Navigator.pop(context, localizations.ok), - child: Text(localizations.ok), - ), - ], - ), - ); }, items: [ DropdownMenuItem( @@ -219,23 +184,6 @@ class SettingsMainPageState extends State { appState.sharedPreferencesProvider .setLocale(Locale(value ?? 'en')); appState.changesMade(); - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: Text(localizations.restart_required), - content: Center( - child: Text(localizations.take_effects, - style: const TextStyle(fontWeight: FontWeight.bold)), - ), - actions: [ - TextButton( - onPressed: () => - Navigator.pop(context, localizations.ok), - child: Text(localizations.ok), - ), - ], - ), - ); }, items: AppLocalizations.supportedLocales.map((locale) { return DropdownMenuItem( @@ -317,10 +265,6 @@ class SettingsMainPageState extends State { ), ), actions: [ - /* TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), - child: const Text('Cancel'), - ), */ // A Cancel button on an about widget?? TextButton( onPressed: () => Navigator.pop(context, localizations.ok), child: Text(localizations.ok), diff --git a/chameleonultragui/lib/gui/page/slot_manager.dart b/chameleonultragui/lib/gui/page/slot_manager.dart index 7eb6534f..b72de6b1 100644 --- a/chameleonultragui/lib/gui/page/slot_manager.dart +++ b/chameleonultragui/lib/gui/page/slot_manager.dart @@ -53,8 +53,8 @@ class SlotManagerPageState extends State { try { await appState.communicator!.getFirmwareVersion(); } catch (_) { - appState.log.e("Lost connection to Chameleon!"); - appState.connector.performDisconnect(); + appState.log!.e("Lost connection to Chameleon!"); + appState.connector!.performDisconnect(); appState.changesMade(); } } @@ -468,7 +468,7 @@ class CardSearchDelegate extends SearchDelegate { appState.changesMade(); refresh(gridPosition); } else { - appState.log.e("Can't write this card type yet."); + appState.log!.e("Can't write this card type yet."); close(context, card.name); } }, diff --git a/chameleonultragui/lib/gui/widget/staggered_grid_view.dart b/chameleonultragui/lib/gui/widget/staggered_grid_view.dart index 57faf8c8..c4ecc592 100644 --- a/chameleonultragui/lib/gui/widget/staggered_grid_view.dart +++ b/chameleonultragui/lib/gui/widget/staggered_grid_view.dart @@ -1613,13 +1613,17 @@ abstract class RenderSliverVariableSizeBoxAdaptor extends RenderSliver @override void attach(PipelineOwner owner) { super.attach(owner); - _keepAliveBucket.values.forEach((child) => child.attach(owner)); + for (var child in _keepAliveBucket.values) { + child.attach(owner); + } } @override void detach() { super.detach(); - _keepAliveBucket.values.forEach((child) => child.detach()); + for (var child in _keepAliveBucket.values) { + child.detach(); + } } @override @@ -1931,13 +1935,17 @@ mixin TileContainerRenderObjectMixin child.attach(owner)); + for (var child in _childRenderObjects.values) { + child.attach(owner); + } } @override void detach() { super.detach(); - _childRenderObjects.values.forEach((child) => child.detach()); + for (var child in _childRenderObjects.values) { + child.detach(); + } } @override diff --git a/chameleonultragui/lib/helpers/flash.dart b/chameleonultragui/lib/helpers/flash.dart index 41031653..78603930 100644 --- a/chameleonultragui/lib/helpers/flash.dart +++ b/chameleonultragui/lib/helpers/flash.dart @@ -195,7 +195,7 @@ void validateFiles(Uint8List dat, Uint8List bin) { Future flashFirmware(ChameleonGUIState appState) async { Uint8List applicationDat, applicationBin; - Uint8List content = await fetchFirmware(appState.connector.device); + Uint8List content = await fetchFirmware(appState.connector!.device); (applicationDat, applicationBin) = await unpackFirmware(content); @@ -241,11 +241,11 @@ Future flashFile( if (enterDFU) { await connection!.enterDFUMode(); - await appState.connector.performDisconnect(); + await appState.connector!.performDisconnect(); } - if (appState.connector.isOpen) { - await appState.connector.performDisconnect(); + if (appState.connector!.isOpen) { + await appState.connector!.performDisconnect(); } if (Platform.isAndroid) { @@ -257,7 +257,7 @@ Future flashFile( while (chameleons.isEmpty) { await asyncSleep(250); - chameleons = await appState.connector.availableChameleons(true); + chameleons = await appState.connector!.availableChameleons(true); } var toFlash = chameleons[0]; @@ -283,17 +283,17 @@ Future flashFile( } } - await appState.connector.connectSpecificDevice(chameleons[0].port); + await appState.connector!.connectSpecificDevice(chameleons[0].port); - var dfu = DFUCommunicator( + var dfu = DFUCommunicator(appState.log!, port: appState.connector, viaBLE: toFlash.type == ConnectionType.ble); await dfu.setPRN(); await dfu.getMTU(); appState.changesMade(); await dfu.flashFirmware(0x01, applicationDat, callback); await dfu.flashFirmware(0x02, applicationBin, callback); - appState.log.i("Firmware flashed!"); - appState.connector.performDisconnect(); + appState.log!.i("Firmware flashed!"); + appState.connector!.performDisconnect(); await asyncSleep(500); // allow exit DFU mode appState.changesMade(); } diff --git a/chameleonultragui/lib/helpers/general.dart b/chameleonultragui/lib/helpers/general.dart index 82a61687..d935c23a 100644 --- a/chameleonultragui/lib/helpers/general.dart +++ b/chameleonultragui/lib/helpers/general.dart @@ -2,7 +2,9 @@ import 'dart:io'; import 'dart:typed_data'; import 'dart:io' show Platform; import 'package:chameleonultragui/bridge/chameleon.dart'; +import 'package:chameleonultragui/sharedprefsprovider.dart'; import 'package:flutter/material.dart'; +import 'package:logger/logger.dart'; Future asyncSleep(int milliseconds) async { await Future.delayed(Duration(milliseconds: milliseconds)); @@ -158,3 +160,16 @@ TagFrequency chameleonTagToFrequency(TagType tag) { return TagFrequency.hf; } } + +class SharedPreferencesLogger extends LogOutput { + SharedPreferencesProvider? provider; + + SharedPreferencesLogger(this.provider); + + @override + void output(OutputEvent event) { + for (var line in event.lines) { + provider?.addLogLine(line); + } + } +} diff --git a/chameleonultragui/lib/helpers/github.dart b/chameleonultragui/lib/helpers/github.dart index 4a9d497f..37e3ccf0 100644 --- a/chameleonultragui/lib/helpers/github.dart +++ b/chameleonultragui/lib/helpers/github.dart @@ -28,6 +28,12 @@ List> developers = [ 'avatarUrl': 'https://avatars.githubusercontent.com/u/61940251', 'username': 'Akisame-AI' }, + { + 'name': 'Andrés Ruz Nieto', + 'description': 'Translator', + 'avatarUrl': 'https://avatars.githubusercontent.com/u/40019177', + 'username': 'aruznieto' + }, ]; Future>> fetchGitHubContributors() async { diff --git a/chameleonultragui/lib/l10n/app_da.arb b/chameleonultragui/lib/l10n/app_da.arb index 1a40e043..be99a33c 100644 --- a/chameleonultragui/lib/l10n/app_da.arb +++ b/chameleonultragui/lib/l10n/app_da.arb @@ -137,7 +137,7 @@ "slot_status": "Slot status", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Mifare Classic emulator indstillinger", + "mifare_classic_emulator_settings": "Mifare Classic emulator indstillinger", "mode_gen1a": "Gen1A Magic Mode", "mode_gen2": "Gen2 Magic Mode", "use_from_block": "Brug UID/SAK/ATQA fra blok 0", diff --git a/chameleonultragui/lib/l10n/app_de.arb b/chameleonultragui/lib/l10n/app_de.arb index 4f7d5679..92a3dda6 100644 --- a/chameleonultragui/lib/l10n/app_de.arb +++ b/chameleonultragui/lib/l10n/app_de.arb @@ -137,7 +137,7 @@ "slot_status": "Slot-Status", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Mifare Classic Emulator Einstellungen", + "mifare_classic_emulator_settings": "Mifare Classic Emulator Einstellungen", "mode_gen1a": "Gen1A Magic Modus", "mode_gen2": "Gen2 Magic Modus", "use_from_block": "Benutze UID/SAK/ATQA aus 0 Block", diff --git a/chameleonultragui/lib/l10n/app_de_AT.arb b/chameleonultragui/lib/l10n/app_de_AT.arb index d887feca..efed542f 100644 --- a/chameleonultragui/lib/l10n/app_de_AT.arb +++ b/chameleonultragui/lib/l10n/app_de_AT.arb @@ -137,7 +137,7 @@ "slot_status": "Slot-Status", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Mifare Classic Emulator Einstellungn", + "mifare_classic_emulator_settings": "Mifare Classic Emulator Einstellungn", "mode_gen1a": "Gen1A Magic Modus", "mode_gen2": "Gen2 Magic Modus", "use_from_block": "Nimm UID/SAK/ATQA aus Block Nui", diff --git a/chameleonultragui/lib/l10n/app_en.arb b/chameleonultragui/lib/l10n/app_en.arb index de18508e..90489d3e 100644 --- a/chameleonultragui/lib/l10n/app_en.arb +++ b/chameleonultragui/lib/l10n/app_en.arb @@ -137,7 +137,7 @@ "slot_status": "Slot Status", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Mifare Classic emulator settings", + "mifare_classic_emulator_settings": "Mifare Classic emulator settings", "mode_gen1a": "Gen1A Magic Mode", "mode_gen2": "Gen2 Magic Mode", "use_from_block": "Use UID/SAK/ATQA from 0 block", diff --git a/chameleonultragui/lib/l10n/app_es.arb b/chameleonultragui/lib/l10n/app_es.arb index 243839cc..87c3747f 100644 --- a/chameleonultragui/lib/l10n/app_es.arb +++ b/chameleonultragui/lib/l10n/app_es.arb @@ -137,7 +137,7 @@ "slot_status": "Estado de la ranura", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Ajustes de emulacion Mifare Classic", + "mifare_classic_emulator_settings": "Ajustes de emulacion Mifare Classic", "mode_gen1a": "Modo Gen1A Magic", "mode_gen2": "Modo Gen2 Magic", "use_from_block": "Usar UID/SAK/ATQA desde el bloque 0", diff --git a/chameleonultragui/lib/l10n/app_fr.arb b/chameleonultragui/lib/l10n/app_fr.arb index 589d2a78..1e5dcebd 100644 --- a/chameleonultragui/lib/l10n/app_fr.arb +++ b/chameleonultragui/lib/l10n/app_fr.arb @@ -137,7 +137,7 @@ "slot_status": "Statut de l'emplacement", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Paramètres de l'émulateur Mifare Classic", + "mifare_classic_emulator_settings": "Paramètres de l'émulateur Mifare Classic", "mode_gen1a": "Gen1A Magic Mode", "mode_gen2": "Gen2 Magic Mode", "use_from_block": "Utiliser UID/SAK/ATQA à partir de 0 bloc", diff --git a/chameleonultragui/lib/l10n/app_ko.arb b/chameleonultragui/lib/l10n/app_ko.arb index d72dc63e..ed45efa6 100644 --- a/chameleonultragui/lib/l10n/app_ko.arb +++ b/chameleonultragui/lib/l10n/app_ko.arb @@ -137,7 +137,7 @@ "slot_status": "슬롯 상태", "hf": "HF", "lf": "LF", - "mifare_clasic_e_s": "Mifare Classic 에뮬레이터 설정", + "mifare_classic_emulator_settings": "Mifare Classic 에뮬레이터 설정", "mode_gen1a": "Gen1A 매직 모드", "mode_gen2": "Gen2 매직 모드", "use_from_block": "0 블록의 UID/SAK/ATQA 사용", diff --git a/chameleonultragui/lib/l10n/app_ru.arb b/chameleonultragui/lib/l10n/app_ru.arb index bd7b0c14..f811fc2a 100644 --- a/chameleonultragui/lib/l10n/app_ru.arb +++ b/chameleonultragui/lib/l10n/app_ru.arb @@ -137,7 +137,7 @@ "slot_status": "Статус слота", "hf": "ВЧ", "lf": "НЧ", - "mifare_clasic_e_s": "Настройка эмулятора Mifare Classic", + "mifare_classic_emulator_settings": "Настройка эмулятора Mifare Classic", "mode_gen1a": "Режим Gen1A", "mode_gen2": "Режим Gen2", "use_from_block": "Использовать UID/SAK/ATQA из 0 блока", diff --git a/chameleonultragui/lib/main.dart b/chameleonultragui/lib/main.dart index 9c85062b..d5136eff 100644 --- a/chameleonultragui/lib/main.dart +++ b/chameleonultragui/lib/main.dart @@ -3,6 +3,7 @@ import 'package:chameleonultragui/bridge/chameleon.dart'; import 'package:chameleonultragui/connector/serial_abstract.dart'; import 'package:chameleonultragui/connector/serial_android.dart'; import 'package:chameleonultragui/connector/serial_ble.dart'; +import 'package:chameleonultragui/helpers/general.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; @@ -54,33 +55,8 @@ class ChameleonGUI extends StatelessWidget { create: (context) => ChameleonGUIState(_sharedPreferencesProvider), ), ], - child: MaterialApp( - title: 'Chameleon Ultra GUI', // App Name - locale: _sharedPreferencesProvider.getLocale(), - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - theme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed( - seedColor: - _sharedPreferencesProvider.getThemeColor()), // Color Scheme - brightness: Brightness.light, // Light Theme - ), - darkTheme: ThemeData.dark().copyWith( - colorScheme: ColorScheme.fromSeed( - seedColor: _sharedPreferencesProvider.getThemeColor(), - brightness: Brightness.dark), // Color Scheme - brightness: Brightness.dark, // Dark Theme - ), - themeMode: _sharedPreferencesProvider.getTheme(), // Dark Theme - home: const MainPage(), - ), + child: MainPage(sharedPreferencesProvider: _sharedPreferencesProvider), ); - - //return ChangeNotifierProvider( - // create: (context) => MyAppState(), - // child: - //); } } @@ -88,11 +64,12 @@ class ChameleonGUIState extends ChangeNotifier { final SharedPreferencesProvider sharedPreferencesProvider; ChameleonGUIState(this.sharedPreferencesProvider); -// Android uses AndroidSerial, iOS can only use BLESerial -// The rest (desktops?) can use NativeSerial - AbstractSerial connector = Platform.isAndroid - ? AndroidSerial() - : (Platform.isIOS ? BLESerial() : NativeSerial()); + SharedPreferencesProvider? _sharedPreferencesProvider; + Logger? log; // Logger + + // Android uses AndroidSerial, iOS can only use BLESerial + // The rest (desktops?) can use NativeSerial + AbstractSerial? connector; ChameleonCommunicator? communicator; bool devMode = false; @@ -103,8 +80,6 @@ class ChameleonGUIState extends ChangeNotifier { bool forceMfkey32Page = false; - Logger log = Logger(); // Logger, App wide logger - void changesMade() { notifyListeners(); } @@ -116,20 +91,30 @@ class ChameleonGUIState extends ChangeNotifier { } class MainPage extends StatefulWidget { - // Main Page - const MainPage({super.key}); + const MainPage({super.key, required this.sharedPreferencesProvider}); + + final SharedPreferencesProvider sharedPreferencesProvider; @override State createState() => _MainPageState(); } class _MainPageState extends State { - // Main Page State, Sidebar visible, Navigation var selectedIndex = 0; @override Widget build(BuildContext context) { - var appState = context.watch(); // Get State + var appState = context.watch(); + appState._sharedPreferencesProvider = widget.sharedPreferencesProvider; + appState.log ??= Logger( + output: appState._sharedPreferencesProvider!.isDebugLogging() + ? SharedPreferencesLogger(appState._sharedPreferencesProvider!) + : ConsoleOutput()); + appState.connector ??= Platform.isAndroid + ? AndroidSerial(log: appState.log!) + : (Platform.isIOS + ? BLESerial(log: appState.log!) + : NativeSerial(log: appState.log!)); if (appState.sharedPreferencesProvider.getSideBarAutoExpansion()) { double width = MediaQuery.of(context).size.width; if (width >= 600) { @@ -142,7 +127,7 @@ class _MainPageState extends State { appState.devMode = appState.sharedPreferencesProvider.isDebugMode(); Widget page; // Set Page - if (!appState.connector.connected && + if (!appState.connector!.connected && selectedIndex != 0 && selectedIndex != 2 && selectedIndex != 5 && @@ -154,11 +139,11 @@ class _MainPageState extends State { switch (selectedIndex) { // Sidebar Navigation case 0: - if (appState.connector.pendingConnection) { + if (appState.connector!.pendingConnection) { page = const PendingConnectionPage(); } else { - if (appState.connector.connected) { - if (appState.connector.isDFU) { + if (appState.connector!.connected) { + if (appState.connector!.isDFU) { page = const FlashingPage(); } else { page = const HomePage(); @@ -201,78 +186,100 @@ class _MainPageState extends State { WakelockPlus.toggle(enable: page is FlashingPage); - return LayoutBuilder(// Build Page - builder: (context, constraints) { - return Scaffold( - body: Row( - children: [ - (!appState.connector.isDFU || !appState.connector.connected) - ? SafeArea( - child: NavigationRail( - // Sidebar - extended: appState.sharedPreferencesProvider - .getSideBarExpanded(), - destinations: [ - // Sidebar Items - NavigationRailDestination( - icon: const Icon(Icons.home), - label: Text( - AppLocalizations.of(context)!.home), // Home - ), - NavigationRailDestination( - disabled: !appState.connector.connected, - icon: const Icon(Icons.widgets), - label: Text( - AppLocalizations.of(context)!.slot_manager), - ), - NavigationRailDestination( - icon: - const Icon(Icons.auto_awesome_motion_outlined), - label: - Text(AppLocalizations.of(context)!.saved_cards), - ), - NavigationRailDestination( - disabled: !appState.connector.connected, - icon: const Icon(Icons.wifi), - label: - Text(AppLocalizations.of(context)!.read_card), - ), - NavigationRailDestination( - disabled: !appState.connector.connected, - icon: const Icon(Icons.credit_card), - label: - Text(AppLocalizations.of(context)!.write_card), - ), - NavigationRailDestination( - icon: const Icon(Icons.settings), - label: Text(AppLocalizations.of(context)!.settings), - ), - if (appState.devMode) + return MaterialApp( + title: 'Chameleon Ultra GUI', // App Name + locale: widget.sharedPreferencesProvider.getLocale(), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: widget.sharedPreferencesProvider + .getThemeColor()), // Color Scheme + brightness: Brightness.light, // Light Theme + ), + darkTheme: ThemeData.dark().copyWith( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: widget.sharedPreferencesProvider.getThemeColor(), + brightness: Brightness.dark), // Color Scheme + brightness: Brightness.dark, // Dark Theme + ), + themeMode: widget.sharedPreferencesProvider.getTheme(), // Dark Theme + home: LayoutBuilder(// Build Page + builder: (context, constraints) { + return Scaffold( + body: Row( + children: [ + (!appState.connector!.isDFU || !appState.connector!.connected) + ? SafeArea( + child: NavigationRail( + // Sidebar + extended: appState.sharedPreferencesProvider + .getSideBarExpanded(), + destinations: [ + // Sidebar Items NavigationRailDestination( - icon: const Icon(Icons.bug_report), + icon: const Icon(Icons.home), label: Text( - '🐞 ${AppLocalizations.of(context)!.debug} 🐞'), + AppLocalizations.of(context)!.home), // Home ), - ], - selectedIndex: selectedIndex, - onDestinationSelected: (value) { - setState(() { - selectedIndex = value; - }); - }, - ), - ) - : const SizedBox(), - Expanded( - child: Container( - color: Theme.of(context).colorScheme.primaryContainer, - child: page, + NavigationRailDestination( + disabled: !appState.connector!.connected, + icon: const Icon(Icons.widgets), + label: Text( + AppLocalizations.of(context)!.slot_manager), + ), + NavigationRailDestination( + icon: const Icon( + Icons.auto_awesome_motion_outlined), + label: Text( + AppLocalizations.of(context)!.saved_cards), + ), + NavigationRailDestination( + disabled: !appState.connector!.connected, + icon: const Icon(Icons.wifi), + label: + Text(AppLocalizations.of(context)!.read_card), + ), + NavigationRailDestination( + disabled: !appState.connector!.connected, + icon: const Icon(Icons.credit_card), + label: Text( + AppLocalizations.of(context)!.write_card), + ), + NavigationRailDestination( + icon: const Icon(Icons.settings), + label: + Text(AppLocalizations.of(context)!.settings), + ), + if (appState.devMode) + NavigationRailDestination( + icon: const Icon(Icons.bug_report), + label: Text( + '🐞 ${AppLocalizations.of(context)!.debug} 🐞'), + ), + ], + selectedIndex: selectedIndex, + onDestinationSelected: (value) { + setState(() { + selectedIndex = value; + }); + }, + ), + ) + : const SizedBox(), + Expanded( + child: Container( + color: Theme.of(context).colorScheme.primaryContainer, + child: page, + ), ), - ), - ], - ), - bottomNavigationBar: const BottomProgressBar()); - }); + ], + ), + bottomNavigationBar: const BottomProgressBar()); + }), + ); } } @@ -282,7 +289,7 @@ class BottomProgressBar extends StatelessWidget { @override Widget build(BuildContext context) { var appState = context.watch(); - return (appState.connector.connected && appState.connector.isDFU) + return (appState.connector!.connected && appState.connector!.isDFU) ? LinearProgressIndicator( value: appState.progress, backgroundColor: Colors.grey[300], diff --git a/chameleonultragui/lib/sharedprefsprovider.dart b/chameleonultragui/lib/sharedprefsprovider.dart index 493c09c1..cde9d0e5 100644 --- a/chameleonultragui/lib/sharedprefsprovider.dart +++ b/chameleonultragui/lib/sharedprefsprovider.dart @@ -316,7 +316,6 @@ class SharedPreferencesProvider extends ChangeNotifier { return 'Français'; case 'ko': return '한국어'; - case 'en': return 'English'; case 'da': @@ -325,4 +324,28 @@ class SharedPreferencesProvider extends ChangeNotifier { return '------'; } } + + bool isDebugLogging() { + return _sharedPreferences.getBool('debug_logging') ?? false; + } + + void setDebugLogging(bool value) { + _sharedPreferences.setBool('debug_logging', value); + } + + void addLogLine(String value) { + List rows = + _sharedPreferences.getStringList('debug_logging_value') ?? []; + rows.add(value); + + if (rows.length > 2500) { + rows.removeAt(0); + } + + _sharedPreferences.setStringList('debug_logging_value', rows); + } + + List getLogLines() { + return _sharedPreferences.getStringList('debug_logging_value') ?? []; + } }