From c3690902c81e6e1d08bac2d245a8bd3f57b47a9d Mon Sep 17 00:00:00 2001 From: Juan Gilsanz Polo Date: Sun, 23 Oct 2022 19:27:23 +0200 Subject: [PATCH] Some improvements --- lib/l10n/app_en.arb | 4 +- lib/l10n/app_es.arb | 4 +- lib/providers/servers_provider.dart | 21 - .../encryption/custom_text_field.dart | 60 +++ .../settings/encryption/encryption.dart | 484 +++++------------- .../encryption/encryption_functions.dart | 106 ++++ .../settings/encryption/master_switch.dart | 69 +++ 7 files changed, 355 insertions(+), 393 deletions(-) create mode 100644 lib/screens/settings/encryption/custom_text_field.dart create mode 100644 lib/screens/settings/encryption/encryption_functions.dart create mode 100644 lib/screens/settings/encryption/master_switch.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 45ce2b2..fd2b10b 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -523,5 +523,7 @@ "dataNotValid": "Data not valid", "encryptionConfigSaved": "Encryption configuration saved successfully", "encryptionConfigNotSaved": "Encryption configuration could not be saved", - "configError": "Configuration error" + "configError": "Configuration error", + "enterOnlyCertificate": "Enter only the certificate. Do not input the ---BEGIN--- and ---END--- lines.", + "enterOnlyPrivateKey": "Enter only the key. Do not input the ---BEGIN--- and ---END--- lines." } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 7b0fb1c..97ab7d3 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -523,5 +523,7 @@ "dataNotValid": "Datos no válidos", "encryptionConfigSaved": "Configuración de cifrado guardada correctamente", "encryptionConfigNotSaved": "No se pudo guardar la configuración de encriptado", - "configError": "Configuration error" + "configError": "Configuration error", + "enterOnlyCertificate": "Introduce sólo el certificado. No introduzcas las líneas ---BEGIN--- y ---END---.", + "enterOnlyPrivateKey": "Introduce sólo la clave privada. No introduzcas las líneas ---BEGIN--- y ---END---." } \ No newline at end of file diff --git a/lib/providers/servers_provider.dart b/lib/providers/servers_provider.dart index 63dbccb..c309bea 100644 --- a/lib/providers/servers_provider.dart +++ b/lib/providers/servers_provider.dart @@ -50,11 +50,6 @@ class ServersProvider with ChangeNotifier { data: null ); - final Encryption _encryptionSettings = Encryption( - loadStatus: 0, // 0 = loading, 1 = loaded, 2 = error, - data: null - ); - FilteringStatus? _filteringStatus; List get serversList { @@ -97,10 +92,6 @@ class ServersProvider with ChangeNotifier { return _dnsInfo; } - Encryption get encryptionSettings { - return _encryptionSettings; - } - void setDbInstance(Database db) { _dbInstance = db; } @@ -211,18 +202,6 @@ class ServersProvider with ChangeNotifier { } } - void setEncryptionSettings(EncryptionData data) { - _encryptionSettings.data = data; - notifyListeners(); - } - - void setEncryptionSettingsLoadStatus(int status, bool notify) { - _encryptionSettings.loadStatus = status; - if (notify == true) { - notifyListeners(); - } - } - Future createServer(Server server) async { final saved = await saveServerIntoDb(server); if (saved == true) { diff --git a/lib/screens/settings/encryption/custom_text_field.dart b/lib/screens/settings/encryption/custom_text_field.dart new file mode 100644 index 0000000..3b1cf79 --- /dev/null +++ b/lib/screens/settings/encryption/custom_text_field.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +class EncryptionTextField extends StatelessWidget { + final bool enabled; + final TextEditingController controller; + final IconData icon; + final void Function(String) onChanged; + final String? errorText; + final String label; + final TextInputType? keyboardType; + final bool? multiline; + final String? helperText; + + const EncryptionTextField({ + Key? key, + required this.enabled, + required this.controller, + required this.icon, + required this.onChanged, + this.errorText, + required this.label, + this.keyboardType, + this.multiline, + this.helperText, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 200 + ), + child: TextFormField( + enabled: enabled, + controller: controller, + onChanged: onChanged, + decoration: InputDecoration( + prefixIcon: Icon(icon), + border: const OutlineInputBorder( + borderRadius: BorderRadius.all( + Radius.circular(10) + ) + ), + errorText: errorText, + labelText: label, + helperText: helperText, + helperMaxLines: 10, + helperStyle: TextStyle( + color: Theme.of(context).listTileTheme.iconColor + ) + ), + keyboardType: TextInputType.multiline, + maxLines: multiline == true ? null : 1, + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/settings/encryption/encryption.dart b/lib/screens/settings/encryption/encryption.dart index 1f49eac..1fe611e 100644 --- a/lib/screens/settings/encryption/encryption.dart +++ b/lib/screens/settings/encryption/encryption.dart @@ -6,6 +6,9 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:adguard_home_manager/screens/settings/section_label.dart'; import 'package:adguard_home_manager/screens/settings/encryption/config_error_modal.dart'; +import 'package:adguard_home_manager/screens/settings/encryption/custom_text_field.dart'; +import 'package:adguard_home_manager/screens/settings/encryption/master_switch.dart'; +import 'package:adguard_home_manager/screens/settings/encryption/encryption_functions.dart'; import 'package:adguard_home_manager/widgets/custom_switch_list_tile.dart'; import 'package:adguard_home_manager/classes/process_modal.dart'; @@ -44,6 +47,8 @@ class EncryptionSettingsWidget extends StatefulWidget { } class _EncryptionSettingsWidgetState extends State { + int loadStatus = 0; + bool enabled = false; final TextEditingController domainNameController = TextEditingController(); @@ -78,12 +83,12 @@ class _EncryptionSettingsWidgetState extends State { final TextEditingController pastePrivateKeyController = TextEditingController(); String? pastePrivateKeyError; - bool validData = false; + bool localValidationValid = false; String? validDataError; int dataValidApi = 0; void fetchData({bool? showRefreshIndicator}) async { - widget.serversProvider.setEncryptionSettingsLoadStatus(0, showRefreshIndicator ?? false); + setState(() => loadStatus = 0); final result = await getEncryptionSettings(server: widget.serversProvider.selectedServer!); @@ -115,120 +120,17 @@ class _EncryptionSettingsWidgetState extends State { privateKeyPathController.text = result['data'].privateKeyPath; } usePreviouslySavedKey = result['data'].privateKeySaved; - }); - widget.serversProvider.setEncryptionSettings(result['data']); - widget.serversProvider.setEncryptionSettingsLoadStatus(1, true); + loadStatus = 1; + }); } else { widget.appConfigProvider.addLog(result['log']); - widget.serversProvider.setEncryptionSettingsLoadStatus(2, true); + setState(() => loadStatus = 2); } } } - void validateDomain(String domain) { - RegExp regExp = RegExp(r'^([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+$'); - if (regExp.hasMatch(domain)) { - setState(() => domainError = null); - checkValidDataApi(); - } - else { - setState(() => domainError = AppLocalizations.of(context)!.domainNotValid); - } - checkDataValid(); - } - - void validatePort(String value, String portType) { - if (int.tryParse(value) != null && int.parse(value) <= 65535) { - setState(() { - switch (portType) { - case 'https': - setState(() => httpsPortError = null); - break; - - case 'tls': - setState(() => tlsPortError = null); - break; - - case 'quic': - setState(() => dnsOverQuicPortError = null); - break; - - default: - break; - } - }); - checkValidDataApi(); - } - else { - setState(() { - switch (portType) { - case 'https': - setState(() => httpsPortError = AppLocalizations.of(context)!.invalidPort); - break; - - case 'tls': - setState(() => tlsPortError = AppLocalizations.of(context)!.invalidPort); - break; - - case 'quic': - setState(() => dnsOverQuicPortError = AppLocalizations.of(context)!.invalidPort); - break; - - default: - break; - } - }); - } - checkDataValid(); - } - - void validateCertificate(String cert) { - final regExp = RegExp(r'(-{3,}(\bBEGIN CERTIFICATE\b))|(-{3,}-{3,}(\END CERTIFICATE\b)-{3,})', multiLine: true); - if (regExp.hasMatch(cert.replaceAll('\n', ''))) { - setState(() => certificateContentError = AppLocalizations.of(context)!.invalidCertificate); - } - else { - setState(() => certificateContentError = null); - } - checkDataValid(); - checkValidDataApi(); - } - - void validatePrivateKey(String cert) { - final regExp = RegExp(r'(-{3,}(\bBEGIN\b).*(PRIVATE KEY\b))|(-{3,}-{3,}(\bEND\b).*(PRIVATE KEY\b)-{3,})', multiLine: true); - if (regExp.hasMatch(cert.replaceAll('\n', ''))) { - setState(() => pastePrivateKeyError = AppLocalizations.of(context)!.invalidPrivateKey); - } - else { - setState(() => pastePrivateKeyError = null); - } - checkValidDataApi(); - } - - void validatePath(String cert, String item) { - final regExp = RegExp(r'^(\/{0,1}(?!\/))[A-Za-z0-9\/\-_]+(\.([a-zA-Z]+))?$'); - if (regExp.hasMatch(cert)) { - if (item == 'cert') { - setState(() => certificatePathError = null); - } - else if (item == 'private_key') { - setState(() => privateKeyPathError = null); - } - checkValidDataApi(); - } - else { - if (item == 'cert') { - setState(() => certificatePathError = AppLocalizations.of(context)!.invalidPath); - } - else if (item == 'private_key') { - setState(() => privateKeyPathError = AppLocalizations.of(context)!.invalidPath); - } - } - checkDataValid(); - } - Future checkValidDataApi({Map? data}) async { setState(() => dataValidApi = 0); @@ -295,11 +197,11 @@ class _EncryptionSettingsWidgetState extends State { pastePrivateKeyError == null )) ) { - setState(() => validData = true); + setState(() => localValidationValid = true); checkValidDataApi(); } else { - setState(() => validData = false); + setState(() => localValidationValid = false); } } @@ -355,50 +257,8 @@ class _EncryptionSettingsWidgetState extends State { } } - Widget generateStatus() { - if (dataValidApi == 0) { - return const SizedBox( - height: 25, - width: 25, - child: CircularProgressIndicator( - strokeWidth: 3, - ) - ); - } - else if (dataValidApi == 1) { - return const Icon( - Icons.check_circle_rounded, - color: Colors.green, - ); - } - else if (dataValidApi == 2) { - return const Icon( - Icons.cancel_rounded, - color: Colors.red, - ); - } - else { - return const SizedBox(); - } - } - - String generateStatusString() { - if (dataValidApi == 0) { - return AppLocalizations.of(context)!.validatingData; - } - else if (dataValidApi == 1) { - return AppLocalizations.of(context)!.dataValid; - } - else if (dataValidApi == 2) { - return AppLocalizations.of(context)!.dataNotValid; - } - else { - return ""; - } - } - Widget generateBody() { - switch (widget.serversProvider.encryptionSettings.loadStatus) { + switch (loadStatus) { case 0: return SizedBox( width: double.maxFinite, @@ -423,89 +283,25 @@ class _EncryptionSettingsWidgetState extends State { case 1: return ListView( children: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - left: 12, - right: 12 - ), - child: Material( - color: Theme.of(context).primaryColor.withOpacity(0.1), - borderRadius: BorderRadius.circular(28), - child: InkWell( - onTap: () { - setState(() => enabled = !enabled); - checkDataValid(); - checkValidDataApi(); - }, - borderRadius: BorderRadius.circular(28), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 12 - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - AppLocalizations.of(context)!.enableEncryption, - style: const TextStyle( - fontSize: 18, - ), - ), - const SizedBox(height: 3), - Text( - AppLocalizations.of(context)!.enableEncryptionTypes, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).listTileTheme.iconColor, - ), - ) - ], - ), - ), - Switch( - value: enabled, - onChanged: (value) { - setState(() => enabled = value); - checkDataValid(); - checkValidDataApi(); - }, - activeColor: Theme.of(context).primaryColor, - ), - ], - ), - ), - ), - ), + EncryptionMasterSwitch( + value: enabled, + onChange: (value) { + setState(() => enabled = value); + checkDataValid(); + } ), SectionLabel(label: AppLocalizations.of(context)!.serverConfiguration), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: domainNameController, - onChanged: validateDomain, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.link_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: domainError, - labelText: AppLocalizations.of(context)!.domainName, - helperText: AppLocalizations.of(context)!.domainNameDescription, - helperStyle: TextStyle( - color: Theme.of(context).listTileTheme.iconColor - ), - helperMaxLines: 10 - ), - ), + EncryptionTextField( + enabled: enabled, + controller: domainNameController, + icon: Icons.link_rounded, + onChanged: (value) { + setState(() => domainError = validateDomain(context, value)); + checkDataValid(); + }, + errorText: domainError, + label: AppLocalizations.of(context)!.domainName, + helperText: AppLocalizations.of(context)!.domainNameDescription, ), const SizedBox(height: 10), CustomSwitchListTile( @@ -513,70 +309,48 @@ class _EncryptionSettingsWidgetState extends State { onChanged: (value) { setState(() => redirectHttps = value); checkDataValid(); - checkValidDataApi(); }, title: AppLocalizations.of(context)!.redirectHttps, disabled: !enabled, ), const SizedBox(height: 10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: httpsPortController, - onChanged: (value) => validatePort(value, 'https'), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.numbers_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: httpsPortError, - labelText: AppLocalizations.of(context)!.httpsPort, - ), - keyboardType: TextInputType.number, - ), + EncryptionTextField( + enabled: enabled, + controller: httpsPortController, + icon: Icons.numbers_rounded, + onChanged: (value) { + setState(() => httpsPortError = validatePort(context, value)); + checkDataValid(); + }, + errorText: httpsPortError, + label: AppLocalizations.of(context)!.httpsPort, + keyboardType: TextInputType.number, ), const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: tlsPortController, - onChanged: (value) => validatePort(value, 'tls'), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.numbers_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: tlsPortError, - labelText: AppLocalizations.of(context)!.tlsPort, - ), - keyboardType: TextInputType.number, - ), + EncryptionTextField( + enabled: enabled, + controller: tlsPortController, + icon: Icons.numbers_rounded, + onChanged: (value) { + setState(() => tlsPortError = validatePort(context, value)); + checkDataValid(); + }, + errorText: tlsPortError, + label: AppLocalizations.of(context)!.tlsPort, + keyboardType: TextInputType.number, ), const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: dnsOverQuicPortController, - onChanged: (value) => validatePort(value, 'quic'), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.numbers_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: dnsOverQuicPortError, - labelText: AppLocalizations.of(context)!.dnsOverQuicPort, - ), - keyboardType: TextInputType.number, - ), + EncryptionTextField( + enabled: enabled, + controller: dnsOverQuicPortController, + icon: Icons.numbers_rounded, + onChanged: (value) { + setState(() => dnsOverQuicPortError = validatePort(context, value)); + checkDataValid(); + }, + errorText: dnsOverQuicPortError, + label: AppLocalizations.of(context)!.dnsOverQuicPort, + keyboardType: TextInputType.number, ), SectionLabel(label: AppLocalizations.of(context)!.certificates), Card( @@ -602,7 +376,6 @@ class _EncryptionSettingsWidgetState extends State { ? (value) { setState(() => certificateOption = int.parse(value.toString())); checkDataValid(); - checkValidDataApi(); } : null, title: Text( @@ -619,7 +392,6 @@ class _EncryptionSettingsWidgetState extends State { ? (value) { setState(() => certificateOption = int.parse(value.toString())); checkDataValid(); - checkValidDataApi(); } : null, title: Text( @@ -630,43 +402,30 @@ class _EncryptionSettingsWidgetState extends State { ), ), const SizedBox(height: 10), - if (certificateOption == 0) Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: certificatePathController, - onChanged: (value) => validatePath(value, 'cert'), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.description_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: certificatePathError, - labelText: AppLocalizations.of(context)!.certificatePath, - ), - ), + if (certificateOption == 0) EncryptionTextField( + enabled: enabled, + controller: certificatePathController, + icon: Icons.description_rounded, + onChanged: (value) { + setState(() => certificatePathError = validatePath(context, value)); + checkDataValid(); + }, + label: AppLocalizations.of(context)!.certificatePath, + errorText: certificatePathError, ), - if (certificateOption == 1) Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: certificateContentController, - onChanged: validateCertificate, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.description_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: certificateContentError, - labelText: AppLocalizations.of(context)!.certificateContent, - ), - keyboardType: TextInputType.multiline, - maxLines: null, - ), + if (certificateOption == 1) EncryptionTextField( + enabled: enabled, + controller: certificateContentController, + icon: Icons.description_rounded, + onChanged: (value) { + setState(() => certificateContentError = validateCertificate(context, value)); + checkDataValid(); + }, + label: AppLocalizations.of(context)!.certificateContent, + helperText: AppLocalizations.of(context)!.enterOnlyCertificate, + errorText: certificateContentError, + multiline: true, + keyboardType: TextInputType.multiline, ), SectionLabel(label: AppLocalizations.of(context)!.privateKey), RadioListTile( @@ -676,7 +435,6 @@ class _EncryptionSettingsWidgetState extends State { ? (value) { setState(() => privateKeyOption = int.parse(value.toString())); checkDataValid(); - checkValidDataApi(); } : null, title: Text( @@ -693,7 +451,6 @@ class _EncryptionSettingsWidgetState extends State { ? (value) { setState(() => privateKeyOption = int.parse(value.toString())); checkDataValid(); - checkValidDataApi(); } : null, title: Text( @@ -712,45 +469,32 @@ class _EncryptionSettingsWidgetState extends State { ), const SizedBox(height: 10) ], - if (privateKeyOption == 0) Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled, - controller: privateKeyPathController, - onChanged: (value) => validatePath(value, 'private_key'), - decoration: InputDecoration( - prefixIcon: const Icon(Icons.description_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: privateKeyPathError, - labelText: AppLocalizations.of(context)!.privateKeyPath, - ), - ), + if (privateKeyOption == 0) EncryptionTextField( + enabled: enabled, + controller: privateKeyPathController, + icon: Icons.description_rounded, + onChanged: (value) { + setState(() => privateKeyPathError = validatePath(context, value)); + checkDataValid(); + }, + label: AppLocalizations.of(context)!.privateKeyPath, + errorText: privateKeyPathError, ), - if (privateKeyOption == 1) Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - enabled: enabled == true - ? !usePreviouslySavedKey - : false, - controller: pastePrivateKeyController, - onChanged: validatePrivateKey, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.description_rounded), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(10) - ) - ), - errorText: pastePrivateKeyError, - labelText: AppLocalizations.of(context)!.pastePrivateKey, - ), - keyboardType: TextInputType.multiline, - maxLines: null, - ), + if (privateKeyOption == 1) EncryptionTextField( + enabled: enabled == true + ? !usePreviouslySavedKey + : false, + controller: pastePrivateKeyController, + icon: Icons.description_rounded, + onChanged: (value) { + setState(() => pastePrivateKeyError = validatePrivateKey(context, value)); + checkDataValid(); + }, + label: AppLocalizations.of(context)!.pastePrivateKey, + helperText: AppLocalizations.of(context)!.enterOnlyPrivateKey, + errorText: pastePrivateKeyError, + keyboardType: TextInputType.multiline, + multiline: true, ), const SizedBox(height: 20), ], @@ -798,11 +542,11 @@ class _EncryptionSettingsWidgetState extends State { builder: (context) => EncryptionErrorModal(error: validDataError!) ) } : null, - icon: generateStatus(), - tooltip: generateStatusString() + icon: generateStatus(localValidationValid, dataValidApi), + tooltip: generateStatusString(context, localValidationValid, dataValidApi) ), IconButton( - onPressed: validData == true && dataValidApi == 1 + onPressed: localValidationValid == true && dataValidApi == 1 ? () => saveData() : null, icon: const Icon(Icons.save), diff --git a/lib/screens/settings/encryption/encryption_functions.dart b/lib/screens/settings/encryption/encryption_functions.dart new file mode 100644 index 0000000..3640fdd --- /dev/null +++ b/lib/screens/settings/encryption/encryption_functions.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +String? validateDomain(BuildContext context, String domain) { + RegExp regExp = RegExp(r'^([a-z0-9|-]+\.)*[a-z0-9|-]+\.[a-z]+$'); + if (regExp.hasMatch(domain)) { + return null; + } + else { + return AppLocalizations.of(context)!.domainNotValid; + } +} + +String? validatePort(BuildContext context, String value) { + if (int.tryParse(value) != null && int.parse(value) <= 65535) { + return null; + } + else { + return AppLocalizations.of(context)!.invalidPort; + } +} + +String? validateCertificate(BuildContext context, String cert) { + final regExp = RegExp(r'(-{3,}(\bBEGIN CERTIFICATE\b))|(-{3,}-{3,}(\END CERTIFICATE\b)-{3,})', multiLine: true); + if (regExp.hasMatch(cert.replaceAll('\n', ''))) { + return AppLocalizations.of(context)!.invalidCertificate; + } + else { + return null; + } +} + +String? validatePrivateKey(BuildContext context, String cert) { + final regExp = RegExp(r'(-{3,}(\bBEGIN\b).*(PRIVATE KEY\b))|(-{3,}-{3,}(\bEND\b).*(PRIVATE KEY\b)-{3,})', multiLine: true); + if (regExp.hasMatch(cert.replaceAll('\n', ''))) { + return AppLocalizations.of(context)!.invalidPrivateKey; + } + else { + return null; + } +} + +String? validatePath(BuildContext context, String cert) { + final regExp = RegExp(r'^(\/{0,1}(?!\/))[A-Za-z0-9\/\-_]+(\.([a-zA-Z]+))?$'); + if (regExp.hasMatch(cert)) { + return null; + } + else { + return AppLocalizations.of(context)!.invalidPath; + } +} + +Widget generateStatus(bool localValidation, int dataValidApi) { + if (localValidation == true) { + if (dataValidApi == 0) { + return const SizedBox( + height: 25, + width: 25, + child: CircularProgressIndicator( + strokeWidth: 3, + ) + ); + } + else if (dataValidApi == 1) { + return const Icon( + Icons.check_circle_rounded, + color: Colors.green, + ); + } + else if (dataValidApi == 2) { + return const Icon( + Icons.cancel_rounded, + color: Colors.red, + ); + } + else { + return const SizedBox(); + } + } + else { + return const Icon( + Icons.error_rounded, + color: Colors.grey, + ); + } +} + +String generateStatusString(BuildContext context, bool localValidation, int dataValidApi) { + if (localValidation == true) { + if (dataValidApi == 0) { + return AppLocalizations.of(context)!.validatingData; + } + else if (dataValidApi == 1) { + return AppLocalizations.of(context)!.dataValid; + } + else if (dataValidApi == 2) { + return AppLocalizations.of(context)!.dataNotValid; + } + else { + return ""; + } + } + else { + return AppLocalizations.of(context)!.dataNotValid; + } +} \ No newline at end of file diff --git a/lib/screens/settings/encryption/master_switch.dart b/lib/screens/settings/encryption/master_switch.dart new file mode 100644 index 0000000..d7e9d90 --- /dev/null +++ b/lib/screens/settings/encryption/master_switch.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class EncryptionMasterSwitch extends StatelessWidget { + final bool value; + final void Function(bool) onChange; + + const EncryptionMasterSwitch({ + Key? key, + required this.value, + required this.onChange + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only( + top: 10, + left: 12, + right: 12 + ), + child: Material( + color: Theme.of(context).primaryColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(28), + child: InkWell( + onTap: () => onChange(!value), + borderRadius: BorderRadius.circular(28), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 12 + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.of(context)!.enableEncryption, + style: const TextStyle( + fontSize: 18, + ), + ), + const SizedBox(height: 3), + Text( + AppLocalizations.of(context)!.enableEncryptionTypes, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).listTileTheme.iconColor, + ), + ) + ], + ), + ), + Switch( + value: value, + onChanged: (value) => onChange(value), + activeColor: Theme.of(context).primaryColor, + ), + ], + ), + ), + ), + ), + ); + } +} \ No newline at end of file