diff --git a/debugInfo/aab/aab.zip b/debugInfo/aab/aab.zip new file mode 100644 index 0000000..b62d828 Binary files /dev/null and b/debugInfo/aab/aab.zip differ diff --git a/debugInfo/aab/arm.symbols b/debugInfo/aab/arm.symbols new file mode 100644 index 0000000..1d762c9 Binary files /dev/null and b/debugInfo/aab/arm.symbols differ diff --git a/debugInfo/aab/arm64.symbols b/debugInfo/aab/arm64.symbols new file mode 100644 index 0000000..12fb2d1 Binary files /dev/null and b/debugInfo/aab/arm64.symbols differ diff --git a/debugInfo/aab/x64.symbols b/debugInfo/aab/x64.symbols new file mode 100644 index 0000000..fcd7eee Binary files /dev/null and b/debugInfo/aab/x64.symbols differ diff --git a/lib/models/export.dart b/lib/models/export.dart new file mode 100644 index 0000000..ee40f1d --- /dev/null +++ b/lib/models/export.dart @@ -0,0 +1,4 @@ +enum ExportType { + SHARE_UNENCRYPTED, + SAVE_TO_STORAGE_UNENCRYPTED, +} diff --git a/lib/redux/appstate.g.dart b/lib/redux/appstate.g.dart index 30642a6..75c784b 100644 --- a/lib/redux/appstate.g.dart +++ b/lib/redux/appstate.g.dart @@ -30,7 +30,8 @@ abstract class $AppState { String toString() => "AppState(entries: $entries, isSyncing: $isSyncing, autofillLaunch: $autofillLaunch, isLoggedIn: $isLoggedIn)"; @override - bool operator ==(dynamic other) => + bool operator ==(Object other) => + other is AppState && other.runtimeType == runtimeType && entries == other.entries && isSyncing == other.isSyncing && diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index 8f5f677..d6cd7fc 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -4,6 +4,7 @@ import 'package:autofill_service/autofill_service.dart'; import 'package:ez_localization/ez_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_icons/flutter_icons.dart'; +import 'package:passwd/widgets/export.dart'; import '../../utils/loggers.dart'; import '../../widgets/sync/settings_sync_widget.dart'; @@ -17,8 +18,9 @@ class _SettingsScreenState extends State { final settingsItems = [ if (Platform.isAndroid) ListTile( - title: Text('Activate autofill service'), + title: Text('Activate autofill service'), // TODO: localize onTap: () async { + // TODO: abstract autofill final response = await AutofillService().requestSetAutofillService(); Loggers.mainLogger.info( 'Autofill requestSetAutofillService: ${response}', @@ -26,6 +28,7 @@ class _SettingsScreenState extends State { }, ), SettingsSyncWidget(), + ExportSettingsWidget(), ]; @override diff --git a/lib/services/export/export_impl.dart b/lib/services/export/export_impl.dart new file mode 100644 index 0000000..212d540 --- /dev/null +++ b/lib/services/export/export_impl.dart @@ -0,0 +1,48 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:injectable/injectable.dart'; +import 'package:path/path.dart'; +import 'package:share/share.dart'; + +import '../../models/export.dart'; +import '../database/database_service.dart'; +import '../locator.dart'; +import '../path/path_service.dart'; +import 'export_service.dart'; + +@Injectable(as: ExportService) +class ExportImpl implements ExportService { + final pathService = locator(); + final databaseService = locator(); + + @override + Future export(ExportType type) async { + switch (type) { + case ExportType.SHARE_UNENCRYPTED: + await share_unencrypted(); + break; + + case ExportType.SAVE_TO_STORAGE_UNENCRYPTED: + throw UnimplementedError(); + } + } + + Future share_unencrypted() async { + if (!Platform.isAndroid && !Platform.isIOS) { + throw UnsupportedError('Share is only available on mobile devices'); + } + + final tempDir = await pathService.getTempDir(); + final tempDbPath = join(tempDir.path, 'passwd_db.json'); + final file = File(tempDbPath); + + await file.writeAsString(json.encode(databaseService.entries)); + await Share.shareFiles( + [tempDbPath], + mimeTypes: ['text/json', 'application/json'], + ); + + await file.delete(); + } +} diff --git a/lib/services/export/export_service.dart b/lib/services/export/export_service.dart new file mode 100644 index 0000000..c91aefa --- /dev/null +++ b/lib/services/export/export_service.dart @@ -0,0 +1,5 @@ +import '../../models/export.dart'; + +abstract class ExportService { + Future export(ExportType type); +} diff --git a/lib/services/locator.config.dart b/lib/services/locator.config.dart index 93b47a9..ac353c9 100644 --- a/lib/services/locator.config.dart +++ b/lib/services/locator.config.dart @@ -27,6 +27,8 @@ import 'crypto/crypto_service.dart'; import 'database/database_impl.dart'; import 'database/database_service.dart'; import 'dio.dart'; +import 'export/export_impl.dart'; +import 'export/export_service.dart'; import 'favicon/favicon_new.dart'; import 'favicon/favicon_service.dart'; import 'migration/migration_impl.dart'; @@ -68,6 +70,7 @@ GetIt $initGetIt( gh.lazySingleton(() => CryptoCrypt()); gh.lazySingleton(() => DatabaseImpl()); gh.lazySingleton(() => dioModule.dio); + gh.factory(() => ExportImpl()); gh.lazySingleton(() => FaviconNew()); gh.lazySingleton(() => MigrationImpl()); gh.lazySingleton(() => PasswordImpl()); diff --git a/lib/services/path/path_path_provider.dart b/lib/services/path/path_path_provider.dart index 3dd3d05..d89dfb5 100644 --- a/lib/services/path/path_path_provider.dart +++ b/lib/services/path/path_path_provider.dart @@ -14,6 +14,11 @@ class PathPathProvider implements PathService { return (await getApplicationDocumentsDirectory()); } + @override + Future getTempDir() async { + return (await getTemporaryDirectory()); + } + @override Future checkCacheDir() async { if (!Platform.isAndroid || !Platform.isIOS) { diff --git a/lib/services/path/path_service.dart b/lib/services/path/path_service.dart index 59198b6..e8fe252 100644 --- a/lib/services/path/path_service.dart +++ b/lib/services/path/path_service.dart @@ -3,4 +3,5 @@ import 'dart:io'; abstract class PathService { Future getDocDir(); Future checkCacheDir(); + Future getTempDir(); } diff --git a/lib/widgets/export.dart b/lib/widgets/export.dart new file mode 100644 index 0000000..8bf662a --- /dev/null +++ b/lib/widgets/export.dart @@ -0,0 +1,58 @@ +import 'dart:io'; + +import 'package:ez_localization/ez_localization.dart'; +import 'package:flutter/material.dart'; + +import '../models/export.dart'; +import '../services/export/export_service.dart'; +import '../services/locator.dart'; +import '../utils/loggers.dart'; + +class ExportSettingsWidget extends StatelessWidget { + Future export(BuildContext context) async { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Are you sure?'), // TODO: localize + content: + Text('The database export will be unencrypted'), // TODO: localize + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(context.getString('no')), + ), + TextButton( + onPressed: () async { + Loggers.mainLogger.info( + 'Export Type SHARE_UNENCRYPTED', + ); + Navigator.of(context).pop(); + await locator() + .export(ExportType.SHARE_UNENCRYPTED); + }, + child: Text(context.getString('yes')), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + if (Platform.isAndroid || Platform.isIOS) { + return ListTile( + title: Text('Export'), // TODO: localize + onTap: () async { + Loggers.mainLogger.info( + 'Export exportService requested', + ); + await export(context); + }, + ); + } + + return Container(); + } +} diff --git a/pubspec.lock b/pubspec.lock index 6305dc5..43ecb53 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "12.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.39.17" + version: "0.40.6" animations: dependency: "direct main" description: @@ -114,14 +114,14 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.6.2" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.5" build_daemon: dependency: transitive description: @@ -135,21 +135,21 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.3.11" + version: "1.5.3" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.10.2" + version: "1.11.1" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.0.1" + version: "6.1.7" built_collection: dependency: transitive description: @@ -241,13 +241,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.4.1" - csslib: - dependency: transitive - description: - name: csslib - url: "https://pub.dartlang.org" - source: hosted - version: "0.16.2" dart_otp: dependency: "direct main" description: @@ -261,7 +254,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.3.6" + version: "1.3.10" desktop_window: dependency: "direct main" description: @@ -393,7 +386,7 @@ packages: name: functional_data_generator url: "https://pub.dartlang.org" source: hosted - version: "0.3.4" + version: "0.3.7" get_it: dependency: "direct main" description: @@ -422,13 +415,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.0" - html: - dependency: transitive - description: - name: html - url: "https://pub.dartlang.org" - source: hosted - version: "0.14.0+4" http: dependency: transitive description: @@ -470,7 +456,7 @@ packages: name: injectable_generator url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.0.6" intl: dependency: transitive description: @@ -744,6 +730,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.24.1" + share: + dependency: "direct main" + description: + name: share + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.5+4" shared_preferences: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4b80211..90e2b06 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: provider: ^4.3.2+2 provider_for_redux: ^1.1.3 responsive_builder: ^0.3.0 + share: ^0.6.5+4 shared_preferences: ^0.5.12+4 supercharged: ^1.11.1 touch_bar: ^0.0.1-alpha.1 @@ -55,7 +56,7 @@ dev_dependencies: sdk: flutter build_runner: ^1.10.2 flutter_launcher_icons: ^0.8.1 - functional_data_generator: ^0.3.4 + functional_data_generator: ^0.3.7 injectable_generator: ^1.0.5 pedantic: ^1.9.2