diff --git a/chameleonultragui/lib/gui/menu/dictionary_edit.dart b/chameleonultragui/lib/gui/menu/dictionary_edit.dart index e7c2afcd..227b5855 100644 --- a/chameleonultragui/lib/gui/menu/dictionary_edit.dart +++ b/chameleonultragui/lib/gui/menu/dictionary_edit.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:chameleonultragui/sharedprefsprovider.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'dart:typed_data'; import 'package:provider/provider.dart'; import 'package:chameleonultragui/main.dart'; @@ -14,7 +15,8 @@ class DictionaryEditMenu extends StatefulWidget { final Dictionary dict; final bool isNew; - const DictionaryEditMenu({Key? key, required this.dict, this.isNew = false}) : super(key: key); + const DictionaryEditMenu({Key? key, required this.dict, this.isNew = false}) + : super(key: key); @override DictionaryEditMenuState createState() => DictionaryEditMenuState(); @@ -87,7 +89,7 @@ class DictionaryEditMenuState extends State { labelText: localizations.name, hintText: localizations.enter_dict_name, prefix: IconButton( - icon: Icon(Icons.nfc, color: currentColor), + icon: Icon(Icons.key, color: currentColor), onPressed: () async { showDialog( context: context, @@ -137,6 +139,9 @@ class DictionaryEditMenuState extends State { TextFormField( maxLines: null, controller: keysController, + style: GoogleFonts.robotoMono( + fontSize: 16.0, + ), decoration: InputDecoration( labelText: localizations.keys, hintText: localizations.enter_dict_keys, diff --git a/chameleonultragui/lib/gui/page/saved_cards.dart b/chameleonultragui/lib/gui/page/saved_cards.dart index 85267d3d..10fa6f86 100644 --- a/chameleonultragui/lib/gui/page/saved_cards.dart +++ b/chameleonultragui/lib/gui/page/saved_cards.dart @@ -10,6 +10,7 @@ import 'package:chameleonultragui/sharedprefsprovider.dart'; import 'package:file_picker/file_picker.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:provider/provider.dart'; import 'package:uuid/uuid.dart'; import 'package:chameleonultragui/gui/menu/card_edit.dart'; @@ -688,13 +689,26 @@ class SavedCardsPageState extends State { width: 600, child: ListView( children: [ - Text(output), + Text( + output, + style: GoogleFonts.robotoMono( + fontSize: 16.0, + ), + ), ], ), ) ], ), actions: [ + IconButton( + onPressed: () async { + await dictMergeDialog( + context, dictionary); + Navigator.pop(context); + }, + icon: const Icon(Icons.merge), + ), IconButton( onPressed: () { showDialog( @@ -892,4 +906,136 @@ class SavedCardsPageState extends State { ), ); } + + Future dictMergeDialog(BuildContext context, Dictionary mergeDict) { + var appState = context.read(); + var dicts = appState.sharedPreferencesProvider.getDictionaries(); + + dicts.sort((a, b) => a.name.compareTo(b.name)); + + return showSearch( + context: context, + delegate: DictMergeDelegate(dicts, mergeDict), + ); + } +} + +class DictMergeDelegate extends SearchDelegate { + final List dicts; + final Dictionary mergeDict; + List selectedDicts = []; + + DictMergeDelegate(this.dicts, this.mergeDict) { + selectedDicts = List.filled(dicts.length, false); + } + + @override + List buildActions(BuildContext context) { + var appState = context.read(); + return [ + IconButton( + icon: const Icon(Icons.clear), + onPressed: () { + query = ''; + }, + ), + const SizedBox(width: 10), + IconButton( + icon: const Icon(Icons.merge), + onPressed: () { + List selectedForMerge = []; + List output = dicts; + + // Get selected dicts + for (var i = 0; i < selectedDicts.length; i++) { + if (selectedDicts[i]) { + selectedForMerge.add(dicts[i]); + } + } + + // Merge + for (var dict in selectedForMerge) { + mergeDict.keys = mergeDict.keys + dict.keys; + } + + // Deduplicate + mergeDict.keys = { + for (var key in mergeDict.keys) Object.hashAll(key): key + }.values.toList(); + + // Replace + for (var i = 0; i < output.length; i++) { + if (output[i].id == mergeDict.id) { + output[i] = mergeDict; + } + } + + appState.sharedPreferencesProvider.setDictionaries(output); + + Navigator.pop(context); + appState.changesMade(); + }, + ), + ]; + } + + @override + Widget buildLeading(BuildContext context) { + return IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () { + close(context, ''); + }, + ); + } + + @override + Widget buildResults(BuildContext context) { + final results = dicts + .where((dict) => dict.name.toLowerCase().contains(query.toLowerCase())); + + return ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, int index) { + final dict = results.elementAt(index); + if (dict.id == mergeDict.id) { + return Container(); + } + return CheckboxListTile( + value: selectedDicts[index], + title: Text(dict.name), + secondary: Icon(Icons.key, color: dict.color), + subtitle: Text("${dict.keys.length.toString()} keys"), + onChanged: (value) {}, + ); + }, + ); + } + + @override + Widget buildSuggestions(BuildContext context) { + final results = dicts + .where((dict) => dict.name.toLowerCase().contains(query.toLowerCase())); + + return ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, int index) { + final dict = results.elementAt(index); + var appState = context.read(); + if (dict.id == mergeDict.id) { + return Container(); + } + return CheckboxListTile( + value: selectedDicts[index], + title: Text(dict.name), + secondary: Icon(Icons.key, color: dict.color), + subtitle: Text("${dict.keys.length.toString()} keys"), + onChanged: (value) { + selectedDicts[index] = value!; + appState.changesMade(); + }, + ); + }, + ); + } } diff --git a/chameleonultragui/pubspec.lock b/chameleonultragui/pubspec.lock index 0a5c49ec..eb1bbfd6 100644 --- a/chameleonultragui/pubspec.lock +++ b/chameleonultragui/pubspec.lock @@ -253,6 +253,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + google_fonts: + dependency: "direct main" + description: + name: google_fonts + sha256: e20ff62b158b96f392bfc8afe29dee1503c94fbea2cbe8186fd59b756b8ae982 + url: "https://pub.dev" + source: hosted + version: "5.1.0" http: dependency: "direct main" description: diff --git a/chameleonultragui/pubspec.yaml b/chameleonultragui/pubspec.yaml index 14764d22..f16b5fae 100644 --- a/chameleonultragui/pubspec.yaml +++ b/chameleonultragui/pubspec.yaml @@ -66,6 +66,7 @@ dependencies: sdk: flutter intl: any wakelock_plus: ^1.1.1 + google_fonts: ^5.1.0 # crowdin_sdk: ^0.3.1 dev_dependencies: diff --git a/chameleonultragui/untranslated_messages.json b/chameleonultragui/untranslated_messages.json index 3d6c7129..5f198380 100644 --- a/chameleonultragui/untranslated_messages.json +++ b/chameleonultragui/untranslated_messages.json @@ -1,5 +1,33 @@ { "da": [ + "ok", + "slot", + "slots", + "system", + "indigo", + "debug_mode_confirmation", + "platform", + "android", + "firmware_is_corrupted", + "dfu", + "firmware_version", + "up_to_date", + "version", + "uid", + "sak", + "atqa", + "mini", + "hf", + "lf", + "mifare_classic_emulator_settings", + "mode_gen1a", + "mode_gen2", + "normal", + "recovery_error_no_supported", + "no_card_found", + "letter_space", + "tag_type", + "too_long_name", "save_recovered_keys", "save_recovered_keys_where", "save_recovered_keys_to_file", @@ -26,16 +54,22 @@ "recovery_in_progress" ], - "es": [ - "save_recovered_keys", - "save_recovered_keys_where", - "save_recovered_keys_to_file", - "add_recovered_keys_to_existing_dict", - "create_new_dict_with_recovered_keys", - "recovery_in_progress" - ], - - "fr": [ + "ko": [ + "slot", + "debug_mode_confirmation", + "firmware_is_corrupted", + "dfu", + "up_to_date", + "uid", + "sak", + "atqa", + "hf", + "lf", + "mifare_classic_emulator_settings", + "recovery_error_no_supported", + "no_card_found", + "letter_space", + "too_long_name", "save_recovered_keys", "save_recovered_keys_where", "save_recovered_keys_to_file", @@ -44,7 +78,33 @@ "recovery_in_progress" ], - "ko": [ + "ro": [ + "ok", + "card", + "slot", + "indigo", + "debug_mode_confirmation", + "android", + "dfu_flash_ultra", + "dfu_flash_lite", + "firmware_is_corrupted", + "dfu", + "up_to_date", + "auto", + "sector", + "uid", + "sak", + "atqa", + "mini", + "hf", + "lf", + "mifare_classic_emulator_settings", + "normal", + "deceive", + "shadow", + "recovery_error_no_supported", + "no_card_found", + "letter_space", "save_recovered_keys", "save_recovered_keys_where", "save_recovered_keys_to_file", @@ -54,6 +114,7 @@ ], "ru": [ + "too_long_name", "save_recovered_keys", "save_recovered_keys_where", "save_recovered_keys_to_file",