From 7187552d930de83c1b5e6e0da62876e019e2539b Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Sat, 30 Dec 2023 15:44:38 +0530 Subject: [PATCH 1/7] Remove launch_review package --- lib/ui/landing_page/main_overflow_menu.dart | 8 ++- pubspec.lock | 58 ++++++++++----------- pubspec.yaml | 1 - 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/lib/ui/landing_page/main_overflow_menu.dart b/lib/ui/landing_page/main_overflow_menu.dart index 6ad8290b..930313e1 100644 --- a/lib/ui/landing_page/main_overflow_menu.dart +++ b/lib/ui/landing_page/main_overflow_menu.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:launch_review/launch_review.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:paintroid/io/src/ui/about_dialog.dart'; import 'package:paintroid/ui/pop_menu_button.dart'; @@ -47,7 +47,11 @@ class _MainOverFlowMenuState extends ConsumerState { String version = packageInfo.version; switch (option) { case MainOverflowMenuOption.rate: - LaunchReview.launch(androidAppId: androidAppId, iOSAppId: iOSAppId); + const MethodChannel channel = MethodChannel('launch_review'); + await channel.invokeMethod('launch', { + 'android_id': androidAppId, + 'ios_id': iOSAppId, + }); break; case MainOverflowMenuOption.help: if (mounted) { diff --git a/pubspec.lock b/pubspec.lock index 9811a1c3..cfe65726 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" convert: dependency: transitive description: @@ -388,10 +388,10 @@ packages: dependency: "direct main" description: name: flutter_riverpod - sha256: b83ac5827baadefd331ea1d85110f34645827ea234ccabf53a655f41901a9bf4 + sha256: da9591d1f8d5881628ccd5c25c40e74fc3eef50ba45e40c3905a06e1712412d5 url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.4.9" flutter_svg: dependency: "direct main" description: @@ -544,10 +544,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -572,14 +572,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" - launch_review: - dependency: "direct main" - description: - name: launch_review - sha256: "04cdaf752033cefd53bc0fa9c22105801ef53791a93d8b6cdd00fcb3c1c1604b" - url: "https://pub.dev" - source: hosted - version: "3.0.1" lints: dependency: transitive description: @@ -608,18 +600,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: transitive description: @@ -640,10 +632,10 @@ packages: dependency: "direct dev" description: name: mockito - sha256: dd61809f04da1838a680926de50a9e87385c1de91c6579629c3d1723946e8059 + sha256: "8b46d7eb40abdda92d62edd01546051f0c27365e65608c284de336dccfef88cc" url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "5.4.1" oxidized: dependency: "direct main" description: @@ -856,10 +848,10 @@ packages: dependency: "direct dev" description: name: riverpod - sha256: "80e48bebc83010d5e67a11c9514af6b44bbac1ec77b4333c8ea65dbc79e2d8ef" + sha256: "942999ee48b899f8a46a860f1e13cee36f2f77609eb54c5b7a669bb20d550b11" url: "https://pub.dev" source: hosted - version: "2.3.6" + version: "2.4.9" riverpod_analyzer_utils: dependency: transitive description: @@ -997,10 +989,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sqflite: dependency: "direct main" description: @@ -1117,10 +1109,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" timing: dependency: transitive description: @@ -1237,10 +1229,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "11.7.1" watcher: dependency: transitive description: @@ -1249,6 +1241,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1306,5 +1306,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0-0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.3.0" diff --git a/pubspec.yaml b/pubspec.yaml index a5e01859..4334dc2d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,7 +34,6 @@ dependencies: url_launcher: ^6.1.5 package_info_plus: ^4.0.1 device_info_plus: ^9.0.3 - launch_review: ^3.0.1 smooth_page_indicator: ^1.0.0+2 shared_preferences: ^2.0.15 From 00a5ef168bbcef97516fa6e3ae363561bef022e5 Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Sat, 30 Dec 2023 16:18:55 +0530 Subject: [PATCH 2/7] Remove launch_review package --- lib/ui/landing_page/main_overflow_menu.dart | 23 +++++++++---- pubspec.lock | 36 ++++++++++----------- pubspec.yaml | 2 +- 3 files changed, 36 insertions(+), 25 deletions(-) diff --git a/lib/ui/landing_page/main_overflow_menu.dart b/lib/ui/landing_page/main_overflow_menu.dart index 930313e1..56265478 100644 --- a/lib/ui/landing_page/main_overflow_menu.dart +++ b/lib/ui/landing_page/main_overflow_menu.dart @@ -1,11 +1,13 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:paintroid/io/src/ui/about_dialog.dart'; import 'package:paintroid/ui/pop_menu_button.dart'; import 'package:paintroid/ui/styles.dart'; import 'package:paintroid/ui/util.dart'; +import 'package:url_launcher/url_launcher.dart'; enum MainOverflowMenuOption { rate('Rate us!'), @@ -47,11 +49,20 @@ class _MainOverFlowMenuState extends ConsumerState { String version = packageInfo.version; switch (option) { case MainOverflowMenuOption.rate: - const MethodChannel channel = MethodChannel('launch_review'); - await channel.invokeMethod('launch', { - 'android_id': androidAppId, - 'ios_id': iOSAppId, - }); + if (Platform.isAndroid || Platform.isIOS) { + final appId = Platform.isAndroid + ? androidAppId + : iOSAppId; + final url = Uri.parse( + Platform.isAndroid + ? 'market://details?id=$appId' + : 'https://apps.apple.com/app/id$appId', + ); + launchUrl( + url, + mode: LaunchMode.externalApplication, + ); + } break; case MainOverflowMenuOption.help: if (mounted) { diff --git a/pubspec.lock b/pubspec.lock index cfe65726..b9ed3f0d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1149,66 +1149,66 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3 + sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 url: "https://pub.dev" source: hosted - version: "6.1.11" + version: "6.2.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "1a5848f598acc5b7d8f7c18b8cb834ab667e59a13edc3c93e9d09cf38cc6bc87" + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.0.34" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "6.2.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e" + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab" + sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.2.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.1" uuid: dependency: transitive description: @@ -1306,5 +1306,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.3.0" + dart: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4334dc2d..d6b6f555 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: toast: ^0.3.0 sqflite: permission_handler: ^10.0.0 - url_launcher: ^6.1.5 + url_launcher: ^6.2.2 package_info_plus: ^4.0.1 device_info_plus: ^9.0.3 smooth_page_indicator: ^1.0.0+2 From 504ecad5225f19819f16694bbffb1c6b630e350c Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Fri, 23 Feb 2024 23:40:51 +0530 Subject: [PATCH 3/7] resolve conflicts --- .fvm/fvm_config.json | 4 + .github/workflows/main.yml | 27 +- .gitignore | 22 +- Makefile | 60 +- README.md | 108 +- analysis_options.yaml | 5 +- generate_files.sh | 1 - generate_protos.sh | 3 - ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile | 2 +- ios/Podfile.lock | 76 +- ios/Runner.xcodeproj/project.pbxproj | 11 +- ios/Runner/Info.plist | 2 + l10n.yaml | 3 - lib/command/command.dart | 6 - lib/command/src/command.dart | 5 - .../command/graphic/draw_path_command.dart | 19 - lib/core/app_localizations.dart | 10 - lib/core/path_with_action_history.dart | 45 - lib/io/io.dart | 18 - lib/io/serialization.dart | 10 - lib/io/src/entity/catrobat_image.dart | 22 - lib/io/src/serialization/proto/protos.dart | 4 - .../proto/schema/catrobat_image.proto | 12 - .../command/graphic/draw_path_command.proto | 9 - .../proto/schema/graphic/paint.proto | 34 - .../proto/schema/graphic/path.proto | 27 - .../proto_serializer_with_versioning.dart | 22 - .../serializer/catrobat_image_serializer.dart | 78 -- .../graphic/draw_path_command_serializer.dart | 49 - .../serializer/graphic/paint_serializer.dart | 69 - .../serializer/graphic/path_serializer.dart | 84 -- .../src/serialization/version_serializer.dart | 27 - lib/main.dart | 62 +- lib/pocket_paint_app.dart | 66 + lib/ui/drawing_space/bottom_nav_bar.dart | 73 -- lib/ui/drawing_space/tool_options.dart | 21 - lib/workspace/workspace.dart | 4 - melos.yaml | 97 ++ packages/command/.metadata | 10 + packages/command/analysis_options.yaml | 23 + packages/command/lib/command.dart | 10 + packages/command/lib/command_providers.dart | 5 + .../lib/graphic_factory}/graphic_factory.dart | 2 +- .../graphic_factory_provider.dart | 2 +- .../graphic_factory_provider.g.dart | 25 + .../src/command_factory}/command_factory.dart | 3 +- .../command_factory_provider.dart | 2 +- .../command_factory_provider.g.dart | 25 + .../src/command_implementation/command.dart | 19 + .../graphic/draw_path_command.dart | 51 + .../graphic/draw_path_command.g.dart | 24 + .../graphic}/graphic_command.dart | 4 +- .../src/command_manager}/command_manager.dart | 3 +- .../command_manager_provider.dart | 3 +- .../command_manager_provider.g.dart | 25 + .../sync_command_manager.dart | 4 +- .../lib/utils/path_with_action_history.dart | 92 ++ packages/command/pubspec.yaml | 42 + .../test/unit}/command_factory_test.dart | 3 +- .../test/unit}/draw_path_command_test.dart | 3 +- .../unit/draw_path_command_test.mocks.dart | 616 +++++++++ packages/component_library/.metadata | 10 + .../component_library/analysis_options.yaml | 23 + .../assets}/img/checkerboard.png | Bin .../img}/pocketpaint_intro_landscape.png | Bin .../img}/pocketpaint_intro_portrait.png | Bin .../assets/img}/pocketpaint_logo_small.png | Bin .../assets}/svg/ic_brush.svg | 0 .../assets}/svg/ic_clipboard.svg | 0 .../assets}/svg/ic_clipping.svg | 0 .../assets}/svg/ic_cursor.svg | 0 .../assets}/svg/ic_edit_circle.svg | 0 .../assets}/svg/ic_eraser.svg | 0 .../component_library/assets}/svg/ic_fill.svg | 0 .../component_library/assets}/svg/ic_hand.svg | 0 .../assets}/svg/ic_import.svg | 0 .../assets}/svg/ic_layers.svg | 0 .../component_library/assets}/svg/ic_line.svg | 0 .../assets}/svg/ic_pipette.svg | 0 .../assets}/svg/ic_shapes.svg | 0 .../assets}/svg/ic_smudge.svg | 0 .../assets}/svg/ic_spray_can.svg | 0 .../assets}/svg/ic_stamp.svg | 0 .../component_library/assets}/svg/ic_text.svg | 0 .../assets}/svg/ic_tools.svg | 0 .../assets}/svg/ic_transform.svg | 0 .../assets}/svg/ic_watercolor.svg | 0 .../lib/component_library.dart | 13 + .../src/components}/bottom_nav_bar_icon.dart | 9 +- .../src/components}/custom_action_chip.dart | 3 + .../components}/icon_button_with_label.dart | 0 .../lib/src/components/icon_svg.dart | 27 + .../lib/src/components/imgs.dart | 59 + .../lib/src/components}/loading_overlay.dart | 0 .../lib/src/components}/pop_menu_button.dart | 0 .../lib/src/theme}/color_schemes.dart | 0 .../lib/src/theme}/styles.dart | 2 +- .../lib/src/utils/open_url.dart | 0 .../lib/src/utils}/toast_utils.dart | 0 packages/component_library/pubspec.yaml | 43 + packages/database/.metadata | 10 + packages/database/analysis_options.yaml | 24 + packages/database/lib/database.dart | 8 + .../database/lib/src/models}/project.dart | 0 .../database/lib/src}/project_dao.dart | 2 +- .../database/lib/src}/project_database.dart | 7 +- .../database/lib/src/project_database.g.dart | 204 +++ .../lib/src/utils}/date_time_converter.dart | 0 packages/database/pubspec.yaml | 35 + .../test/unit}/project_database_test.dart | 4 +- .../features/landing_page_screen/.metadata | 10 + .../landing_page_screen/analysis_options.yaml | 23 + .../lib/landing_page_screen.dart | 9 + .../src/components}/custom_action_button.dart | 3 + .../lib/src/components}/image_preview.dart | 6 +- .../src/components}/main_overflow_menu.dart | 10 +- .../src/components}/project_list_tile.dart | 7 +- .../components}/project_overflow_menu.dart | 8 +- .../lib/src}/landing_page.dart | 29 +- .../features/landing_page_screen/pubspec.yaml | 56 + .../test}/fixture/image/test.jpg | Bin .../test}/fixture/image/test.png | Bin .../test/fixture/image/test1.png | Bin 0 -> 1272 bytes .../test/widget}/landing_page_test.dart | 17 +- .../test/widget/landing_page_test.mocks.dart | 412 ++++++ packages/features/onboarding_screen/.metadata | 10 + .../onboarding_screen/analysis_options.yaml | 23 + .../lib/onboarding_screen.dart | 13 + .../components}/bottom_nav_bar_container.dart | 4 +- .../components}/onboarding_page_app_bar.dart | 0 .../onboarding_page_bottom_nav_bar.dart | 2 +- .../lib/src/onboarding_screen.dart | 9 +- .../lib/src/screens}/screen1.dart | 2 +- .../lib/src/screens}/screen2.dart | 7 +- .../lib/src/screens}/screen3.dart | 15 +- .../lib/src/screens}/screen4.dart | 9 +- .../lib/src/screens}/screen5.dart | 7 +- .../features/onboarding_screen/pubspec.yaml | 46 + .../test/widget/onboarding_screen_test.dart | 8 +- packages/features/workspace_screen/.metadata | 10 + .../workspace_screen/analysis_options.yaml | 23 + .../components/bottom_bar/bottom_nav_bar.dart | 89 ++ .../bottom_bar/bottom_nav_bar_items.dart | 6 + .../tool_options/stroke_cap_tool_option.dart | 17 +- .../tool_options/stroke_tool_options.dart | 14 + .../stroke_width_tool_option.dart | 13 +- .../bottom_bar/tool_options/tool_option.dart | 28 + .../bottom_bar/tool_options/tool_options.dart | 30 + .../bottom_bar/tools}/tool_button.dart | 13 +- .../bottom_bar/tools}/tools_bottom_sheet.dart | 5 +- .../drawing_surface}/canvas_painter.dart | 7 +- .../checkerboard_pattern.dart | 11 +- .../drawing_surface}/command_painter.dart | 2 +- .../drawing_surface}/drawing_canvas.dart | 9 +- .../exit_fullscreen_button.dart | 4 +- .../components/top_bar}/overflow_menu.dart | 16 +- .../src/components/top_bar}/top_app_bar.dart | 2 +- .../src/models}/image_with_pixel_info.dart | 0 .../lib/src}/service/device_service.dart | 0 .../lib/src/states}/canvas_dirty_state.dart | 0 .../lib/src/states}/canvas_state_data.dart | 3 +- .../src/states/canvas_state_data.freezed.dart | 221 ++++ .../src/states}/canvas_state_provider.dart | 9 +- .../src/states/canvas_state_provider.g.dart | 25 + ...ool_options_visibility_state_provider.dart | 15 + ...l_options_visibility_state_provider.g.dart | 27 + .../lib/src/states}/workspace_state.dart | 0 .../src/states}/workspace_state_notifier.dart | 4 +- .../src/usecase/render_image_for_export.dart | 8 +- .../lib/src/workspace_screen.dart | 18 +- .../lib/workspace_screen.dart | 27 + .../features/workspace_screen/pubspec.yaml | 55 + .../unit}/render_image_for_export_test.dart | 11 +- .../render_image_for_export_test.mocks.dart | 684 ++++++++++ .../bottom_control_navigation_bar_test.dart | 162 +++ .../widget}/bottom_nav_bar_interactions.dart | 23 +- .../test/widget}/canvas_interactions.dart | 2 +- .../test/widget}/eraser_tool_test.dart | 16 +- .../test/widget}/hand_tool_test.dart | 17 +- .../interactive_viewer_interactions.dart | 0 .../test/widget/workspace_screen_test.dart | 21 +- packages/io_library/.metadata | 10 + packages/io_library/analysis_options.yaml | 23 + packages/io_library/lib/io_library.dart | 34 + .../lib/src/enums}/image_format.dart | 2 - .../lib/src/enums}/image_location.dart | 0 .../lib}/src/failure/load_image_failure.dart | 2 +- .../lib}/src/failure/save_image_failure.dart | 2 +- .../io_library/lib/src}/io_handler.dart | 29 +- .../converter/paint_converter.dart | 50 + .../converter/path_action_converter.dart | 44 + .../path_with_action_history_converter.dart | 34 + .../versioning/serializer_version.dart | 19 + .../versioning/version_strategy.dart | 25 + .../lib/src/models/catrobat_image.dart | 57 + .../lib/src/models/catrobat_image.g.dart | 28 + .../io_library/lib/src/models}/failure.dart | 0 .../lib/src/models}/image_from_file.dart | 2 +- .../lib/src/models}/image_meta_data.dart | 2 +- .../lib/src/models}/loggable_mixin.dart | 0 .../lib}/src/service/file_service.dart | 5 +- .../lib}/src/service/image_service.dart | 5 +- .../lib}/src/service/permission_service.dart | 6 +- .../src/service/photo_library_service.dart | 5 +- .../io_library/lib}/src/ui/about_dialog.dart | 6 +- .../lib}/src/ui/delete_project_dialog.dart | 2 +- .../lib}/src/ui/discard_changes_dialog.dart | 2 +- .../lib}/src/ui/generic_dialog.dart | 0 .../lib}/src/ui/image_format_info.dart | 3 +- .../lib}/src/ui/load_image_dialog.dart | 3 +- .../lib}/src/ui/overwrite_dialog.dart | 2 +- .../lib}/src/ui/project_details_dialog.dart | 8 +- .../lib}/src/ui/save_image_dialog.dart | 7 +- .../usecase/load_image_from_file_manager.dart | 36 +- .../load_image_from_photo_library.dart | 6 +- .../src/usecase/save_as_catrobat_image.dart | 21 +- .../src/usecase/save_as_raster_image.dart | 3 +- packages/io_library/pubspec.yaml | 59 + .../io_library/test/fixture/image/test.jpg | Bin 0 -> 1731 bytes .../io_library/test/fixture/image/test.png | Bin 0 -> 1272 bytes .../io_library/test/fixture/image/test1.png | Bin 0 -> 1272 bytes .../draw_path_command_serializer_test.dart | 60 + .../converter/paint_converter_test.dart | 90 ++ .../converter/path_action_converter_test.dart | 47 + ...th_with_action_history_converter_test.dart | 43 + .../image/catrobat_image_serializer_test.dart | 55 + .../utils/dummy_command_factory.dart | 63 + .../utils/dummy_paint_factory.dart | 39 + .../utils/dummy_path_factory.dart | 14 + .../utils/dummy_version_strategy.dart | 17 + .../test/unit}/service/file_service_test.dart | 2 +- .../unit}/service/image_service_test.dart | 2 +- .../service/photo_library_service_test.dart | 15 +- .../photo_library_service_test.mocks.dart | 480 +++++++ .../load_image_from_photo_library_test.dart | 3 +- ...d_image_from_photo_library_test.mocks.dart | 211 ++++ .../usecase/save_as_raster_image_test.dart | 9 +- .../save_as_raster_image_test.mocks.dart | 333 +++++ packages/l10n/.metadata | 10 + packages/l10n/analysis_options.yaml | 25 + packages/l10n/l10n.yaml | 6 + packages/l10n/lib/l10n.dart | 4 + .../l10n/lib/src/l10n/app_localizations.dart | 179 +++ .../lib/src/l10n/app_localizations_en.dart | 33 + .../l10n/lib/src/l10n/app_translations_en.arb | 0 packages/l10n/pubspec.yaml | 26 + packages/tools/.metadata | 10 + packages/tools/analysis_options.yaml | 23 + .../tools/lib}/src/brush_tool/brush_tool.dart | 8 +- .../src/brush_tool/brush_tool_provider.dart | 8 +- .../src/brush_tool/brush_tool_provider.g.dart | 24 + .../src/brush_tool/brush_tool_state_data.dart | 0 .../brush_tool_state_data.freezed.dart | 134 ++ .../brush_tool/brush_tool_state_provider.dart | 4 +- .../brush_tool_state_provider.g.dart | 26 + .../tools/lib/src/enums}/tool_types.dart | 0 .../src/eraser_tool/eraser_tool_provider.dart | 8 +- .../eraser_tool/eraser_tool_provider.g.dart | 24 + .../tools/lib}/src/hand_tool/hand_tool.dart | 7 +- .../src/hand_tool/hand_tool_provider.dart | 7 +- .../src/hand_tool/hand_tool_provider.g.dart | 24 + .../tool => packages/tools/lib}/src/tool.dart | 4 +- .../tools/lib}/src/tool_data.dart | 2 +- .../lib}/src/toolbox/toolbox_state_data.dart | 2 +- .../toolbox/toolbox_state_data.freezed.dart | 173 +++ .../src/toolbox/toolbox_state_provider.dart | 3 +- .../src/toolbox/toolbox_state_provider.g.dart | 25 + .../tools/lib/tools.dart | 15 +- packages/tools/pubspec.yaml | 45 + .../test/unit}/brush_tool_state_test.dart | 8 +- .../tools/test/unit}/brush_tool_test.dart | 6 +- .../test/unit/brush_tool_test.mocks.dart | 1108 +++++++++++++++++ .../tools/test/unit}/hand_tool_test.dart | 6 +- .../tools/test/unit/hand_tool_test.mocks.dart | 340 +++++ pubspec.lock | 561 ++++++--- pubspec.yaml | 53 +- setup_melos.sh | 14 + setup_sdk.sh | 72 ++ .../serialization/paint_serializer_test.dart | 53 - .../bottom_control_navigation_bar_test.dart | 36 - test/widget/utils/utils.dart | 2 - 282 files changed, 8726 insertions(+), 1487 deletions(-) create mode 100644 .fvm/fvm_config.json delete mode 100755 generate_files.sh delete mode 100755 generate_protos.sh delete mode 100644 l10n.yaml delete mode 100644 lib/command/command.dart delete mode 100644 lib/command/src/command.dart delete mode 100644 lib/command/src/implementation/command/graphic/draw_path_command.dart delete mode 100644 lib/core/app_localizations.dart delete mode 100644 lib/core/path_with_action_history.dart delete mode 100644 lib/io/io.dart delete mode 100644 lib/io/serialization.dart delete mode 100644 lib/io/src/entity/catrobat_image.dart delete mode 100644 lib/io/src/serialization/proto/protos.dart delete mode 100644 lib/io/src/serialization/proto/schema/catrobat_image.proto delete mode 100644 lib/io/src/serialization/proto/schema/command/graphic/draw_path_command.proto delete mode 100644 lib/io/src/serialization/proto/schema/graphic/paint.proto delete mode 100644 lib/io/src/serialization/proto/schema/graphic/path.proto delete mode 100644 lib/io/src/serialization/proto_serializer_with_versioning.dart delete mode 100644 lib/io/src/serialization/serializer/catrobat_image_serializer.dart delete mode 100644 lib/io/src/serialization/serializer/command/graphic/draw_path_command_serializer.dart delete mode 100644 lib/io/src/serialization/serializer/graphic/paint_serializer.dart delete mode 100644 lib/io/src/serialization/serializer/graphic/path_serializer.dart delete mode 100644 lib/io/src/serialization/version_serializer.dart create mode 100644 lib/pocket_paint_app.dart delete mode 100644 lib/ui/drawing_space/bottom_nav_bar.dart delete mode 100644 lib/ui/drawing_space/tool_options.dart delete mode 100644 lib/workspace/workspace.dart create mode 100644 melos.yaml create mode 100644 packages/command/.metadata create mode 100644 packages/command/analysis_options.yaml create mode 100644 packages/command/lib/command.dart create mode 100644 packages/command/lib/command_providers.dart rename {lib/core => packages/command/lib/graphic_factory}/graphic_factory.dart (93%) rename {lib/core => packages/command/lib/graphic_factory}/graphic_factory_provider.dart (77%) create mode 100644 packages/command/lib/graphic_factory/graphic_factory_provider.g.dart rename {lib/command/src => packages/command/lib/src/command_factory}/command_factory.dart (56%) rename {lib/command/src => packages/command/lib/src/command_factory}/command_factory_provider.dart (78%) create mode 100644 packages/command/lib/src/command_factory/command_factory_provider.g.dart create mode 100644 packages/command/lib/src/command_implementation/command.dart create mode 100644 packages/command/lib/src/command_implementation/graphic/draw_path_command.dart create mode 100644 packages/command/lib/src/command_implementation/graphic/draw_path_command.g.dart rename {lib/command/src => packages/command/lib/src/command_implementation/graphic}/graphic_command.dart (60%) rename {lib/command/src => packages/command/lib/src/command_manager}/command_manager.dart (74%) rename {lib/command/src => packages/command/lib/src/command_manager}/command_manager_provider.dart (60%) create mode 100644 packages/command/lib/src/command_manager/command_manager_provider.g.dart rename {lib/command/src/implementation/manager => packages/command/lib/src/command_manager}/sync_command_manager.dart (85%) create mode 100644 packages/command/lib/utils/path_with_action_history.dart create mode 100644 packages/command/pubspec.yaml rename {test/unit/command => packages/command/test/unit}/command_factory_test.dart (83%) rename {test/unit/command => packages/command/test/unit}/draw_path_command_test.dart (87%) create mode 100644 packages/command/test/unit/draw_path_command_test.mocks.dart create mode 100644 packages/component_library/.metadata create mode 100644 packages/component_library/analysis_options.yaml rename {assets => packages/component_library/assets}/img/checkerboard.png (100%) rename {assets/icon => packages/component_library/assets/img}/pocketpaint_intro_landscape.png (100%) rename {assets/icon => packages/component_library/assets/img}/pocketpaint_intro_portrait.png (100%) rename {assets/icon => packages/component_library/assets/img}/pocketpaint_logo_small.png (100%) rename {assets => packages/component_library/assets}/svg/ic_brush.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_clipboard.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_clipping.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_cursor.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_edit_circle.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_eraser.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_fill.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_hand.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_import.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_layers.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_line.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_pipette.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_shapes.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_smudge.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_spray_can.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_stamp.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_text.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_tools.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_transform.svg (100%) rename {assets => packages/component_library/assets}/svg/ic_watercolor.svg (100%) create mode 100644 packages/component_library/lib/component_library.dart rename {lib/ui/shared => packages/component_library/lib/src/components}/bottom_nav_bar_icon.dart (68%) rename {lib/ui/shared => packages/component_library/lib/src/components}/custom_action_chip.dart (93%) rename {lib/ui/shared => packages/component_library/lib/src/components}/icon_button_with_label.dart (100%) create mode 100644 packages/component_library/lib/src/components/icon_svg.dart create mode 100644 packages/component_library/lib/src/components/imgs.dart rename {lib/ui/shared => packages/component_library/lib/src/components}/loading_overlay.dart (100%) rename {lib/ui => packages/component_library/lib/src/components}/pop_menu_button.dart (100%) rename {lib/ui => packages/component_library/lib/src/theme}/color_schemes.dart (100%) rename {lib/ui => packages/component_library/lib/src/theme}/styles.dart (92%) rename lib/ui/util.dart => packages/component_library/lib/src/utils/open_url.dart (100%) rename {lib/core => packages/component_library/lib/src/utils}/toast_utils.dart (100%) create mode 100644 packages/component_library/pubspec.yaml create mode 100644 packages/database/.metadata create mode 100644 packages/database/analysis_options.yaml create mode 100644 packages/database/lib/database.dart rename {lib/data/model => packages/database/lib/src/models}/project.dart (100%) rename {lib/data => packages/database/lib/src}/project_dao.dart (92%) rename {lib/data => packages/database/lib/src}/project_database.dart (73%) create mode 100644 packages/database/lib/src/project_database.g.dart rename {lib/data/typeconverters => packages/database/lib/src/utils}/date_time_converter.dart (100%) create mode 100644 packages/database/pubspec.yaml rename {test/unit/data => packages/database/test/unit}/project_database_test.dart (95%) create mode 100644 packages/features/landing_page_screen/.metadata create mode 100644 packages/features/landing_page_screen/analysis_options.yaml create mode 100644 packages/features/landing_page_screen/lib/landing_page_screen.dart rename {lib/ui/landing_page => packages/features/landing_page_screen/lib/src/components}/custom_action_button.dart (90%) rename {lib/ui/landing_page => packages/features/landing_page_screen/lib/src/components}/image_preview.dart (89%) rename {lib/ui/landing_page => packages/features/landing_page_screen/lib/src/components}/main_overflow_menu.dart (88%) rename {lib/ui/landing_page => packages/features/landing_page_screen/lib/src/components}/project_list_tile.dart (84%) rename {lib/ui/landing_page => packages/features/landing_page_screen/lib/src/components}/project_overflow_menu.dart (89%) rename {lib/ui/landing_page => packages/features/landing_page_screen/lib/src}/landing_page.dart (87%) create mode 100644 packages/features/landing_page_screen/pubspec.yaml rename {test => packages/features/landing_page_screen/test}/fixture/image/test.jpg (100%) rename {test => packages/features/landing_page_screen/test}/fixture/image/test.png (100%) create mode 100644 packages/features/landing_page_screen/test/fixture/image/test1.png rename {test/widget/ui => packages/features/landing_page_screen/test/widget}/landing_page_test.dart (95%) create mode 100644 packages/features/landing_page_screen/test/widget/landing_page_test.mocks.dart create mode 100644 packages/features/onboarding_screen/.metadata create mode 100644 packages/features/onboarding_screen/analysis_options.yaml create mode 100644 packages/features/onboarding_screen/lib/onboarding_screen.dart rename {lib/ui/onboarding => packages/features/onboarding_screen/lib/src/components}/bottom_nav_bar_container.dart (83%) rename {lib/ui/onboarding => packages/features/onboarding_screen/lib/src/components}/onboarding_page_app_bar.dart (100%) rename {lib/ui/onboarding => packages/features/onboarding_screen/lib/src/components}/onboarding_page_bottom_nav_bar.dart (94%) rename lib/ui/onboarding/onboarding_page.dart => packages/features/onboarding_screen/lib/src/onboarding_screen.dart (90%) rename {lib/ui/onboarding/onboarding_screens => packages/features/onboarding_screen/lib/src/screens}/screen1.dart (94%) rename {lib/ui/onboarding/onboarding_screens => packages/features/onboarding_screen/lib/src/screens}/screen2.dart (93%) rename {lib/ui/onboarding/onboarding_screens => packages/features/onboarding_screen/lib/src/screens}/screen3.dart (94%) rename {lib/ui/onboarding/onboarding_screens => packages/features/onboarding_screen/lib/src/screens}/screen4.dart (86%) rename {lib/ui/onboarding/onboarding_screens => packages/features/onboarding_screen/lib/src/screens}/screen5.dart (88%) create mode 100644 packages/features/onboarding_screen/pubspec.yaml rename test/widget/ui/onboarding_page_test.dart => packages/features/onboarding_screen/test/widget/onboarding_screen_test.dart (97%) create mode 100644 packages/features/workspace_screen/.metadata create mode 100644 packages/features/workspace_screen/analysis_options.yaml create mode 100644 packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar.dart create mode 100644 packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar_items.dart rename lib/ui/drawing_space/bottom_brush_tool_options.dart => packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_cap_tool_option.dart (85%) create mode 100644 packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_tool_options.dart rename lib/ui/drawing_space/top_brush_tool_options.dart => packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_width_tool_option.dart (87%) create mode 100644 packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_option.dart create mode 100644 packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_options.dart rename {lib/ui/shared => packages/features/workspace_screen/lib/src/components/bottom_bar/tools}/tool_button.dart (70%) rename {lib/ui/drawing_space => packages/features/workspace_screen/lib/src/components/bottom_bar/tools}/tools_bottom_sheet.dart (74%) rename {lib/workspace/src/ui => packages/features/workspace_screen/lib/src/components/drawing_surface}/canvas_painter.dart (83%) rename {lib/workspace/src/ui => packages/features/workspace_screen/lib/src/components/drawing_surface}/checkerboard_pattern.dart (57%) rename {lib/workspace/src/ui => packages/features/workspace_screen/lib/src/components/drawing_surface}/command_painter.dart (89%) rename {lib/workspace/src/ui => packages/features/workspace_screen/lib/src/components/drawing_surface}/drawing_canvas.dart (90%) rename {lib/ui/drawing_space => packages/features/workspace_screen/lib/src/components/drawing_surface}/exit_fullscreen_button.dart (88%) rename {lib/ui/shared => packages/features/workspace_screen/lib/src/components/top_bar}/overflow_menu.dart (89%) rename {lib/ui/shared => packages/features/workspace_screen/lib/src/components/top_bar}/top_app_bar.dart (82%) rename {lib/core => packages/features/workspace_screen/lib/src/models}/image_with_pixel_info.dart (100%) rename {lib => packages/features/workspace_screen/lib/src}/service/device_service.dart (100%) rename {lib/workspace/src/state => packages/features/workspace_screen/lib/src/states}/canvas_dirty_state.dart (100%) rename {lib/workspace/src/state/canvas => packages/features/workspace_screen/lib/src/states}/canvas_state_data.dart (80%) create mode 100644 packages/features/workspace_screen/lib/src/states/canvas_state_data.freezed.dart rename {lib/workspace/src/state/canvas => packages/features/workspace_screen/lib/src/states}/canvas_state_provider.dart (89%) create mode 100644 packages/features/workspace_screen/lib/src/states/canvas_state_provider.g.dart create mode 100644 packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.dart create mode 100644 packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.g.dart rename {lib/workspace/src/state => packages/features/workspace_screen/lib/src/states}/workspace_state.dart (100%) rename {lib/workspace/src/state => packages/features/workspace_screen/lib/src/states}/workspace_state_notifier.dart (88%) rename {lib/workspace => packages/features/workspace_screen/lib}/src/usecase/render_image_for_export.dart (88%) rename lib/ui/pocket_paint.dart => packages/features/workspace_screen/lib/src/workspace_screen.dart (77%) create mode 100644 packages/features/workspace_screen/lib/workspace_screen.dart create mode 100644 packages/features/workspace_screen/pubspec.yaml rename {test/unit/workspace/usecase => packages/features/workspace_screen/test/unit}/render_image_for_export_test.dart (93%) create mode 100644 packages/features/workspace_screen/test/unit/render_image_for_export_test.mocks.dart create mode 100644 packages/features/workspace_screen/test/widget/bottom_control_navigation_bar_test.dart rename {test/widget/utils/interactions => packages/features/workspace_screen/test/widget}/bottom_nav_bar_interactions.dart (72%) rename {test/widget/utils/interactions => packages/features/workspace_screen/test/widget}/canvas_interactions.dart (96%) rename {test/widget/tool => packages/features/workspace_screen/test/widget}/eraser_tool_test.dart (75%) rename {test/widget/tool => packages/features/workspace_screen/test/widget}/hand_tool_test.dart (83%) rename {test/widget/utils/interactions => packages/features/workspace_screen/test/widget}/interactive_viewer_interactions.dart (100%) rename test/widget/ui/pocket_paint_test.dart => packages/features/workspace_screen/test/widget/workspace_screen_test.dart (83%) create mode 100644 packages/io_library/.metadata create mode 100644 packages/io_library/analysis_options.yaml create mode 100644 packages/io_library/lib/io_library.dart rename {lib/io/src/entity => packages/io_library/lib/src/enums}/image_format.dart (81%) rename {lib/io/src/entity => packages/io_library/lib/src/enums}/image_location.dart (100%) rename {lib/io => packages/io_library/lib}/src/failure/load_image_failure.dart (90%) rename {lib/io => packages/io_library/lib}/src/failure/save_image_failure.dart (90%) rename {lib/ui => packages/io_library/lib/src}/io_handler.dart (89%) create mode 100644 packages/io_library/lib/src/json_serialization/converter/paint_converter.dart create mode 100644 packages/io_library/lib/src/json_serialization/converter/path_action_converter.dart create mode 100644 packages/io_library/lib/src/json_serialization/converter/path_with_action_history_converter.dart create mode 100644 packages/io_library/lib/src/json_serialization/versioning/serializer_version.dart create mode 100644 packages/io_library/lib/src/json_serialization/versioning/version_strategy.dart create mode 100644 packages/io_library/lib/src/models/catrobat_image.dart create mode 100644 packages/io_library/lib/src/models/catrobat_image.g.dart rename {lib/core => packages/io_library/lib/src/models}/failure.dart (100%) rename {lib/io/src/entity => packages/io_library/lib/src/models}/image_from_file.dart (89%) rename {lib/io/src/entity => packages/io_library/lib/src/models}/image_meta_data.dart (94%) rename {lib/core => packages/io_library/lib/src/models}/loggable_mixin.dart (100%) rename {lib/io => packages/io_library/lib}/src/service/file_service.dart (96%) rename {lib/io => packages/io_library/lib}/src/service/image_service.dart (91%) rename {lib/io => packages/io_library/lib}/src/service/permission_service.dart (94%) rename {lib/io => packages/io_library/lib}/src/service/photo_library_service.dart (88%) rename {lib/io => packages/io_library/lib}/src/ui/about_dialog.dart (94%) rename {lib/io => packages/io_library/lib}/src/ui/delete_project_dialog.dart (93%) rename {lib/io => packages/io_library/lib}/src/ui/discard_changes_dialog.dart (94%) rename {lib/io => packages/io_library/lib}/src/ui/generic_dialog.dart (100%) rename {lib/io => packages/io_library/lib}/src/ui/image_format_info.dart (93%) rename {lib/io => packages/io_library/lib}/src/ui/load_image_dialog.dart (88%) rename {lib/io => packages/io_library/lib}/src/ui/overwrite_dialog.dart (92%) rename {lib/io => packages/io_library/lib}/src/ui/project_details_dialog.dart (93%) rename {lib/io => packages/io_library/lib}/src/ui/save_image_dialog.dart (97%) rename {lib/io => packages/io_library/lib}/src/usecase/load_image_from_file_manager.dart (66%) rename {lib/io => packages/io_library/lib}/src/usecase/load_image_from_photo_library.dart (77%) rename {lib/io => packages/io_library/lib}/src/usecase/save_as_catrobat_image.dart (59%) rename {lib/io => packages/io_library/lib}/src/usecase/save_as_raster_image.dart (93%) create mode 100644 packages/io_library/pubspec.yaml create mode 100644 packages/io_library/test/fixture/image/test.jpg create mode 100644 packages/io_library/test/fixture/image/test.png create mode 100644 packages/io_library/test/fixture/image/test1.png create mode 100644 packages/io_library/test/unit/serialization/command/draw_path_command_serializer_test.dart create mode 100644 packages/io_library/test/unit/serialization/converter/paint_converter_test.dart create mode 100644 packages/io_library/test/unit/serialization/converter/path_action_converter_test.dart create mode 100644 packages/io_library/test/unit/serialization/converter/path_with_action_history_converter_test.dart create mode 100644 packages/io_library/test/unit/serialization/image/catrobat_image_serializer_test.dart create mode 100644 packages/io_library/test/unit/serialization/utils/dummy_command_factory.dart create mode 100644 packages/io_library/test/unit/serialization/utils/dummy_paint_factory.dart create mode 100644 packages/io_library/test/unit/serialization/utils/dummy_path_factory.dart create mode 100644 packages/io_library/test/unit/serialization/utils/dummy_version_strategy.dart rename {test/unit/io => packages/io_library/test/unit}/service/file_service_test.dart (97%) rename {test/unit/io => packages/io_library/test/unit}/service/image_service_test.dart (97%) rename {test/unit/io => packages/io_library/test/unit}/service/photo_library_service_test.dart (93%) create mode 100644 packages/io_library/test/unit/service/photo_library_service_test.mocks.dart rename {test/unit/io => packages/io_library/test/unit}/usecase/load_image_from_photo_library_test.dart (97%) create mode 100644 packages/io_library/test/unit/usecase/load_image_from_photo_library_test.mocks.dart rename {test/unit/io => packages/io_library/test/unit}/usecase/save_as_raster_image_test.dart (97%) create mode 100644 packages/io_library/test/unit/usecase/save_as_raster_image_test.mocks.dart create mode 100644 packages/l10n/.metadata create mode 100644 packages/l10n/analysis_options.yaml create mode 100644 packages/l10n/l10n.yaml create mode 100644 packages/l10n/lib/l10n.dart create mode 100644 packages/l10n/lib/src/l10n/app_localizations.dart create mode 100644 packages/l10n/lib/src/l10n/app_localizations_en.dart rename assets/l10n/en.arb => packages/l10n/lib/src/l10n/app_translations_en.arb (100%) create mode 100644 packages/l10n/pubspec.yaml create mode 100644 packages/tools/.metadata create mode 100644 packages/tools/analysis_options.yaml rename {lib/tool => packages/tools/lib}/src/brush_tool/brush_tool.dart (82%) rename {lib/tool => packages/tools/lib}/src/brush_tool/brush_tool_provider.dart (52%) create mode 100644 packages/tools/lib/src/brush_tool/brush_tool_provider.g.dart rename {lib/tool => packages/tools/lib}/src/brush_tool/brush_tool_state_data.dart (100%) create mode 100644 packages/tools/lib/src/brush_tool/brush_tool_state_data.freezed.dart rename {lib/tool => packages/tools/lib}/src/brush_tool/brush_tool_state_provider.dart (89%) create mode 100644 packages/tools/lib/src/brush_tool/brush_tool_state_provider.g.dart rename {lib/tool/src => packages/tools/lib/src/enums}/tool_types.dart (100%) rename {lib/tool => packages/tools/lib}/src/eraser_tool/eraser_tool_provider.dart (52%) create mode 100644 packages/tools/lib/src/eraser_tool/eraser_tool_provider.g.dart rename {lib/tool => packages/tools/lib}/src/hand_tool/hand_tool.dart (76%) rename {lib/tool => packages/tools/lib}/src/hand_tool/hand_tool_provider.dart (53%) create mode 100644 packages/tools/lib/src/hand_tool/hand_tool_provider.g.dart rename {lib/tool => packages/tools/lib}/src/tool.dart (80%) rename {lib/tool => packages/tools/lib}/src/tool_data.dart (97%) rename {lib/tool => packages/tools/lib}/src/toolbox/toolbox_state_data.dart (88%) create mode 100644 packages/tools/lib/src/toolbox/toolbox_state_data.freezed.dart rename {lib/tool => packages/tools/lib}/src/toolbox/toolbox_state_provider.dart (94%) create mode 100644 packages/tools/lib/src/toolbox/toolbox_state_provider.g.dart rename lib/tool/tool.dart => packages/tools/lib/tools.dart (73%) create mode 100644 packages/tools/pubspec.yaml rename {test/unit/tool => packages/tools/test/unit}/brush_tool_state_test.dart (88%) rename {test/unit/tool => packages/tools/test/unit}/brush_tool_test.dart (95%) create mode 100644 packages/tools/test/unit/brush_tool_test.mocks.dart rename {test/unit/tool => packages/tools/test/unit}/hand_tool_test.dart (88%) create mode 100644 packages/tools/test/unit/hand_tool_test.mocks.dart create mode 100755 setup_melos.sh create mode 100755 setup_sdk.sh delete mode 100644 test/unit/io/serialization/paint_serializer_test.dart delete mode 100644 test/widget/ui/bottom_control_navigation_bar_test.dart delete mode 100644 test/widget/utils/utils.dart diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json new file mode 100644 index 00000000..23e73971 --- /dev/null +++ b/.fvm/fvm_config.json @@ -0,0 +1,4 @@ +{ + "flutterSdkVersion": "3.10.5", + "flavors": {} +} \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d56cf780..b522c6d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,36 +1,28 @@ name: Build, Test and Analyze -on: [ push, pull_request ] +on: [push, pull_request] jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install Protoc - uses: arduino/setup-protoc@v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: subosito/flutter-action@v2.10.0 with: - flutter-version: '3.10.5' - channel: 'stable' - cache-key: 'flutter-:os:-:channel:-:version:-:arch:-:hash:' - cache-path: '${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:' + flutter-version: "3.10.5" + channel: "stable" + cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:" + cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:" architecture: x64 # optional, x64 or arm64 - name: Setup run: | - flutter pub get - flutter pub run build_runner build --delete-conflicting-outputs - dart pub global activate protoc_plugin - chmod +x generate_protos.sh - ./generate_protos.sh + make get - name: Static Analysis - run: flutter analyze + run: make lint - name: Unit Tests - run: flutter test test/unit + run: melos run test:unit - name: Widget Tests - run: flutter test test/widget + run: melos run test:widget - name: Build release package run: flutter build apk --release - name: Archive build artifacts @@ -39,4 +31,3 @@ jobs: name: apk path: | build/app/outputs/flutter-apk/app-release.apk - diff --git a/.gitignore b/.gitignore index ec3a832c..ac8927d7 100644 --- a/.gitignore +++ b/.gitignore @@ -30,10 +30,12 @@ migrate_working_dir/ .pub/ /build/ coverage -*.mocks.dart -*.pb*.dart -*.g.dart -*.freezed.dart + +# Commented out to push generated code: +# *.mocks.dart +# *.pb*.dart +# *.g.dart +# *.freezed.dart # Web related lib/generated_plugin_registrant.dart @@ -48,3 +50,15 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +.fvm/flutter_sdk + +# Melos +pubspec_overrides.yaml + +# Packages +packages/*/pubspec.lock +packages/*/build + +packages/features/*/pubspec.lock +packages/features/*/build diff --git a/Makefile b/Makefile index ea6c04f4..516b0a8d 100644 --- a/Makefile +++ b/Makefile @@ -1,30 +1,56 @@ -.PHONY: pubget build watch clean test analyze test-unit test-widget test-all all +.PHONY: run pods-clean get clean build languages lint format test watch -clean: - flutter clean +FLUTTER := fvm flutter +DART := fvm dart + +run: + $(FLUTTER) run + +pods-clean: + rm -Rf ios/Pods ; \ + rm -Rf ios/.symlinks ; \ + rm -Rf ios/Flutter/Flutter.framework ; \ + rm -Rf ios/Flutter/Flutter.podspec ; \ + rm ios/Podfile ; \ + rm ios/Podfile.lock ; \ + +get: + chmod +x ./setup_sdk.sh + ./setup_sdk.sh + chmod +x ./setup_melos.sh + ./setup_melos.sh + melos bootstrap -pubget: - flutter pub get +clean: + melos clean build: - dart run build_runner build --delete-conflicting-outputs + melos run build:all -run: - flutter run +languages: + @cd packages/l10n ; \ + $(FLUTTER) gen-l10n + @echo "-> Generated l10n" -all: clean pubget build run +lint: + $(FLUTTER) analyze + melos run lint:all -watch: - dart run build_runner watch --delete-conflicting-outputs +format: + $(DART) format --set-exit-if-changed . -analyze: - flutter analyze +test: + melos run test:all test-unit: - flutter test test/unit + melos run test:unit test-widget: - flutter test test/widget + melos run test:widget + +watch: + $(DART) run build_runner watch --delete-conflicting-outputs -test-all: - flutter test +melos: + $(DART) pub global activate melos + \ No newline at end of file diff --git a/README.md b/README.md index 89672c2b..d3a25760 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,80 @@ -Paintroid -========= +# Paintroid -Paintroid, also known as **Pocket Paint**, is associated -to [Catroid](https://github.com/Catrobat/Catroid). It is a graphical paint editor application for -the Android platform that, among others, allows setting parts of pictures to transparent. +Paintroid, also known as **Pocket Paint**, is associated to [Catroid](https://github.com/Catrobat/Catroid). It is a graphical paint editor application for the Android platform that, among others, allows setting parts of pictures to transparent. -Since Pocket Paint is now available in **Google Play store** you can also download Paintroid (Pocket -Paint) from [here](https://play.google.com/store/apps/details?id=org.catrobat.paintroid). -Alternatively, you can find it on ** -F-Droid** [here](https://f-droid.org/packages/org.catrobat.paintroid/). +Since Pocket Paint is now available in **Google Play store** you can also download Paintroid (Pocket Paint) from [here](https://play.google.com/store/apps/details?id=org.catrobat.paintroid). Alternatively, you can find it on **F-Droid** [here](https://f-droid.org/packages/org.catrobat.paintroid/). -For more information oriented towards developers please visit -our [developers page](http://developer.catrobat.org/). +For more information oriented towards developers please visit our [developers page](http://developer.catrobat.org/). > **Note** This repository is the Flutter version of [Paintroid](https://github.com/Catrobat/Paintroid) ## Getting Started -1. Install [Flutter](https://docs.flutter.dev/get-started/install) - - check the currently used version in file ".github/workflows/main.yml" -2. Set up the [Protocol Buffer](https://grpc.io/docs/languages/dart/quickstart/) compiler - - `protoc` for Dart -4. Get dependencies - `flutter pub get` -5. Build supporting files - `./generate_files.sh` -6. Build protobuf files - `./generate_protos.sh` -7. Run app - `flutter run lib/main.dart` +1. Install [Flutter](https://docs.flutter.dev/get-started/install): + - Currently used version specified in _.github/workflows/main.yml_ + - **Recommended**: Use [fvm](https://fvm.app/) for managing Flutter versions +2. Get dependencies: `make get` +3. Run app: `make run` + +> In case `make` does not work for you, `melos` can be used for most of the commands. Check them out in _Makefile_ or in _melos.yaml_. + +What `make get` does: + +- Runs `./setup-sdk.sh`, if _fvm_ is not installed: + - changes "FLUTTER" (= `fvm flutter`) to `flutter` in _Makefile_ + - changes "DART" (= `fvm dart`) to `dart` in _Makefile_ if _fvm_ + - changes "sdkPath" (= `.fvm/flutter_sdk`) to `auto` in _melos.yaml_ +- Runs `./setup-melos.sh`: activates _melos_ if not activated. + +## Building generated files + +- For **protoc**: + - Set up the [Protocol Buffer](https://grpc.io/docs/languages/dart/quickstart/) compiler + - Run `./generate_protos.sh` +- For **build-runner**: run `make build` +- For **localizations**: run `make languages` ## Tests -1. For unit tests, run `flutter test` at the project root -2. For integration tests - - - Make sure you have an iOS/Android device online by running `flutter devices` - - Run `flutter test integration_test -d ` - > **Note** Replace `` with the ID of the device from previous command +- Run tests for **all** packages: + - all: `make test` + - unit: `make test-unit` + - widget: `make test-widget` +- Run tests for a **specific** package: + - all: `melos test` + - unit: `melos test-unit` + - widget: `melos test-widget` + +**For integration tests:** + +1. Make sure you have an iOS/Android device online by running `flutter devices` +2. `cd` into the package where the test is located +3. Run `flutter test -d ` + - Replace `` with the ID of the device from `flutter devices` + - Replace `` with the actual path to the test (_test/..._) -# Issues # +## Issues -**Please report all bugs on -our [Jira Bugtracker](https://jira.catrob.at/secure/CreateIssue.jspa?pid=10401&issuetype=1)** +**Please report all bugs on our [Jira Bugtracker](https://jira.catrob.at/secure/CreateIssue.jspa?pid=10401&issuetype=1)** -# Contributing # +## Contributing -If you want to contribute we suggest that you start -with [forking](https://help.github.com/articles/fork-a-repo/) our repository and browse the code. -Then you can look at our [Issue-Tracker](https://jira.catrob.at/secure/RapidBoard.jspa?rapidView=60) -and start with fixing one ticket. We strictly -use [Test-Driven Development](http://c2.com/cgi/wiki?TestDrivenDevelopment) -and [Clean Code](http://www.planetgeek.ch/wp-content/uploads/2013/06/Clean-Code-V2.2.pdf), so first -read everything you can about these development methods. Code developed in a different style will -not be accepted. After you've created a pull request we will review your code and do a full testrun -on your branch. +If you want to contribute we suggest that you start with [forking](https://help.github.com/articles/fork-a-repo/) our repository and browse the code. Then you can look at our [Issue-Tracker](https://jira.catrob.at/secure/RapidBoard.jspa?rapidView=60) and start with fixing one ticket. We strictly use [Test-Driven Development](http://c2.com/cgi/wiki?TestDrivenDevelopment) and [Clean Code](http://www.planetgeek.ch/wp-content/uploads/2013/06/Clean-Code-V2.2.pdf), so first read everything you can about these development methods. Code developed in a different style will not be accepted. After you've created a pull request we will review your code and do a full testrun on your branch. -If you want to implement a new feature, please ask about the details in JIRA or our IRC channel ( -#catrobat or #catrobatdev) first. +If you want to implement a new feature, please ask about the details in JIRA or our IRC channel (#catrobat or #catrobatdev) first. -Let's start to set up the working environment using the instructions in -our [Wiki](https://github.com/Catrobat/Catroid/wiki/Setup-working-environment)! +Let's start to set up the working environment using the instructions in our [Wiki](https://github.com/Catrobat/Catroid/wiki/Setup-working-environment)! -# Resources and links # +## Resources and links -* [Google Play Store Download](https://play.google.com/store/apps/details?id=org.catrobat.paintroid) -* [F-Droid Download](https://f-droid.org/packages/org.catrobat.paintroid/) -* [Frequently Asked Questions](https://github.com/Catrobat/Catroid/wiki/Frequently-Asked-Questions) -* [Credits](http://developer.catrobat.org/credits) -* [Statistics on OpenHub](https://www.openhub.net/p/catrobat/) -* [Twitter](http://twitter.com/Catroid) -* [Our Google group](https://groups.google.com/forum/?fromgroups#!forum/catrobat) +- [Google Play Store Download](https://play.google.com/store/apps/details?id=org.catrobat.paintroid) +- [F-Droid Download](https://f-droid.org/packages/org.catrobat.paintroid/) +- [Frequently Asked Questions](https://github.com/Catrobat/Catroid/wiki/Frequently-Asked-Questions) +- [Credits](http://developer.catrobat.org/credits) +- [Statistics on OpenHub](https://www.openhub.net/p/catrobat/) +- [Twitter](http://twitter.com/Catroid) +- [Our Google group](https://groups.google.com/forum/?fromgroups#!forum/catrobat) -# License # +## License [License](http://developer.catrobat.org/licenses) of our project (mainly AGPL v3). diff --git a/analysis_options.yaml b/analysis_options.yaml index f26de79a..1ee68bd9 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -9,7 +9,6 @@ linter: constant_identifier_names: false analyzer: - errors: missing_enum_constant_in_switch: error exhaustive_cases: error @@ -20,5 +19,5 @@ analyzer: unused_import: error exclude: - - lib/**.pb*.dart - - lib/data/*.g.dart + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/generate_files.sh b/generate_files.sh deleted file mode 100755 index acb214db..00000000 --- a/generate_files.sh +++ /dev/null @@ -1 +0,0 @@ -flutter pub run build_runner build --delete-conflicting-outputs \ No newline at end of file diff --git a/generate_protos.sh b/generate_protos.sh deleted file mode 100755 index a0183514..00000000 --- a/generate_protos.sh +++ /dev/null @@ -1,3 +0,0 @@ -cd lib/io/src/serialization/proto || exit -mkdir -p output -protoc --dart_out=output --proto_path=schema $(find schema -iname "*.proto") google/protobuf/any.proto \ No newline at end of file diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 8d4492f9..9625e105 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/ios/Podfile b/ios/Podfile index 96a322b8..3854b694 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 72cf21bc..62cb12ec 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - device_info_plus (0.0.1): + - Flutter - DKImagePickerController/Core (4.3.4): - DKImagePickerController/ImageDataManager - DKImagePickerController/Resource @@ -34,60 +36,108 @@ PODS: - DKImagePickerController/PhotoGallery - Flutter - Flutter (1.0.0) + - flutter_localization (0.0.1): + - Flutter + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) - image_picker_ios (0.0.1): - Flutter - integration_test (0.0.1): - Flutter - - path_provider_ios (0.0.1): + - launch_review (0.0.1): + - Flutter + - package_info_plus (0.4.5): + - Flutter + - path_provider_foundation (0.0.1): - Flutter - - permission_handler_apple (9.0.4): + - FlutterMacOS + - permission_handler_apple (9.1.1): - Flutter - SDWebImage (5.13.1): - SDWebImage/Core (= 5.13.1) - SDWebImage/Core (5.13.1) + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite (0.0.3): + - Flutter + - FMDB (>= 2.7.5) - SwiftyGif (5.4.3) + - url_launcher_ios (0.0.1): + - Flutter DEPENDENCIES: + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) + - flutter_localization (from `.symlinks/plugins/flutter_localization/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - launch_review (from `.symlinks/plugins/launch_review/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: trunk: - DKImagePickerController - DKPhotoGallery + - FMDB - SDWebImage - SwiftyGif EXTERNAL SOURCES: + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" file_picker: :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter + flutter_localization: + :path: ".symlinks/plugins/flutter_localization/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" integration_test: :path: ".symlinks/plugins/integration_test/ios" - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + launch_review: + :path: ".symlinks/plugins/launch_review/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: + device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - file_picker: 817ab1d8cd2da9d2da412a417162deee3500fc95 - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb - integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 - permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + file_picker: ce3938a0df3cc1ef404671531facef740d03f920 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_localization: f43b18844a2b3d2c71fd64f04ffd6b1e64dd54d4 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 + integration_test: 13825b8a9334a850581300559b8839134b124670 + launch_review: 75d5a956ba8eaa493e9c9d4bf4c05e505e8d5ed0 + package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 SDWebImage: fb26a455eeda4c7a55e4dcb6172dbb258af7a4ca + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 + url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b -PODFILE CHECKSUM: 0bb9cd0668dcd7eee7f213e7cfebebde242de503 +PODFILE CHECKSUM: a62623f56f2d1d0e85a4a3c73509cd2832d5c86f -COCOAPODS: 1.11.3 +COCOAPODS: 1.14.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 2db987e8..a7efac41 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -200,10 +200,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -236,6 +238,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -340,7 +343,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -418,7 +421,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -467,7 +470,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index ea55f3eb..83a9271b 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -118,5 +118,7 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/l10n.yaml b/l10n.yaml deleted file mode 100644 index fc80cd87..00000000 --- a/l10n.yaml +++ /dev/null @@ -1,3 +0,0 @@ -arb-dir: assets/l10n -template-arb-file: en.arb -output-localization-file: app_localizations.dart \ No newline at end of file diff --git a/lib/command/command.dart b/lib/command/command.dart deleted file mode 100644 index 041aef1a..00000000 --- a/lib/command/command.dart +++ /dev/null @@ -1,6 +0,0 @@ -export 'src/command.dart'; -export 'src/command_factory.dart'; -export 'src/command_manager.dart'; -export 'src/graphic_command.dart'; -export 'src/implementation/command/graphic/draw_path_command.dart'; -export 'src/implementation/manager/sync_command_manager.dart'; diff --git a/lib/command/src/command.dart b/lib/command/src/command.dart deleted file mode 100644 index 555fd20a..00000000 --- a/lib/command/src/command.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:equatable/equatable.dart'; - -abstract class Command with EquatableMixin { - const Command(); -} diff --git a/lib/command/src/implementation/command/graphic/draw_path_command.dart b/lib/command/src/implementation/command/graphic/draw_path_command.dart deleted file mode 100644 index 1b4993ba..00000000 --- a/lib/command/src/implementation/command/graphic/draw_path_command.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/widgets.dart'; -import 'package:paintroid/command/src/graphic_command.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; - -class DrawPathCommand extends GraphicCommand { - const DrawPathCommand(this.path, super.paint); - - final PathWithActionHistory path; - - @override - void call(Canvas canvas) { - canvas.drawPath(path, paint); - } - - @override - List get props => [paint, path]; -} diff --git a/lib/core/app_localizations.dart b/lib/core/app_localizations.dart deleted file mode 100644 index 304170cb..00000000 --- a/lib/core/app_localizations.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/widgets.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart' as generated; -import 'package:flutter_gen/gen_l10n/app_localizations_en.dart'; - -class AppLocalizations { - AppLocalizations._(); - - static generated.AppLocalizations of(BuildContext context) => - generated.AppLocalizations.of(context) ?? AppLocalizationsEn(); -} diff --git a/lib/core/path_with_action_history.dart b/lib/core/path_with_action_history.dart deleted file mode 100644 index 75bffb9b..00000000 --- a/lib/core/path_with_action_history.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'dart:ui'; - -class PathWithActionHistory extends Path { - final actions = []; - - @override - void moveTo(double x, double y) { - actions.add(MoveToAction(x, y)); - super.moveTo(x, y); - } - - @override - void lineTo(double x, double y) { - actions.add(LineToAction(x, y)); - super.lineTo(x, y); - } - - @override - void close() { - actions.add(const CloseAction()); - super.close(); - } -} - -abstract class PathAction { - const PathAction(); -} - -class MoveToAction extends PathAction { - final double x; - final double y; - - const MoveToAction(this.x, this.y); -} - -class LineToAction extends PathAction { - final double x; - final double y; - - const LineToAction(this.x, this.y); -} - -class CloseAction extends PathAction { - const CloseAction(); -} diff --git a/lib/io/io.dart b/lib/io/io.dart deleted file mode 100644 index 51858f85..00000000 --- a/lib/io/io.dart +++ /dev/null @@ -1,18 +0,0 @@ -export 'src/entity/catrobat_image.dart'; -export 'src/entity/image_from_file.dart'; -export 'src/entity/image_location.dart'; -export 'src/entity/image_meta_data.dart'; -export 'src/failure/load_image_failure.dart'; -export 'src/failure/save_image_failure.dart'; -export 'src/serialization/serializer/catrobat_image_serializer.dart'; -export 'src/service/file_service.dart'; -export 'src/service/image_service.dart'; -export 'src/service/permission_service.dart'; -export 'src/service/photo_library_service.dart'; -export 'src/ui/discard_changes_dialog.dart'; -export 'src/ui/load_image_dialog.dart'; -export 'src/ui/save_image_dialog.dart'; -export 'src/usecase/load_image_from_file_manager.dart'; -export 'src/usecase/load_image_from_photo_library.dart'; -export 'src/usecase/save_as_catrobat_image.dart'; -export 'src/usecase/save_as_raster_image.dart'; diff --git a/lib/io/serialization.dart b/lib/io/serialization.dart deleted file mode 100644 index 7009b8be..00000000 --- a/lib/io/serialization.dart +++ /dev/null @@ -1,10 +0,0 @@ -export 'src/serialization/proto/output/catrobat_image.pb.dart'; -export 'src/serialization/proto/output/command/graphic/draw_path_command.pb.dart'; -export 'src/serialization/proto/output/google/protobuf/any.pb.dart'; -export 'src/serialization/proto/output/graphic/paint.pb.dart'; -export 'src/serialization/proto/output/graphic/path.pb.dart'; -export 'src/serialization/proto_serializer_with_versioning.dart'; -export 'src/serialization/serializer/command/graphic/draw_path_command_serializer.dart'; -export 'src/serialization/serializer/graphic/paint_serializer.dart'; -export 'src/serialization/serializer/graphic/path_serializer.dart'; -export 'src/serialization/version_serializer.dart'; diff --git a/lib/io/src/entity/catrobat_image.dart b/lib/io/src/entity/catrobat_image.dart deleted file mode 100644 index 67e6a703..00000000 --- a/lib/io/src/entity/catrobat_image.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'dart:ui'; - -import 'package:paintroid/command/command.dart' show Command; - -class CatrobatImage { - static const magicValue = 'CATROBAT'; - static const latestVersion = 1; - - final int version; - final int width; - final int height; - final Iterable commands; - final Image? backgroundImage; - - const CatrobatImage( - this.commands, - this.width, - this.height, - this.backgroundImage, { - this.version = latestVersion, - }); -} diff --git a/lib/io/src/serialization/proto/protos.dart b/lib/io/src/serialization/proto/protos.dart deleted file mode 100644 index a180b981..00000000 --- a/lib/io/src/serialization/proto/protos.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'output/command/graphic/draw_path_command.pb.dart'; -export 'output/google/protobuf/any.pb.dart'; -export 'output/graphic/paint.pb.dart'; -export 'output/graphic/path.pb.dart'; diff --git a/lib/io/src/serialization/proto/schema/catrobat_image.proto b/lib/io/src/serialization/proto/schema/catrobat_image.proto deleted file mode 100644 index 5ac819af..00000000 --- a/lib/io/src/serialization/proto/schema/catrobat_image.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -import "google/protobuf/any.proto"; - -message SerializableCatrobatImage { - string magicValue = 1; - int32 version = 2; - uint32 width = 3; - uint32 height = 4; - repeated google.protobuf.Any commands = 5; - bytes backgroundImage = 6; -} \ No newline at end of file diff --git a/lib/io/src/serialization/proto/schema/command/graphic/draw_path_command.proto b/lib/io/src/serialization/proto/schema/command/graphic/draw_path_command.proto deleted file mode 100644 index 84b8781b..00000000 --- a/lib/io/src/serialization/proto/schema/command/graphic/draw_path_command.proto +++ /dev/null @@ -1,9 +0,0 @@ -syntax = 'proto3'; - -import 'graphic/paint.proto'; -import 'graphic/path.proto'; - -message SerializableDrawPathCommand { - SerializablePaint paint = 1; - SerializablePath path = 2; -} \ No newline at end of file diff --git a/lib/io/src/serialization/proto/schema/graphic/paint.proto b/lib/io/src/serialization/proto/schema/graphic/paint.proto deleted file mode 100644 index d9a40ceb..00000000 --- a/lib/io/src/serialization/proto/schema/graphic/paint.proto +++ /dev/null @@ -1,34 +0,0 @@ -syntax = 'proto3'; - -message SerializablePaint { - uint32 color = 1; - - float strokeWidth = 2; - - enum StrokeCap { - STROKE_CAP_ROUND = 0; - STROKE_CAP_BUTT = 1; - STROKE_CAP_SQUARE = 2; - } - StrokeCap cap = 3; - - enum PaintingStyle { - PAINTING_STYLE_FILL = 0; - PAINTING_STYLE_STROKE = 1; - } - PaintingStyle style = 4; - - enum BlendMode { - BLEND_MODE_SCR_OVER = 0; - BLEND_MODE_CLEAR = 1; - } - BlendMode blendMode = 5; - - enum StrokeJoin { - STROKE_JOIN_MITER = 0; - STROKE_JOIN_ROUND = 1; - STROKE_JOIN_BEVEL = 2; - } - StrokeJoin strokeJoin = 6; - -} \ No newline at end of file diff --git a/lib/io/src/serialization/proto/schema/graphic/path.proto b/lib/io/src/serialization/proto/schema/graphic/path.proto deleted file mode 100644 index 8fa45480..00000000 --- a/lib/io/src/serialization/proto/schema/graphic/path.proto +++ /dev/null @@ -1,27 +0,0 @@ -syntax = "proto3"; - -message SerializablePath { - message Action { - message MoveTo { - double x = 1; - double y = 2; - } - message LineTo { - double x = 1; - double y = 2; - } - message Close {} - - oneof action { - MoveTo move_to = 1; - LineTo line_to = 2; - Close close = 3; - } - } - repeated Action actions = 1; - enum FillType { - NON_ZERO = 0; - EVEN_ODD = 1; - } - FillType fill_type = 2; -} \ No newline at end of file diff --git a/lib/io/src/serialization/proto_serializer_with_versioning.dart b/lib/io/src/serialization/proto_serializer_with_versioning.dart deleted file mode 100644 index df24ba22..00000000 --- a/lib/io/src/serialization/proto_serializer_with_versioning.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:paintroid/io/src/serialization/version_serializer.dart'; -import 'package:protobuf/protobuf.dart' show GeneratedMessage; - -abstract class ProtoSerializerWithVersioning - extends VersionSerializer { - const ProtoSerializerWithVersioning(super.version); - - static const urlPrefix = 'org.catrobat.paintroid'; - - @protected - SERIALIZABLE Function(Uint8List binary) get fromBytesToSerializable; - - @nonVirtual - Future fromBytes(Uint8List binary) => - deserialize(fromBytesToSerializable(binary)); - - @nonVirtual - Future toBytes(T object) async => - (await serializeWithLatestVersion(object)).writeToBuffer(); -} diff --git a/lib/io/src/serialization/serializer/catrobat_image_serializer.dart b/lib/io/src/serialization/serializer/catrobat_image_serializer.dart deleted file mode 100644 index bdbed0e0..00000000 --- a/lib/io/src/serialization/serializer/catrobat_image_serializer.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:typed_data'; -import 'dart:ui'; - -import 'package:flutter_riverpod/flutter_riverpod.dart' show Provider; -import 'package:paintroid/command/command.dart' show Command, DrawPathCommand; -import 'package:paintroid/io/io.dart' show CatrobatImage, IImageService; -import 'package:paintroid/io/serialization.dart'; - -class CatrobatImageSerializer extends ProtoSerializerWithVersioning< - CatrobatImage, SerializableCatrobatImage> { - final DrawPathCommandSerializer _drawPathCommandSerializer; - final IImageService _imageService; - - const CatrobatImageSerializer( - super.version, this._imageService, this._drawPathCommandSerializer); - - static final provider = Provider.family( - (ref, int ver) => CatrobatImageSerializer( - ver, - ref.watch(IImageService.provider), - ref.watch(DrawPathCommandSerializer.provider(ver)), - ), - ); - - @override - Future serializeWithLatestVersion( - CatrobatImage object) async { - Uint8List? backgroundImageData; - if (object.backgroundImage != null) { - final result = await _imageService.exportAsPng(object.backgroundImage!); - backgroundImageData = - result.unwrapOrElse((failure) => throw failure.message); - } - return SerializableCatrobatImage() - ..magicValue = CatrobatImage.magicValue - ..version = CatrobatImage.latestVersion - ..width = object.width - ..height = object.height - ..backgroundImage = - (backgroundImageData != null) ? backgroundImageData : Uint8List(0) - ..commands.addAll(await Future.wait(object.commands.map((command) async { - if (command is DrawPathCommand) { - return Any.pack( - await _drawPathCommandSerializer - .serializeWithLatestVersion(command), - typeUrlPrefix: ProtoSerializerWithVersioning.urlPrefix, - ); - } else { - throw 'Invalid command type'; - } - }))); - } - - @override - Future deserializeWithLatestVersion( - SerializableCatrobatImage data) async { - final commands = []; - for (final cmd in data.commands) { - if (cmd.canUnpackInto(SerializableDrawPathCommand.getDefault())) { - final unpacked = cmd.unpackInto(SerializableDrawPathCommand()); - commands.add(await _drawPathCommandSerializer.deserialize(unpacked)); - } else { - throw 'Invalid command type'; - } - } - Image? image; - if (data.hasBackgroundImage()) { - final result = - await _imageService.import(Uint8List.fromList(data.backgroundImage)); - image = result.unwrapOrElse((failure) => throw failure.message); - } - return CatrobatImage(commands, data.width, data.height, image, - version: data.version); - } - - @override - final fromBytesToSerializable = SerializableCatrobatImage.fromBuffer; -} diff --git a/lib/io/src/serialization/serializer/command/graphic/draw_path_command_serializer.dart b/lib/io/src/serialization/serializer/command/graphic/draw_path_command_serializer.dart deleted file mode 100644 index 1e1fbeea..00000000 --- a/lib/io/src/serialization/serializer/command/graphic/draw_path_command_serializer.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/command/command.dart' - show CommandFactory, DrawPathCommand; -import 'package:paintroid/command/src/command_factory_provider.dart'; -import 'package:paintroid/io/serialization.dart'; - -class DrawPathCommandSerializer extends ProtoSerializerWithVersioning< - DrawPathCommand, SerializableDrawPathCommand> { - final PathSerializer _pathSerializer; - final PaintSerializer _paintSerializer; - final CommandFactory _commandFactory; - - const DrawPathCommandSerializer( - super.version, - this._pathSerializer, - this._paintSerializer, - this._commandFactory, - ); - - static final provider = Provider.family( - (ref, int ver) => DrawPathCommandSerializer( - ver, - ref.watch(PathSerializer.provider(ver)), - ref.watch(PaintSerializer.provider(ver)), - ref.watch(commandFactoryProvider)), - ); - - @override - final fromBytesToSerializable = SerializableDrawPathCommand.fromBuffer; - - @override - Future deserializeWithLatestVersion( - SerializableDrawPathCommand data) async { - final path = await _pathSerializer.deserialize(data.path); - final paint = await _paintSerializer.deserialize(data.paint); - return _commandFactory.createDrawPathCommand(path, paint); - } - - @override - Future serializeWithLatestVersion( - DrawPathCommand object) async { - final sPaint = - await _paintSerializer.serializeWithLatestVersion(object.paint); - final sPath = await _pathSerializer.serializeWithLatestVersion(object.path); - return SerializableDrawPathCommand() - ..paint = sPaint - ..path = sPath; - } -} diff --git a/lib/io/src/serialization/serializer/graphic/paint_serializer.dart b/lib/io/src/serialization/serializer/graphic/paint_serializer.dart deleted file mode 100644 index 0836ab48..00000000 --- a/lib/io/src/serialization/serializer/graphic/paint_serializer.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter_riverpod/flutter_riverpod.dart' show Provider; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/io/serialization.dart'; - -class PaintSerializer - extends ProtoSerializerWithVersioning { - final GraphicFactory _graphicFactory; - - static const _capMap = { - SerializablePaint_StrokeCap.STROKE_CAP_BUTT: StrokeCap.butt, - SerializablePaint_StrokeCap.STROKE_CAP_ROUND: StrokeCap.round, - SerializablePaint_StrokeCap.STROKE_CAP_SQUARE: StrokeCap.square, - }; - - static const _styleMap = { - SerializablePaint_PaintingStyle.PAINTING_STYLE_FILL: PaintingStyle.fill, - SerializablePaint_PaintingStyle.PAINTING_STYLE_STROKE: PaintingStyle.stroke, - }; - - static const _blendModeMap = { - SerializablePaint_BlendMode.BLEND_MODE_SCR_OVER: BlendMode.srcOver, - SerializablePaint_BlendMode.BLEND_MODE_CLEAR: BlendMode.clear, - }; - - static const _strokeJoinMap = { - SerializablePaint_StrokeJoin.STROKE_JOIN_MITER: StrokeJoin.miter, - SerializablePaint_StrokeJoin.STROKE_JOIN_ROUND: StrokeJoin.round, - SerializablePaint_StrokeJoin.STROKE_JOIN_BEVEL: StrokeJoin.bevel, - }; - - const PaintSerializer(super.version, this._graphicFactory); - - static final provider = Provider.family( - (ref, int ver) => PaintSerializer(ver, ref.watch(graphicFactoryProvider)), - ); - - @override - final fromBytesToSerializable = SerializablePaint.fromBuffer; - - @override - Future deserializeWithLatestVersion(SerializablePaint data) async { - return _graphicFactory.createPaint() - ..color = Color(data.color) - ..strokeWidth = data.strokeWidth - ..strokeCap = _capMap[data.cap] ?? StrokeCap.butt - ..style = _styleMap[data.style] ?? PaintingStyle.fill - ..blendMode = _blendModeMap[data.blendMode] ?? BlendMode.srcOver - ..strokeJoin = _strokeJoinMap[data.strokeJoin] ?? StrokeJoin.miter; - } - - @override - Future serializeWithLatestVersion(Paint object) async { - final serializable = SerializablePaint() - ..color = object.color.value - ..strokeWidth = object.strokeWidth - ..cap = _capMap.entries.firstWhere((e) => e.value == object.strokeCap).key - ..style = _styleMap.entries.firstWhere((e) => e.value == object.style).key - ..blendMode = _blendModeMap.entries - .firstWhere((e) => e.value == object.blendMode) - .key - ..strokeJoin = _strokeJoinMap.entries - .firstWhere((e) => e.value == object.strokeJoin) - .key; - return serializable; - } -} diff --git a/lib/io/src/serialization/serializer/graphic/path_serializer.dart b/lib/io/src/serialization/serializer/graphic/path_serializer.dart deleted file mode 100644 index c60f99c9..00000000 --- a/lib/io/src/serialization/serializer/graphic/path_serializer.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; -import 'package:paintroid/io/serialization.dart'; - -class PathSerializer extends ProtoSerializerWithVersioning< - PathWithActionHistory, SerializablePath> with LoggableMixin { - final GraphicFactory _graphicFactory; - - PathSerializer(super.version, this._graphicFactory); - - static final provider = Provider.family( - (ref, int ver) => PathSerializer(ver, ref.watch(graphicFactoryProvider)), - ); - - @override - Future deserializeWithLatestVersion( - SerializablePath data) async { - final path = _graphicFactory.createPathWithActionHistory(); - switch (data.fillType) { - case SerializablePath_FillType.EVEN_ODD: - path.fillType = PathFillType.evenOdd; - break; - case SerializablePath_FillType.NON_ZERO: - path.fillType = PathFillType.nonZero; - break; - } - for (var i = 0; i < data.actions.length; i++) { - final action = data.actions[i]; - if (action.hasMoveTo()) { - path.moveTo(action.moveTo.x, action.moveTo.y); - } else if (action.hasLineTo()) { - path.lineTo(action.lineTo.x, action.lineTo.y); - } else if (action.hasClose()) { - path.close(); - } else { - logger.severe('No Path Action was set at index $i.'); - } - } - return path; - } - - @override - final fromBytesToSerializable = SerializablePath.fromBuffer; - - @override - Future serializeWithLatestVersion( - PathWithActionHistory object) async { - final serializablePath = SerializablePath(); - switch (object.fillType) { - case PathFillType.nonZero: - serializablePath.fillType = SerializablePath_FillType.NON_ZERO; - break; - case PathFillType.evenOdd: - serializablePath.fillType = SerializablePath_FillType.EVEN_ODD; - break; - } - for (final action in object.actions) { - late final SerializablePath_Action serializableAction; - if (action is MoveToAction) { - final moveTo = SerializablePath_Action_MoveTo() - ..x = action.x - ..y = action.y; - serializableAction = SerializablePath_Action()..moveTo = moveTo; - } else if (action is LineToAction) { - final lineTo = SerializablePath_Action_LineTo() - ..x = action.x - ..y = action.y; - serializableAction = SerializablePath_Action()..lineTo = lineTo; - } else if (action is CloseAction) { - final close = SerializablePath_Action_Close(); - serializableAction = SerializablePath_Action()..close = close; - } else { - logger.severe('Path Action serialization was not handled for $action'); - } - serializablePath.actions.add(serializableAction); - } - return serializablePath; - } -} diff --git a/lib/io/src/serialization/version_serializer.dart b/lib/io/src/serialization/version_serializer.dart deleted file mode 100644 index 5fd5b1f5..00000000 --- a/lib/io/src/serialization/version_serializer.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/foundation.dart'; - -abstract class VersionSerializer { - final int version; - - static const v1 = 1; - - const VersionSerializer(this.version); - - Future serializeWithLatestVersion(FROM object); - - @nonVirtual - Future deserialize(TO data) { - switch (version) { - case v1: - return deserializeV1(data); - default: - throw 'Invalid version'; - } - } - - @protected - Future deserializeV1(TO data) => deserializeWithLatestVersion(data); - - @protected - Future deserializeWithLatestVersion(TO data); -} diff --git a/lib/main.dart b/lib/main.dart index 2a7cf6cd..27182e3a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,10 @@ import 'dart:developer'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logging/logging.dart'; -import 'package:paintroid/ui/color_schemes.dart'; -import 'package:paintroid/ui/landing_page/landing_page.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page.dart'; -import 'package:paintroid/ui/pocket_paint.dart'; -import 'package:paintroid/ui/shared/loading_overlay.dart'; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:paintroid/pocket_paint_app.dart'; + import 'package:shared_preferences/shared_preferences.dart'; void main() async { @@ -36,56 +31,3 @@ void main() async { ), ); } - -class PocketPaintApp extends StatelessWidget { - final bool showOnboardingPage; - - const PocketPaintApp({Key? key, required this.showOnboardingPage}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Pocket Paint', - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - theme: ThemeData.from( - useMaterial3: true, - colorScheme: lightColorScheme, - ), - initialRoute: '/', - onGenerateRoute: (settings) { - switch (settings.name) { - case '/': - return MaterialPageRoute( - builder: (context) => showOnboardingPage - ? const OnboardingPage( - navigateTo: LandingPage(title: 'Pocket Paint'), - ) - : const LandingPage(title: 'Pocket Paint'), - ); - case '/PocketPaint': - return MaterialPageRoute( - builder: (context) => const PocketPaint(), - ); - case '/OnboardingPage': - return MaterialPageRoute( - builder: (context) => const OnboardingPage(), - ); - } - return null; - }, - home: Consumer( - builder: (BuildContext context, WidgetRef ref, Widget? child) { - return LoadingOverlay( - isLoading: ref.watch( - WorkspaceState.provider - .select((state) => state.isPerformingIOTask), - ), - child: child); - }, - child: const LandingPage(title: 'Pocket Paint'), - ), - ); - } -} diff --git a/lib/pocket_paint_app.dart b/lib/pocket_paint_app.dart new file mode 100644 index 00000000..3d617c6a --- /dev/null +++ b/lib/pocket_paint_app.dart @@ -0,0 +1,66 @@ +import 'package:component_library/component_library.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:l10n/l10n.dart'; +import 'package:landing_page_screen/landing_page_screen.dart'; +import 'package:onboarding_screen/onboarding_screen.dart'; +import 'package:workspace_screen/workspace_screen.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + +class PocketPaintApp extends StatelessWidget { + final bool showOnboardingPage; + + const PocketPaintApp({Key? key, required this.showOnboardingPage}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Pocket Paint', + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: AppLocalizations.supportedLocales, + theme: ThemeData.from( + useMaterial3: true, + colorScheme: lightColorScheme, + ), + initialRoute: '/', + onGenerateRoute: (settings) { + switch (settings.name) { + case '/': + return MaterialPageRoute( + builder: (context) => showOnboardingPage + ? const OnboardingPage( + navigateTo: LandingPage(title: 'Pocket Paint'), + ) + : const LandingPage(title: 'Pocket Paint'), + ); + case '/PocketPaint': + return MaterialPageRoute( + builder: (context) => const WorkspaceScreen(), + ); + case '/OnboardingPage': + return MaterialPageRoute( + builder: (context) => const OnboardingPage(), + ); + } + return null; + }, + home: Consumer( + builder: (BuildContext context, WidgetRef ref, Widget? child) { + return LoadingOverlay( + isLoading: ref.watch( + WorkspaceState.provider + .select((state) => state.isPerformingIOTask), + ), + child: child); + }, + child: const LandingPage(title: 'Pocket Paint'), + ), + ); + } +} diff --git a/lib/ui/drawing_space/bottom_nav_bar.dart b/lib/ui/drawing_space/bottom_nav_bar.dart deleted file mode 100644 index 12b1e4e4..00000000 --- a/lib/ui/drawing_space/bottom_nav_bar.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/core/app_localizations.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/drawing_space/tools_bottom_sheet.dart'; -import 'package:paintroid/ui/shared/bottom_nav_bar_icon.dart'; -import 'package:paintroid/ui/styles.dart'; - -class BottomNavBar extends StatelessWidget { - static const height = 64.0; - - const BottomNavBar({Key? key}) : super(key: key); - - void _onNavigationItemSelected(int index, BuildContext context) { - if (index == 0) { - showModalBottomSheet( - context: context, - builder: (BuildContext context) => const SizedBox( - height: 270, - child: ToolsBottomSheet(), - ), - ); - } - } - - @override - Widget build(BuildContext context) { - final localizations = AppLocalizations.of(context); - return NavigationBarTheme( - data: WidgetThemes.bottomNavBarThemeData, - child: NavigationBar( - height: height, - onDestinationSelected: (index) => - _onNavigationItemSelected(index, context), - destinations: [ - NavigationDestination( - label: localizations.tools, - icon: const BottomBarIcon(asset: 'assets/svg/ic_tools.svg'), - ), - Consumer( - builder: (BuildContext context, WidgetRef ref, Widget? child) { - final ToolType currentToolType = ref.watch( - toolBoxStateProvider.select((value) => value.currentToolType), - ); - - final currentToolData = ToolData.allToolsData.firstWhere( - (toolData) => toolData.type == currentToolType, - orElse: () => ToolData.BRUSH, - ); - - return NavigationDestination( - label: currentToolData.name, - icon: BottomBarIcon(asset: currentToolData.svgAssetPath), - ); - }, - ), - NavigationDestination( - label: localizations.color, - icon: Icon( - Icons.check_box_outline_blank, - size: 24, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - NavigationDestination( - label: localizations.layers, - icon: const BottomBarIcon(asset: 'assets/svg/ic_layers.svg'), - ), - ], - ), - ); - } -} diff --git a/lib/ui/drawing_space/tool_options.dart b/lib/ui/drawing_space/tool_options.dart deleted file mode 100644 index db7c0b80..00000000 --- a/lib/ui/drawing_space/tool_options.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:paintroid/ui/drawing_space/bottom_brush_tool_options.dart'; -import 'package:paintroid/ui/drawing_space/top_brush_tool_options.dart'; - -class ToolOptions extends StatelessWidget { - const ToolOptions({super.key}); - - @override - Widget build(BuildContext context) { - return const Padding( - padding: EdgeInsets.all(8), - child: Column( - children: [ - TopBrushToolOptions(), - Spacer(), - BottomBrushToolOptions(), - ], - ), - ); - } -} diff --git a/lib/workspace/workspace.dart b/lib/workspace/workspace.dart deleted file mode 100644 index 0933385b..00000000 --- a/lib/workspace/workspace.dart +++ /dev/null @@ -1,4 +0,0 @@ -export 'src/state/workspace_state_notifier.dart'; -export 'src/ui/canvas_painter.dart'; -export 'src/ui/drawing_canvas.dart'; -export 'src/usecase/render_image_for_export.dart'; diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 00000000..1a3b743c --- /dev/null +++ b/melos.yaml @@ -0,0 +1,97 @@ +name: Paintroid-Flutter +repository: https://github.com/Catrobat/Paintroid-Flutter +sdkPath: .fvm/flutter_sdk + +packages: + - packages/* + - packages/features/* + +command: + bootstrap: + runPubGetInParallel: true + usePubspecOverrides: true + +scripts: + lint:all: + run: melos run analyze + description: Run all static analysis checks. + + analyze: + # We are setting the concurrency to 1 because a higher concurrency can crash + # the analysis server on low performance machines (like GitHub Actions). + run: | + melos exec -c 1 -- \ + dart analyze . --fatal-infos + description: | + Run `dart analyze` in all packages. + - Note: you can also rely on your IDEs Dart Analysis / Issues window. + + test:all: + run: | + melos run test --no-select + description: | + Run all tests available. + + test: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter test" + description: Run `flutter test` for a specific package. + packageFilters: + dirExists: + - test + ignore: + - "*web*" + - "*odm*" + - "*example*" + + test:unit: + run: | + melos run test-unit --no-select + description: | + Run all tests available. + + test-unit: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter test test/unit" + description: Run `flutter test test/unit` for a specific package. + packageFilters: + dirExists: + - test/unit + ignore: + - "*web*" + - "*odm*" + - "*example*" + + test:widget: + run: | + melos run test-widget --no-select + description: | + Run all tests available. + + test-widget: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter test test/widget" + description: Run `flutter test test/widget` for a specific package. + packageFilters: + dirExists: + - test/widget + ignore: + - "*web*" + - "*odm*" + - "*example*" + + build:all: + run: | + melos run build --no-select + description: | + Run all build_runners available. + + build: + # We are setting the concurrency to 1 because a higher concurrency creates dependencies problems. + run: | + melos exec -c 1 --fail-fast -- \ + "flutter pub run build_runner build --delete-conflicting-outputs" + description: Run `flutter pub run build_runner build --delete-conflicting-outputs` for a specific package. diff --git a/packages/command/.metadata b/packages/command/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/command/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/command/analysis_options.yaml b/packages/command/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/command/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/packages/command/lib/command.dart b/packages/command/lib/command.dart new file mode 100644 index 00000000..098170f7 --- /dev/null +++ b/packages/command/lib/command.dart @@ -0,0 +1,10 @@ +library command; + +export 'graphic_factory/graphic_factory.dart'; +export 'src/command_factory/command_factory.dart'; +export 'src/command_implementation/command.dart'; +export 'src/command_implementation/graphic/draw_path_command.dart'; +export 'src/command_implementation/graphic/graphic_command.dart'; +export 'src/command_manager/command_manager.dart'; +export 'src/command_manager/sync_command_manager.dart'; +export 'utils/path_with_action_history.dart'; diff --git a/packages/command/lib/command_providers.dart b/packages/command/lib/command_providers.dart new file mode 100644 index 00000000..aeb71f8d --- /dev/null +++ b/packages/command/lib/command_providers.dart @@ -0,0 +1,5 @@ +library command; + +export 'graphic_factory/graphic_factory_provider.dart'; +export 'src/command_factory/command_factory_provider.dart'; +export 'src/command_manager/command_manager_provider.dart'; diff --git a/lib/core/graphic_factory.dart b/packages/command/lib/graphic_factory/graphic_factory.dart similarity index 93% rename from lib/core/graphic_factory.dart rename to packages/command/lib/graphic_factory/graphic_factory.dart index 7be04c52..d1ddb975 100644 --- a/lib/core/graphic_factory.dart +++ b/packages/command/lib/graphic_factory/graphic_factory.dart @@ -1,6 +1,6 @@ import 'dart:ui'; -import 'package:paintroid/core/path_with_action_history.dart'; +import 'package:command/command.dart'; class GraphicFactory { const GraphicFactory(); diff --git a/lib/core/graphic_factory_provider.dart b/packages/command/lib/graphic_factory/graphic_factory_provider.dart similarity index 77% rename from lib/core/graphic_factory_provider.dart rename to packages/command/lib/graphic_factory/graphic_factory_provider.dart index ee32cac7..887ee7bb 100644 --- a/lib/core/graphic_factory_provider.dart +++ b/packages/command/lib/graphic_factory/graphic_factory_provider.dart @@ -1,4 +1,4 @@ -import 'package:paintroid/core/graphic_factory.dart'; +import 'package:command/graphic_factory/graphic_factory.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'graphic_factory_provider.g.dart'; diff --git a/packages/command/lib/graphic_factory/graphic_factory_provider.g.dart b/packages/command/lib/graphic_factory/graphic_factory_provider.g.dart new file mode 100644 index 00000000..af0d0b89 --- /dev/null +++ b/packages/command/lib/graphic_factory/graphic_factory_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'graphic_factory_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$graphicFactoryHash() => r'a32ae8670aee6a5741031a48a68bac61b3aa411a'; + +/// See also [graphicFactory]. +@ProviderFor(graphicFactory) +final graphicFactoryProvider = Provider.internal( + graphicFactory, + name: r'graphicFactoryProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$graphicFactoryHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef GraphicFactoryRef = ProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/command/src/command_factory.dart b/packages/command/lib/src/command_factory/command_factory.dart similarity index 56% rename from lib/command/src/command_factory.dart rename to packages/command/lib/src/command_factory/command_factory.dart index 7068d45e..328bce11 100644 --- a/lib/command/src/command_factory.dart +++ b/packages/command/lib/src/command_factory/command_factory.dart @@ -1,7 +1,6 @@ import 'dart:ui'; -import 'package:paintroid/command/src/implementation/command/graphic/draw_path_command.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; +import 'package:command/command.dart'; class CommandFactory { const CommandFactory(); diff --git a/lib/command/src/command_factory_provider.dart b/packages/command/lib/src/command_factory/command_factory_provider.dart similarity index 78% rename from lib/command/src/command_factory_provider.dart rename to packages/command/lib/src/command_factory/command_factory_provider.dart index d0141c74..2145599c 100644 --- a/lib/command/src/command_factory_provider.dart +++ b/packages/command/lib/src/command_factory/command_factory_provider.dart @@ -1,4 +1,4 @@ -import 'package:paintroid/command/src/command_factory.dart'; +import 'package:command/command.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'command_factory_provider.g.dart'; diff --git a/packages/command/lib/src/command_factory/command_factory_provider.g.dart b/packages/command/lib/src/command_factory/command_factory_provider.g.dart new file mode 100644 index 00000000..c851d50b --- /dev/null +++ b/packages/command/lib/src/command_factory/command_factory_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'command_factory_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$commandFactoryHash() => r'9274f1b9adb5d973abccec2e14a55c11b299f52c'; + +/// See also [commandFactory]. +@ProviderFor(commandFactory) +final commandFactoryProvider = Provider.internal( + commandFactory, + name: r'commandFactoryProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$commandFactoryHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef CommandFactoryRef = ProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/packages/command/lib/src/command_implementation/command.dart b/packages/command/lib/src/command_implementation/command.dart new file mode 100644 index 00000000..e5377362 --- /dev/null +++ b/packages/command/lib/src/command_implementation/command.dart @@ -0,0 +1,19 @@ +import 'package:command/command.dart'; +import 'package:equatable/equatable.dart'; +import 'package:io_library/io_library.dart'; + +abstract class Command with EquatableMixin { + const Command(); + + Map toJson(); + + factory Command.fromJson(Map json) { + String type = json['type'] as String; + switch (type) { + case SerializerType.DRAW_PATH_COMMAND: + return DrawPathCommand.fromJson(json); + default: + return DrawPathCommand.fromJson(json); + } + } +} diff --git a/packages/command/lib/src/command_implementation/graphic/draw_path_command.dart b/packages/command/lib/src/command_implementation/graphic/draw_path_command.dart new file mode 100644 index 00000000..9b715d4c --- /dev/null +++ b/packages/command/lib/src/command_implementation/graphic/draw_path_command.dart @@ -0,0 +1,51 @@ +import 'dart:ui'; + +import 'package:command/command.dart'; +import 'package:flutter/widgets.dart'; +import 'package:io_library/io_library.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'draw_path_command.g.dart'; + +@JsonSerializable() +class DrawPathCommand extends GraphicCommand { + final String type; + final int version; + + DrawPathCommand( + this.path, + super.paint, { + this.type = SerializerType.DRAW_PATH_COMMAND, + int? version, + }) : version = version ?? + VersionStrategyManager.strategy.getDrawPathCommandVersion(); + + @PathWithActionHistoryConverter() + final PathWithActionHistory path; + + @override + void call(Canvas canvas) { + canvas.drawPath(path, paint); + } + + @override + List get props => [paint, path, type]; + + @override + Map toJson() => _$DrawPathCommandToJson(this); + + factory DrawPathCommand.fromJson(Map json) { + int version = json['version'] as int; + + switch (version) { + case Version.v1: + return _$DrawPathCommandFromJson(json); + case Version.v2: + // For different versions of DrawPathCommand the deserialization + // has to be implemented manually. + // Autogenerated code can only be used for one version + default: + return _$DrawPathCommandFromJson(json); + } + } +} diff --git a/packages/command/lib/src/command_implementation/graphic/draw_path_command.g.dart b/packages/command/lib/src/command_implementation/graphic/draw_path_command.g.dart new file mode 100644 index 00000000..5d76772c --- /dev/null +++ b/packages/command/lib/src/command_implementation/graphic/draw_path_command.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'draw_path_command.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DrawPathCommand _$DrawPathCommandFromJson(Map json) => + DrawPathCommand( + const PathWithActionHistoryConverter() + .fromJson(json['path'] as Map), + const PaintConverter().fromJson(json['paint'] as Map), + type: json['type'] as String? ?? SerializerType.DRAW_PATH_COMMAND, + version: json['version'] as int?, + ); + +Map _$DrawPathCommandToJson(DrawPathCommand instance) => + { + 'paint': const PaintConverter().toJson(instance.paint), + 'type': instance.type, + 'version': instance.version, + 'path': const PathWithActionHistoryConverter().toJson(instance.path), + }; diff --git a/lib/command/src/graphic_command.dart b/packages/command/lib/src/command_implementation/graphic/graphic_command.dart similarity index 60% rename from lib/command/src/graphic_command.dart rename to packages/command/lib/src/command_implementation/graphic/graphic_command.dart index ded772e0..20108abc 100644 --- a/lib/command/src/graphic_command.dart +++ b/packages/command/lib/src/command_implementation/graphic/graphic_command.dart @@ -1,10 +1,12 @@ import 'dart:ui'; -import 'package:paintroid/command/src/command.dart'; +import 'package:command/command.dart'; +import 'package:io_library/io_library.dart'; abstract class GraphicCommand extends Command { const GraphicCommand(this.paint); + @PaintConverter() final Paint paint; void call(Canvas canvas); diff --git a/lib/command/src/command_manager.dart b/packages/command/lib/src/command_manager/command_manager.dart similarity index 74% rename from lib/command/src/command_manager.dart rename to packages/command/lib/src/command_manager/command_manager.dart index c12856cc..e854432b 100644 --- a/lib/command/src/command_manager.dart +++ b/packages/command/lib/src/command_manager/command_manager.dart @@ -1,7 +1,6 @@ import 'dart:ui'; -import 'package:paintroid/command/src/command.dart'; -import 'package:paintroid/command/src/graphic_command.dart'; +import 'package:command/command.dart'; abstract class CommandManager { Iterable get history; diff --git a/lib/command/src/command_manager_provider.dart b/packages/command/lib/src/command_manager/command_manager_provider.dart similarity index 60% rename from lib/command/src/command_manager_provider.dart rename to packages/command/lib/src/command_manager/command_manager_provider.dart index 66a96499..f9b73082 100644 --- a/lib/command/src/command_manager_provider.dart +++ b/packages/command/lib/src/command_manager/command_manager_provider.dart @@ -1,5 +1,4 @@ -import 'package:paintroid/command/src/command_manager.dart'; -import 'package:paintroid/command/src/implementation/manager/sync_command_manager.dart'; +import 'package:command/command.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'command_manager_provider.g.dart'; diff --git a/packages/command/lib/src/command_manager/command_manager_provider.g.dart b/packages/command/lib/src/command_manager/command_manager_provider.g.dart new file mode 100644 index 00000000..528fd2be --- /dev/null +++ b/packages/command/lib/src/command_manager/command_manager_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'command_manager_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$commandManagerHash() => r'cd76374b9414ebebac1eead0d28106cb9fcd4eea'; + +/// See also [commandManager]. +@ProviderFor(commandManager) +final commandManagerProvider = Provider.internal( + commandManager, + name: r'commandManagerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$commandManagerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef CommandManagerRef = ProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/command/src/implementation/manager/sync_command_manager.dart b/packages/command/lib/src/command_manager/sync_command_manager.dart similarity index 85% rename from lib/command/src/implementation/manager/sync_command_manager.dart rename to packages/command/lib/src/command_manager/sync_command_manager.dart index 963ce240..9c060087 100644 --- a/lib/command/src/implementation/manager/sync_command_manager.dart +++ b/packages/command/lib/src/command_manager/sync_command_manager.dart @@ -1,8 +1,6 @@ import 'dart:ui'; -import 'package:paintroid/command/src/command.dart'; -import 'package:paintroid/command/src/command_manager.dart'; -import 'package:paintroid/command/src/graphic_command.dart'; +import 'package:command/command.dart'; class SyncCommandManager implements CommandManager { SyncCommandManager({required List commands}) : _history = commands; diff --git a/packages/command/lib/utils/path_with_action_history.dart b/packages/command/lib/utils/path_with_action_history.dart new file mode 100644 index 00000000..e5657dde --- /dev/null +++ b/packages/command/lib/utils/path_with_action_history.dart @@ -0,0 +1,92 @@ +import 'dart:ui'; + +import 'package:collection/collection.dart'; +import 'package:io_library/io_library.dart'; + +class PathWithActionHistory extends Path { + PathWithActionHistory(); + + @PathActionConverter() + final actions = []; + + @override + void moveTo(double x, double y) { + actions.add(MoveToAction(x, y)); + super.moveTo(x, y); + } + + @override + void lineTo(double x, double y) { + actions.add(LineToAction(x, y)); + super.lineTo(x, y); + } + + @override + void close() { + actions.add(const CloseAction()); + super.close(); + } + + Map toJson() { + return const PathWithActionHistoryConverter().toJson(this); + } + + factory PathWithActionHistory.fromJson(Map json) { + return const PathWithActionHistoryConverter().fromJson(json); + } + + @override + bool operator ==(Object other) { + if (other is PathWithActionHistory) { + return const ListEquality().equals(actions, other.actions); + } + return false; + } + + @override + int get hashCode => const ListEquality().hash(actions); +} + +abstract class PathAction { + const PathAction(); +} + +class MoveToAction extends PathAction { + final double x; + final double y; + + const MoveToAction(this.x, this.y); + + @override + bool operator ==(Object other) { + if (other is MoveToAction) { + return x == other.x && y == other.y; + } + return false; + } + + @override + int get hashCode => Object.hash(x, y); +} + +class LineToAction extends PathAction { + final double x; + final double y; + + const LineToAction(this.x, this.y); + + @override + bool operator ==(Object other) { + if (other is LineToAction) { + return x == other.x && y == other.y; + } + return false; + } + + @override + int get hashCode => Object.hash(x, y); +} + +class CloseAction extends PathAction { + const CloseAction(); +} diff --git a/packages/command/pubspec.yaml b/packages/command/pubspec.yaml new file mode 100644 index 00000000..04fd7856 --- /dev/null +++ b/packages/command/pubspec.yaml @@ -0,0 +1,42 @@ +name: command +description: Internal package for commands. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + equatable: ^2.0.3 + json_annotation: ^4.8.1 + collection: ^1.17.1 + + + # Internal packages + component_library: + path: ../component_library + io_library: + path: ../io_library + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + json_serializable: ^6.7.1 + +flutter: + uses-material-design: true diff --git a/test/unit/command/command_factory_test.dart b/packages/command/test/unit/command_factory_test.dart similarity index 83% rename from test/unit/command/command_factory_test.dart rename to packages/command/test/unit/command_factory_test.dart index 64173bbd..b8af3440 100644 --- a/test/unit/command/command_factory_test.dart +++ b/packages/command/test/unit/command_factory_test.dart @@ -1,8 +1,7 @@ import 'dart:ui'; +import 'package:command/command.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; void main() { late PathWithActionHistory testPath; diff --git a/test/unit/command/draw_path_command_test.dart b/packages/command/test/unit/draw_path_command_test.dart similarity index 87% rename from test/unit/command/draw_path_command_test.dart rename to packages/command/test/unit/draw_path_command_test.dart index 0e15859d..4d34c156 100644 --- a/test/unit/command/draw_path_command_test.dart +++ b/packages/command/test/unit/draw_path_command_test.dart @@ -1,10 +1,9 @@ import 'dart:ui'; +import 'package:command/command.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; import 'draw_path_command_test.mocks.dart'; diff --git a/packages/command/test/unit/draw_path_command_test.mocks.dart b/packages/command/test/unit/draw_path_command_test.mocks.dart new file mode 100644 index 00000000..28d796c0 --- /dev/null +++ b/packages/command/test/unit/draw_path_command_test.mocks.dart @@ -0,0 +1,616 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in command/test/unit/draw_path_command_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:typed_data' as _i3; +import 'dart:ui' as _i2; + +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeRect_0 extends _i1.SmartFake implements _i2.Rect { + _FakeRect_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [Canvas]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCanvas extends _i1.Mock implements _i2.Canvas { + MockCanvas() { + _i1.throwOnMissingStub(this); + } + + @override + void save() => super.noSuchMethod( + Invocation.method( + #save, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void saveLayer( + _i2.Rect? bounds, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #saveLayer, + [ + bounds, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void restore() => super.noSuchMethod( + Invocation.method( + #restore, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void restoreToCount(int? count) => super.noSuchMethod( + Invocation.method( + #restoreToCount, + [count], + ), + returnValueForMissingStub: null, + ); + + @override + int getSaveCount() => (super.noSuchMethod( + Invocation.method( + #getSaveCount, + [], + ), + returnValue: 0, + ) as int); + + @override + void translate( + double? dx, + double? dy, + ) => + super.noSuchMethod( + Invocation.method( + #translate, + [ + dx, + dy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void scale( + double? sx, [ + double? sy, + ]) => + super.noSuchMethod( + Invocation.method( + #scale, + [ + sx, + sy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void rotate(double? radians) => super.noSuchMethod( + Invocation.method( + #rotate, + [radians], + ), + returnValueForMissingStub: null, + ); + + @override + void skew( + double? sx, + double? sy, + ) => + super.noSuchMethod( + Invocation.method( + #skew, + [ + sx, + sy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void transform(_i3.Float64List? matrix4) => super.noSuchMethod( + Invocation.method( + #transform, + [matrix4], + ), + returnValueForMissingStub: null, + ); + + @override + _i3.Float64List getTransform() => (super.noSuchMethod( + Invocation.method( + #getTransform, + [], + ), + returnValue: _i3.Float64List(0), + ) as _i3.Float64List); + + @override + void clipRect( + _i2.Rect? rect, { + _i2.ClipOp? clipOp = _i2.ClipOp.intersect, + bool? doAntiAlias = true, + }) => + super.noSuchMethod( + Invocation.method( + #clipRect, + [rect], + { + #clipOp: clipOp, + #doAntiAlias: doAntiAlias, + }, + ), + returnValueForMissingStub: null, + ); + + @override + void clipRRect( + _i2.RRect? rrect, { + bool? doAntiAlias = true, + }) => + super.noSuchMethod( + Invocation.method( + #clipRRect, + [rrect], + {#doAntiAlias: doAntiAlias}, + ), + returnValueForMissingStub: null, + ); + + @override + void clipPath( + _i2.Path? path, { + bool? doAntiAlias = true, + }) => + super.noSuchMethod( + Invocation.method( + #clipPath, + [path], + {#doAntiAlias: doAntiAlias}, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.Rect getLocalClipBounds() => (super.noSuchMethod( + Invocation.method( + #getLocalClipBounds, + [], + ), + returnValue: _FakeRect_0( + this, + Invocation.method( + #getLocalClipBounds, + [], + ), + ), + ) as _i2.Rect); + + @override + _i2.Rect getDestinationClipBounds() => (super.noSuchMethod( + Invocation.method( + #getDestinationClipBounds, + [], + ), + returnValue: _FakeRect_0( + this, + Invocation.method( + #getDestinationClipBounds, + [], + ), + ), + ) as _i2.Rect); + + @override + void drawColor( + _i2.Color? color, + _i2.BlendMode? blendMode, + ) => + super.noSuchMethod( + Invocation.method( + #drawColor, + [ + color, + blendMode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawLine( + _i2.Offset? p1, + _i2.Offset? p2, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawLine, + [ + p1, + p2, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPaint(_i2.Paint? paint) => super.noSuchMethod( + Invocation.method( + #drawPaint, + [paint], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRect( + _i2.Rect? rect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRect, + [ + rect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRRect( + _i2.RRect? rrect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRRect, + [ + rrect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawDRRect( + _i2.RRect? outer, + _i2.RRect? inner, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawDRRect, + [ + outer, + inner, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawOval( + _i2.Rect? rect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawOval, + [ + rect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawCircle( + _i2.Offset? c, + double? radius, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawCircle, + [ + c, + radius, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawArc( + _i2.Rect? rect, + double? startAngle, + double? sweepAngle, + bool? useCenter, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawArc, + [ + rect, + startAngle, + sweepAngle, + useCenter, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPath( + _i2.Path? path, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawPath, + [ + path, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawImage( + _i2.Image? image, + _i2.Offset? offset, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawImage, + [ + image, + offset, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawImageRect( + _i2.Image? image, + _i2.Rect? src, + _i2.Rect? dst, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawImageRect, + [ + image, + src, + dst, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawImageNine( + _i2.Image? image, + _i2.Rect? center, + _i2.Rect? dst, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawImageNine, + [ + image, + center, + dst, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPicture(_i2.Picture? picture) => super.noSuchMethod( + Invocation.method( + #drawPicture, + [picture], + ), + returnValueForMissingStub: null, + ); + + @override + void drawParagraph( + _i2.Paragraph? paragraph, + _i2.Offset? offset, + ) => + super.noSuchMethod( + Invocation.method( + #drawParagraph, + [ + paragraph, + offset, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPoints( + _i2.PointMode? pointMode, + List<_i2.Offset>? points, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawPoints, + [ + pointMode, + points, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRawPoints( + _i2.PointMode? pointMode, + _i3.Float32List? points, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRawPoints, + [ + pointMode, + points, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawVertices( + _i2.Vertices? vertices, + _i2.BlendMode? blendMode, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawVertices, + [ + vertices, + blendMode, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawAtlas( + _i2.Image? atlas, + List<_i2.RSTransform>? transforms, + List<_i2.Rect>? rects, + List<_i2.Color>? colors, + _i2.BlendMode? blendMode, + _i2.Rect? cullRect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawAtlas, + [ + atlas, + transforms, + rects, + colors, + blendMode, + cullRect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRawAtlas( + _i2.Image? atlas, + _i3.Float32List? rstTransforms, + _i3.Float32List? rects, + _i3.Int32List? colors, + _i2.BlendMode? blendMode, + _i2.Rect? cullRect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRawAtlas, + [ + atlas, + rstTransforms, + rects, + colors, + blendMode, + cullRect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawShadow( + _i2.Path? path, + _i2.Color? color, + double? elevation, + bool? transparentOccluder, + ) => + super.noSuchMethod( + Invocation.method( + #drawShadow, + [ + path, + color, + elevation, + transparentOccluder, + ], + ), + returnValueForMissingStub: null, + ); +} diff --git a/packages/component_library/.metadata b/packages/component_library/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/component_library/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/component_library/analysis_options.yaml b/packages/component_library/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/component_library/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/assets/img/checkerboard.png b/packages/component_library/assets/img/checkerboard.png similarity index 100% rename from assets/img/checkerboard.png rename to packages/component_library/assets/img/checkerboard.png diff --git a/assets/icon/pocketpaint_intro_landscape.png b/packages/component_library/assets/img/pocketpaint_intro_landscape.png similarity index 100% rename from assets/icon/pocketpaint_intro_landscape.png rename to packages/component_library/assets/img/pocketpaint_intro_landscape.png diff --git a/assets/icon/pocketpaint_intro_portrait.png b/packages/component_library/assets/img/pocketpaint_intro_portrait.png similarity index 100% rename from assets/icon/pocketpaint_intro_portrait.png rename to packages/component_library/assets/img/pocketpaint_intro_portrait.png diff --git a/assets/icon/pocketpaint_logo_small.png b/packages/component_library/assets/img/pocketpaint_logo_small.png similarity index 100% rename from assets/icon/pocketpaint_logo_small.png rename to packages/component_library/assets/img/pocketpaint_logo_small.png diff --git a/assets/svg/ic_brush.svg b/packages/component_library/assets/svg/ic_brush.svg similarity index 100% rename from assets/svg/ic_brush.svg rename to packages/component_library/assets/svg/ic_brush.svg diff --git a/assets/svg/ic_clipboard.svg b/packages/component_library/assets/svg/ic_clipboard.svg similarity index 100% rename from assets/svg/ic_clipboard.svg rename to packages/component_library/assets/svg/ic_clipboard.svg diff --git a/assets/svg/ic_clipping.svg b/packages/component_library/assets/svg/ic_clipping.svg similarity index 100% rename from assets/svg/ic_clipping.svg rename to packages/component_library/assets/svg/ic_clipping.svg diff --git a/assets/svg/ic_cursor.svg b/packages/component_library/assets/svg/ic_cursor.svg similarity index 100% rename from assets/svg/ic_cursor.svg rename to packages/component_library/assets/svg/ic_cursor.svg diff --git a/assets/svg/ic_edit_circle.svg b/packages/component_library/assets/svg/ic_edit_circle.svg similarity index 100% rename from assets/svg/ic_edit_circle.svg rename to packages/component_library/assets/svg/ic_edit_circle.svg diff --git a/assets/svg/ic_eraser.svg b/packages/component_library/assets/svg/ic_eraser.svg similarity index 100% rename from assets/svg/ic_eraser.svg rename to packages/component_library/assets/svg/ic_eraser.svg diff --git a/assets/svg/ic_fill.svg b/packages/component_library/assets/svg/ic_fill.svg similarity index 100% rename from assets/svg/ic_fill.svg rename to packages/component_library/assets/svg/ic_fill.svg diff --git a/assets/svg/ic_hand.svg b/packages/component_library/assets/svg/ic_hand.svg similarity index 100% rename from assets/svg/ic_hand.svg rename to packages/component_library/assets/svg/ic_hand.svg diff --git a/assets/svg/ic_import.svg b/packages/component_library/assets/svg/ic_import.svg similarity index 100% rename from assets/svg/ic_import.svg rename to packages/component_library/assets/svg/ic_import.svg diff --git a/assets/svg/ic_layers.svg b/packages/component_library/assets/svg/ic_layers.svg similarity index 100% rename from assets/svg/ic_layers.svg rename to packages/component_library/assets/svg/ic_layers.svg diff --git a/assets/svg/ic_line.svg b/packages/component_library/assets/svg/ic_line.svg similarity index 100% rename from assets/svg/ic_line.svg rename to packages/component_library/assets/svg/ic_line.svg diff --git a/assets/svg/ic_pipette.svg b/packages/component_library/assets/svg/ic_pipette.svg similarity index 100% rename from assets/svg/ic_pipette.svg rename to packages/component_library/assets/svg/ic_pipette.svg diff --git a/assets/svg/ic_shapes.svg b/packages/component_library/assets/svg/ic_shapes.svg similarity index 100% rename from assets/svg/ic_shapes.svg rename to packages/component_library/assets/svg/ic_shapes.svg diff --git a/assets/svg/ic_smudge.svg b/packages/component_library/assets/svg/ic_smudge.svg similarity index 100% rename from assets/svg/ic_smudge.svg rename to packages/component_library/assets/svg/ic_smudge.svg diff --git a/assets/svg/ic_spray_can.svg b/packages/component_library/assets/svg/ic_spray_can.svg similarity index 100% rename from assets/svg/ic_spray_can.svg rename to packages/component_library/assets/svg/ic_spray_can.svg diff --git a/assets/svg/ic_stamp.svg b/packages/component_library/assets/svg/ic_stamp.svg similarity index 100% rename from assets/svg/ic_stamp.svg rename to packages/component_library/assets/svg/ic_stamp.svg diff --git a/assets/svg/ic_text.svg b/packages/component_library/assets/svg/ic_text.svg similarity index 100% rename from assets/svg/ic_text.svg rename to packages/component_library/assets/svg/ic_text.svg diff --git a/assets/svg/ic_tools.svg b/packages/component_library/assets/svg/ic_tools.svg similarity index 100% rename from assets/svg/ic_tools.svg rename to packages/component_library/assets/svg/ic_tools.svg diff --git a/assets/svg/ic_transform.svg b/packages/component_library/assets/svg/ic_transform.svg similarity index 100% rename from assets/svg/ic_transform.svg rename to packages/component_library/assets/svg/ic_transform.svg diff --git a/assets/svg/ic_watercolor.svg b/packages/component_library/assets/svg/ic_watercolor.svg similarity index 100% rename from assets/svg/ic_watercolor.svg rename to packages/component_library/assets/svg/ic_watercolor.svg diff --git a/packages/component_library/lib/component_library.dart b/packages/component_library/lib/component_library.dart new file mode 100644 index 00000000..f3c31da0 --- /dev/null +++ b/packages/component_library/lib/component_library.dart @@ -0,0 +1,13 @@ +library component_library; + +export 'src/components/bottom_nav_bar_icon.dart'; +export 'src/components/custom_action_chip.dart'; +export 'src/components/icon_button_with_label.dart'; +export 'src/components/icon_svg.dart'; +export 'src/components/imgs.dart'; +export 'src/components/loading_overlay.dart'; +export 'src/components/pop_menu_button.dart'; +export 'src/theme/color_schemes.dart'; +export 'src/theme/styles.dart'; +export 'src/utils/open_url.dart'; +export 'src/utils/toast_utils.dart'; diff --git a/lib/ui/shared/bottom_nav_bar_icon.dart b/packages/component_library/lib/src/components/bottom_nav_bar_icon.dart similarity index 68% rename from lib/ui/shared/bottom_nav_bar_icon.dart rename to packages/component_library/lib/src/components/bottom_nav_bar_icon.dart index 9568549d..e35205f1 100644 --- a/lib/ui/shared/bottom_nav_bar_icon.dart +++ b/packages/component_library/lib/src/components/bottom_nav_bar_icon.dart @@ -1,5 +1,5 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; class BottomBarIcon extends StatelessWidget { final String asset; @@ -8,9 +8,10 @@ class BottomBarIcon extends StatelessWidget { @override Widget build(BuildContext context) { - return SvgPicture.asset( - asset, - height: 24, + return IconSvg( + path: asset, + height: 24.0, + width: 24.0, color: Theme.of(context).colorScheme.onSurface, ); } diff --git a/lib/ui/shared/custom_action_chip.dart b/packages/component_library/lib/src/components/custom_action_chip.dart similarity index 93% rename from lib/ui/shared/custom_action_chip.dart rename to packages/component_library/lib/src/components/custom_action_chip.dart index 4642e2aa..697df41a 100644 --- a/lib/ui/shared/custom_action_chip.dart +++ b/packages/component_library/lib/src/components/custom_action_chip.dart @@ -7,12 +7,14 @@ class CustomActionChip extends StatelessWidget { final Color chipBackgroundColor; final EdgeInsetsGeometry? padding; final MaterialTapTargetSize? materialTapTargetSize; + final String hint; const CustomActionChip({ super.key, required this.chipIcon, required this.onPressed, required this.chipBackgroundColor, + required this.hint, this.shape, this.padding, this.materialTapTargetSize, @@ -21,6 +23,7 @@ class CustomActionChip extends StatelessWidget { @override Widget build(BuildContext context) { return ActionChip( + tooltip: hint, label: chipIcon, onPressed: onPressed, shape: shape ?? diff --git a/lib/ui/shared/icon_button_with_label.dart b/packages/component_library/lib/src/components/icon_button_with_label.dart similarity index 100% rename from lib/ui/shared/icon_button_with_label.dart rename to packages/component_library/lib/src/components/icon_button_with_label.dart diff --git a/packages/component_library/lib/src/components/icon_svg.dart b/packages/component_library/lib/src/components/icon_svg.dart new file mode 100644 index 00000000..80e6648d --- /dev/null +++ b/packages/component_library/lib/src/components/icon_svg.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class IconSvg extends StatelessWidget { + final String path; + final double height; + final double width; + final Color? color; + + const IconSvg({ + Key? key, + required this.path, + required this.height, + required this.width, + this.color, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + 'packages/component_library/$path', + height: height, + width: width, + color: color, + ); + } +} diff --git a/packages/component_library/lib/src/components/imgs.dart b/packages/component_library/lib/src/components/imgs.dart new file mode 100644 index 00000000..16421d92 --- /dev/null +++ b/packages/component_library/lib/src/components/imgs.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +class CheckerboardImg extends StatelessWidget { + const CheckerboardImg({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + child: Image.asset( + 'packages/component_library/assets/img/checkerboard.png', + repeat: ImageRepeat.repeat, + cacheWidth: 50, + cacheHeight: 50, + filterQuality: FilterQuality.none, + ), + ); + } +} + +class PocketPaintIntroLandscape extends StatelessWidget { + const PocketPaintIntroLandscape({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + child: Image.asset( + 'packages/component_library/assets/img/pocketpaint_intro_landscape.png', + fit: BoxFit.contain, + ), + ); + } +} + +class PocketPaintIntroPortrait extends StatelessWidget { + const PocketPaintIntroPortrait({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + child: Image.asset( + 'packages/component_library/assets/img/pocketpaint_intro_portrait.png', + fit: BoxFit.fitHeight, + ), + ); + } +} + +class PocketPaintLogoSmall extends StatelessWidget { + const PocketPaintLogoSmall({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + child: Image.asset( + 'packages/component_library/assets/img/pocketpaint_logo_small.png', + ), + ); + } +} diff --git a/lib/ui/shared/loading_overlay.dart b/packages/component_library/lib/src/components/loading_overlay.dart similarity index 100% rename from lib/ui/shared/loading_overlay.dart rename to packages/component_library/lib/src/components/loading_overlay.dart diff --git a/lib/ui/pop_menu_button.dart b/packages/component_library/lib/src/components/pop_menu_button.dart similarity index 100% rename from lib/ui/pop_menu_button.dart rename to packages/component_library/lib/src/components/pop_menu_button.dart diff --git a/lib/ui/color_schemes.dart b/packages/component_library/lib/src/theme/color_schemes.dart similarity index 100% rename from lib/ui/color_schemes.dart rename to packages/component_library/lib/src/theme/color_schemes.dart diff --git a/lib/ui/styles.dart b/packages/component_library/lib/src/theme/styles.dart similarity index 92% rename from lib/ui/styles.dart rename to packages/component_library/lib/src/theme/styles.dart index 94a76673..2e822183 100644 --- a/lib/ui/styles.dart +++ b/packages/component_library/lib/src/theme/styles.dart @@ -1,5 +1,5 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/color_schemes.dart'; abstract class TextThemes { static TextStyle menuItem = TextStyle( diff --git a/lib/ui/util.dart b/packages/component_library/lib/src/utils/open_url.dart similarity index 100% rename from lib/ui/util.dart rename to packages/component_library/lib/src/utils/open_url.dart diff --git a/lib/core/toast_utils.dart b/packages/component_library/lib/src/utils/toast_utils.dart similarity index 100% rename from lib/core/toast_utils.dart rename to packages/component_library/lib/src/utils/toast_utils.dart diff --git a/packages/component_library/pubspec.yaml b/packages/component_library/pubspec.yaml new file mode 100644 index 00000000..15a11017 --- /dev/null +++ b/packages/component_library/pubspec.yaml @@ -0,0 +1,43 @@ +name: component_library +description: Internal package for shared coponents. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + freezed_annotation: ^2.4.1 + + flutter_svg: ^1.1.0 + url_launcher: ^6.1.5 + toast: ^0.3.0 + logging: ^1.0.2 + + collection: ^1.17.1 + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + +flutter: + uses-material-design: true + + assets: + - assets/img/ + - assets/svg/ diff --git a/packages/database/.metadata b/packages/database/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/database/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/database/analysis_options.yaml b/packages/database/analysis_options.yaml new file mode 100644 index 00000000..7e11cc27 --- /dev/null +++ b/packages/database/analysis_options.yaml @@ -0,0 +1,24 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart + - lib/src/*.g.dart diff --git a/packages/database/lib/database.dart b/packages/database/lib/database.dart new file mode 100644 index 00000000..4fba40d5 --- /dev/null +++ b/packages/database/lib/database.dart @@ -0,0 +1,8 @@ +library database; + +export 'src/models/project.dart'; + +export 'src/utils/date_time_converter.dart'; + +export 'src/project_dao.dart'; +export 'src/project_database.dart'; diff --git a/lib/data/model/project.dart b/packages/database/lib/src/models/project.dart similarity index 100% rename from lib/data/model/project.dart rename to packages/database/lib/src/models/project.dart diff --git a/lib/data/project_dao.dart b/packages/database/lib/src/project_dao.dart similarity index 92% rename from lib/data/project_dao.dart rename to packages/database/lib/src/project_dao.dart index ca60fb07..0f173cca 100644 --- a/lib/data/project_dao.dart +++ b/packages/database/lib/src/project_dao.dart @@ -1,5 +1,5 @@ +import 'package:database/src/models/project.dart'; import 'package:floor/floor.dart'; -import 'package:paintroid/data/model/project.dart'; @dao abstract class ProjectDAO { diff --git a/lib/data/project_database.dart b/packages/database/lib/src/project_database.dart similarity index 73% rename from lib/data/project_database.dart rename to packages/database/lib/src/project_database.dart index 15c27544..bfde21c2 100644 --- a/lib/data/project_database.dart +++ b/packages/database/lib/src/project_database.dart @@ -1,10 +1,11 @@ import 'dart:async'; +import 'package:database/src/models/project.dart'; +import 'package:database/src/project_dao.dart'; +import 'package:database/src/utils/date_time_converter.dart'; import 'package:floor/floor.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/data/project_dao.dart'; -import 'package:paintroid/data/typeconverters/date_time_converter.dart'; + import 'package:sqflite/sqflite.dart' as sqflite; part 'project_database.g.dart'; diff --git a/packages/database/lib/src/project_database.g.dart b/packages/database/lib/src/project_database.g.dart new file mode 100644 index 00000000..ea36ed0d --- /dev/null +++ b/packages/database/lib/src/project_database.g.dart @@ -0,0 +1,204 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'project_database.dart'; + +// ************************************************************************** +// FloorGenerator +// ************************************************************************** + +// ignore: avoid_classes_with_only_static_members +class $FloorProjectDatabase { + /// Creates a database builder for a persistent database. + /// Once a database is built, you should keep a reference to it and re-use it. + static _$ProjectDatabaseBuilder databaseBuilder(String name) => + _$ProjectDatabaseBuilder(name); + + /// Creates a database builder for an in memory database. + /// Information stored in an in memory database disappears when the process is killed. + /// Once a database is built, you should keep a reference to it and re-use it. + static _$ProjectDatabaseBuilder inMemoryDatabaseBuilder() => + _$ProjectDatabaseBuilder(null); +} + +class _$ProjectDatabaseBuilder { + _$ProjectDatabaseBuilder(this.name); + + final String? name; + + final List _migrations = []; + + Callback? _callback; + + /// Adds migrations to the builder. + _$ProjectDatabaseBuilder addMigrations(List migrations) { + _migrations.addAll(migrations); + return this; + } + + /// Adds a database [Callback] to the builder. + _$ProjectDatabaseBuilder addCallback(Callback callback) { + _callback = callback; + return this; + } + + /// Creates the database and initializes it. + Future build() async { + final path = name != null + ? await sqfliteDatabaseFactory.getDatabasePath(name!) + : ':memory:'; + final database = _$ProjectDatabase(); + database.database = await database.open( + path, + _migrations, + _callback, + ); + return database; + } +} + +class _$ProjectDatabase extends ProjectDatabase { + _$ProjectDatabase([StreamController? listener]) { + changeListener = listener ?? StreamController.broadcast(); + } + + ProjectDAO? _projectDAOInstance; + + Future open( + String path, + List migrations, [ + Callback? callback, + ]) async { + final databaseOptions = sqflite.OpenDatabaseOptions( + version: 1, + onConfigure: (database) async { + await database.execute('PRAGMA foreign_keys = ON'); + await callback?.onConfigure?.call(database); + }, + onOpen: (database) async { + await callback?.onOpen?.call(database); + }, + onUpgrade: (database, startVersion, endVersion) async { + await MigrationAdapter.runMigrations( + database, startVersion, endVersion, migrations); + + await callback?.onUpgrade?.call(database, startVersion, endVersion); + }, + onCreate: (database, version) async { + await database.execute( + 'CREATE TABLE IF NOT EXISTS `Project` (`name` TEXT NOT NULL, `path` TEXT NOT NULL, `lastModified` INTEGER NOT NULL, `creationDate` INTEGER NOT NULL, `resolution` TEXT, `format` TEXT, `size` INTEGER, `imagePreviewPath` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)'); + + await callback?.onCreate?.call(database, version); + }, + ); + return sqfliteDatabaseFactory.openDatabase(path, options: databaseOptions); + } + + @override + ProjectDAO get projectDAO { + return _projectDAOInstance ??= _$ProjectDAO(database, changeListener); + } +} + +class _$ProjectDAO extends ProjectDAO { + _$ProjectDAO( + this.database, + this.changeListener, + ) : _queryAdapter = QueryAdapter(database), + _projectInsertionAdapter = InsertionAdapter( + database, + 'Project', + (Project item) => { + 'name': item.name, + 'path': item.path, + 'lastModified': _dateTimeConverter.encode(item.lastModified), + 'creationDate': _dateTimeConverter.encode(item.creationDate), + 'resolution': item.resolution, + 'format': item.format, + 'size': item.size, + 'imagePreviewPath': item.imagePreviewPath, + 'id': item.id + }), + _projectDeletionAdapter = DeletionAdapter( + database, + 'Project', + ['id'], + (Project item) => { + 'name': item.name, + 'path': item.path, + 'lastModified': _dateTimeConverter.encode(item.lastModified), + 'creationDate': _dateTimeConverter.encode(item.creationDate), + 'resolution': item.resolution, + 'format': item.format, + 'size': item.size, + 'imagePreviewPath': item.imagePreviewPath, + 'id': item.id + }); + + final sqflite.DatabaseExecutor database; + + final StreamController changeListener; + + final QueryAdapter _queryAdapter; + + final InsertionAdapter _projectInsertionAdapter; + + final DeletionAdapter _projectDeletionAdapter; + + @override + Future deleteProject(int id) async { + await _queryAdapter + .queryNoReturn('DELETE FROM Project WHERE id = ?1', arguments: [id]); + } + + @override + Future> getProjects() async { + return _queryAdapter.queryList( + 'SELECT * FROM Project order by lastModified desc', + mapper: (Map row) => Project( + name: row['name'] as String, + path: row['path'] as String, + lastModified: _dateTimeConverter.decode(row['lastModified'] as int), + creationDate: _dateTimeConverter.decode(row['creationDate'] as int), + resolution: row['resolution'] as String?, + format: row['format'] as String?, + size: row['size'] as int?, + imagePreviewPath: row['imagePreviewPath'] as String?, + id: row['id'] as int?)); + } + + @override + Future getProjectByName(String name) async { + return _queryAdapter.query('SELECT * FROM Project WHERE name = ?1', + mapper: (Map row) => Project( + name: row['name'] as String, + path: row['path'] as String, + lastModified: _dateTimeConverter.decode(row['lastModified'] as int), + creationDate: _dateTimeConverter.decode(row['creationDate'] as int), + resolution: row['resolution'] as String?, + format: row['format'] as String?, + size: row['size'] as int?, + imagePreviewPath: row['imagePreviewPath'] as String?, + id: row['id'] as int?), + arguments: [name]); + } + + @override + Future insertProject(Project project) { + return _projectInsertionAdapter.insertAndReturnId( + project, OnConflictStrategy.replace); + } + + @override + Future> insertProjects(List projects) { + return _projectInsertionAdapter.insertListAndReturnIds( + projects, OnConflictStrategy.replace); + } + + @override + Future deleteProjects(List projects) async { + await _projectDeletionAdapter.deleteList(projects); + } +} + +// ignore_for_file: unused_element +final _dateTimeConverter = DateTimeConverter(); diff --git a/lib/data/typeconverters/date_time_converter.dart b/packages/database/lib/src/utils/date_time_converter.dart similarity index 100% rename from lib/data/typeconverters/date_time_converter.dart rename to packages/database/lib/src/utils/date_time_converter.dart diff --git a/packages/database/pubspec.yaml b/packages/database/pubspec.yaml new file mode 100644 index 00000000..ebe35ad8 --- /dev/null +++ b/packages/database/pubspec.yaml @@ -0,0 +1,35 @@ +name: database +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + freezed_annotation: ^2.4.1 + + floor: ^1.2.0 + sqflite: + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + +flutter: + uses-material-design: true diff --git a/test/unit/data/project_database_test.dart b/packages/database/test/unit/project_database_test.dart similarity index 95% rename from test/unit/data/project_database_test.dart rename to packages/database/test/unit/project_database_test.dart index 0274e171..dc00547a 100644 --- a/test/unit/data/project_database_test.dart +++ b/packages/database/test/unit/project_database_test.dart @@ -1,8 +1,6 @@ +import 'package:database/database.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/data/project_database.dart'; -import 'package:paintroid/data/typeconverters/date_time_converter.dart'; void main() async { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/packages/features/landing_page_screen/.metadata b/packages/features/landing_page_screen/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/features/landing_page_screen/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/features/landing_page_screen/analysis_options.yaml b/packages/features/landing_page_screen/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/features/landing_page_screen/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/packages/features/landing_page_screen/lib/landing_page_screen.dart b/packages/features/landing_page_screen/lib/landing_page_screen.dart new file mode 100644 index 00000000..51869a2e --- /dev/null +++ b/packages/features/landing_page_screen/lib/landing_page_screen.dart @@ -0,0 +1,9 @@ +library landing_page_screen; + +export 'src/components/custom_action_button.dart'; +export 'src/components/image_preview.dart'; +export 'src/components/main_overflow_menu.dart'; +export 'src/components/project_list_tile.dart'; +export 'src/components/project_overflow_menu.dart'; + +export 'src/landing_page.dart'; diff --git a/lib/ui/landing_page/custom_action_button.dart b/packages/features/landing_page_screen/lib/src/components/custom_action_button.dart similarity index 90% rename from lib/ui/landing_page/custom_action_button.dart rename to packages/features/landing_page_screen/lib/src/components/custom_action_button.dart index 2bca6753..bb8b6ae9 100644 --- a/lib/ui/landing_page/custom_action_button.dart +++ b/packages/features/landing_page_screen/lib/src/components/custom_action_button.dart @@ -4,12 +4,14 @@ class CustomActionButton extends StatelessWidget { final String heroTag; final IconData icon; final VoidCallback onPressed; + final String hint; const CustomActionButton({ Key? key, required this.heroTag, required this.icon, required this.onPressed, + required this.hint, }) : super(key: key); @override @@ -18,6 +20,7 @@ class CustomActionButton extends StatelessWidget { heroTag: heroTag, backgroundColor: const Color(0xFFFFAB08), foregroundColor: const Color(0xFFFFFFFF), + tooltip: hint, child: Icon(icon), onPressed: () async => onPressed(), ); diff --git a/lib/ui/landing_page/image_preview.dart b/packages/features/landing_page_screen/lib/src/components/image_preview.dart similarity index 89% rename from lib/ui/landing_page/image_preview.dart rename to packages/features/landing_page_screen/lib/src/components/image_preview.dart index f2e11b6a..31a6b886 100644 --- a/lib/ui/landing_page/image_preview.dart +++ b/packages/features/landing_page_screen/lib/src/components/image_preview.dart @@ -1,8 +1,8 @@ +import 'package:component_library/component_library.dart'; +import 'package:database/database.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/core/toast_utils.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/io/src/service/image_service.dart'; +import 'package:io_library/io_library.dart'; class ImagePreview extends StatelessWidget { final Project? project; diff --git a/lib/ui/landing_page/main_overflow_menu.dart b/packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart similarity index 88% rename from lib/ui/landing_page/main_overflow_menu.dart rename to packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart index 56265478..ff2ca061 100644 --- a/lib/ui/landing_page/main_overflow_menu.dart +++ b/packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart @@ -1,12 +1,10 @@ import 'dart:io'; +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:paintroid/io/src/ui/about_dialog.dart'; -import 'package:paintroid/ui/pop_menu_button.dart'; -import 'package:paintroid/ui/styles.dart'; -import 'package:paintroid/ui/util.dart'; import 'package:url_launcher/url_launcher.dart'; enum MainOverflowMenuOption { @@ -50,9 +48,7 @@ class _MainOverFlowMenuState extends ConsumerState { switch (option) { case MainOverflowMenuOption.rate: if (Platform.isAndroid || Platform.isIOS) { - final appId = Platform.isAndroid - ? androidAppId - : iOSAppId; + final appId = Platform.isAndroid ? androidAppId : iOSAppId; final url = Uri.parse( Platform.isAndroid ? 'market://details?id=$appId' diff --git a/lib/ui/landing_page/project_list_tile.dart b/packages/features/landing_page_screen/lib/src/components/project_list_tile.dart similarity index 84% rename from lib/ui/landing_page/project_list_tile.dart rename to packages/features/landing_page_screen/lib/src/components/project_list_tile.dart index 4ea7012d..c087f805 100644 --- a/lib/ui/landing_page/project_list_tile.dart +++ b/packages/features/landing_page_screen/lib/src/components/project_list_tile.dart @@ -1,9 +1,8 @@ +import 'package:database/database.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/io/io.dart'; -import 'package:paintroid/ui/landing_page/image_preview.dart'; -import 'package:paintroid/ui/landing_page/project_overflow_menu.dart'; +import 'package:io_library/io_library.dart'; +import 'package:landing_page_screen/landing_page_screen.dart'; class ProjectListTile extends StatelessWidget { final Project project; diff --git a/lib/ui/landing_page/project_overflow_menu.dart b/packages/features/landing_page_screen/lib/src/components/project_overflow_menu.dart similarity index 89% rename from lib/ui/landing_page/project_overflow_menu.dart rename to packages/features/landing_page_screen/lib/src/components/project_overflow_menu.dart index bd2fd762..78d68833 100644 --- a/lib/ui/landing_page/project_overflow_menu.dart +++ b/packages/features/landing_page_screen/lib/src/components/project_overflow_menu.dart @@ -1,12 +1,10 @@ import 'dart:io'; +import 'package:component_library/component_library.dart'; +import 'package:database/database.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/core/toast_utils.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/data/project_database.dart'; -import 'package:paintroid/io/src/ui/delete_project_dialog.dart'; -import 'package:paintroid/io/src/ui/project_details_dialog.dart'; +import 'package:io_library/io_library.dart'; enum ProjectOverflowMenuOption { deleteProject('Delete'), diff --git a/lib/ui/landing_page/landing_page.dart b/packages/features/landing_page_screen/lib/src/landing_page.dart similarity index 87% rename from lib/ui/landing_page/landing_page.dart rename to packages/features/landing_page_screen/lib/src/landing_page.dart index 26601a00..9170a6da 100644 --- a/lib/ui/landing_page/landing_page.dart +++ b/packages/features/landing_page_screen/lib/src/landing_page.dart @@ -1,21 +1,12 @@ +import 'package:component_library/component_library.dart'; +import 'package:database/database.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:io_library/io_library.dart'; +import 'package:landing_page_screen/landing_page_screen.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/toast_utils.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/data/project_database.dart'; -import 'package:paintroid/io/io.dart'; -import 'package:paintroid/ui/color_schemes.dart'; -import 'package:paintroid/ui/io_handler.dart'; -import 'package:paintroid/ui/landing_page/custom_action_button.dart'; -import 'package:paintroid/ui/landing_page/image_preview.dart'; -import 'package:paintroid/ui/landing_page/main_overflow_menu.dart'; -import 'package:paintroid/ui/landing_page/project_list_tile.dart'; -import 'package:paintroid/ui/landing_page/project_overflow_menu.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart'; -import 'package:paintroid/workspace/src/state/workspace_state_notifier.dart'; import 'package:toast/toast.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class LandingPage extends ConsumerStatefulWidget { final String title; @@ -161,6 +152,7 @@ class _LandingPageState extends ConsumerState { CustomActionButton( heroTag: 'import_image', icon: Icons.file_download, + hint: 'Load image', onPressed: () async { final bool imageLoaded = await ioHandler.loadImage(context, this, false); @@ -175,6 +167,7 @@ class _LandingPageState extends ConsumerState { CustomActionButton( heroTag: 'new_image', icon: Icons.add, + hint: 'New image', onPressed: () async { _clearCanvas(); _navigateToPocketPaint(); @@ -222,10 +215,10 @@ class _ProjectPreview extends StatelessWidget { openProject(); } }, - icon: SvgPicture.asset( - 'assets/svg/ic_edit_circle.svg', - height: 264, - width: 264, + icon: const IconSvg( + path: 'assets/svg/ic_edit_circle.svg', + height: 264.0, + width: 264.0, ), ), ), diff --git a/packages/features/landing_page_screen/pubspec.yaml b/packages/features/landing_page_screen/pubspec.yaml new file mode 100644 index 00000000..836bb160 --- /dev/null +++ b/packages/features/landing_page_screen/pubspec.yaml @@ -0,0 +1,56 @@ +name: landing_page_screen +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + freezed_annotation: ^2.4.1 + + intl: ^0.18.0 + toast: ^0.3.0 + oxidized: ^5.2.0 + flutter_svg: ^1.1.0 + launch_review: ^3.0.1 + package_info_plus: ^4.0.1 + filesize: ^2.0.1 + url_launcher: ^6.1.5 + + # Internal packages + component_library: + path: ../../component_library + database: + path: ../../database + io_library: + path: ../../io_library + workspace_screen: + path: ../workspace_screen + paintroid: + path: ../../../ + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + +flutter: + uses-material-design: true + + assets: + - test/fixture/image/ diff --git a/test/fixture/image/test.jpg b/packages/features/landing_page_screen/test/fixture/image/test.jpg similarity index 100% rename from test/fixture/image/test.jpg rename to packages/features/landing_page_screen/test/fixture/image/test.jpg diff --git a/test/fixture/image/test.png b/packages/features/landing_page_screen/test/fixture/image/test.png similarity index 100% rename from test/fixture/image/test.png rename to packages/features/landing_page_screen/test/fixture/image/test.png diff --git a/packages/features/landing_page_screen/test/fixture/image/test1.png b/packages/features/landing_page_screen/test/fixture/image/test1.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e03a09329b7d4e7a056b28eacf63b33208aa44 GIT binary patch literal 1272 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jB19BD*PQ~&?~0ssI20000082|tP zC;$Ke82|tP82|tRcubKn-~a#uL`g(JRA>e4mS1R(Q5eTJTUbQ2WGKH(;)1IwmYNH0 zkPD(@ZY39FrM6O1a^c1WrCmtd4M|btf+XZZDJ5=}6c@9JY(g9J=kuv^>U7$cHcM~b zZ+$xFInR5}`#jHi&-=brRi(-bloco|P*$Lq z6halEyb3=cCh(qqEBsT{ad!{sOPH5MvJSfg+F&s>z&kL3A-D!-;Tf!iDV4#U1V2o9 zptBi!00!0%^`Lw_u0yQ2tySLuej9FrkCt+6{aqSUp=uW04agGQ03_K3@F*CSz}8vX6-i_rWg6b%Fu?Ry^G$uR8$xx-}B5pB;ZI+uafeSaXTqA!%nM)u;$qVvmv5a9 z;5A2bXRtl+?o?dMD&GN%RDrS7jKeu^p=PX$#vAwT@d#Yx3-}gV52>uG!8`}Ay8@~F zQ#}u}!k}xK=zVbKVp6tG;4qA~Q$69@{QeA&uNK_B;0<0wd+1-HO|=Rm2J?pg+{C$^ z{`fX@a0QQA@U8vp?R|2m1qasU7U>PbXFRPB~cC`3^d z$1(XVkp(H)OtP?$Y*?_cP!?=Rb{0OCB4H~QN(o8HN?8$>%0?m^N;VX-p;(Zj5c&H3 zr~7hyo@S`YOYhWg&bjBDd+)ht-n%mi314Lp*DJikI^;ry8SkTQNo4Q@%I&iPwU&Rx z@_%Qh5@>`?&{|iJ1CDfI$UosYqUjV8p5XQs-zLlxYa@hti`GR!2cH~5>$vCQcw zNgK?2B&%DB-5af(B#1?NNIHcj7=v5TS`82v3A3s;&_-#l9a>FH@kI!^;u`9kUWQ9( z0Y|1WG|y&%u^zLy$qp-P!zR}bdRg^?DyECU7QJ_Nv2Tn!tOONY1Z^+?H4t~{W(O6! z;<3}HiOpECZ0s%Le?;p7DS$thd@9h@tfJhvR|!5H@&=MY*XJI{cMiG~d?n89)$CE# zAwCse2hJgmu?xX>(o01ucFv=+DHFRe`UtIW(U($2RXT_|?Ur^Niird8?IEZw^87!c zb^BzNmw84x>bftV{hzdIgJk{p%KOPAKj!N9A?(M(f|`O31RV%E i5Og5uK+u8z(18ymm;N57p2}zd0000 extends _i1.SmartFake + implements _i3.StreamController { + _FakeStreamController_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDatabaseExecutor_2 extends _i1.SmartFake + implements _i4.DatabaseExecutor { + _FakeDatabaseExecutor_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeResult_3 extends _i1.SmartFake + implements _i5.Result { + _FakeResult_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [ProjectDatabase]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockProjectDatabase extends _i1.Mock implements _i6.ProjectDatabase { + MockProjectDatabase() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.ProjectDAO get projectDAO => (super.noSuchMethod( + Invocation.getter(#projectDAO), + returnValue: _FakeProjectDAO_0( + this, + Invocation.getter(#projectDAO), + ), + ) as _i2.ProjectDAO); + + @override + _i3.StreamController get changeListener => (super.noSuchMethod( + Invocation.getter(#changeListener), + returnValue: _FakeStreamController_1( + this, + Invocation.getter(#changeListener), + ), + ) as _i3.StreamController); + + @override + set changeListener(_i3.StreamController? _changeListener) => + super.noSuchMethod( + Invocation.setter( + #changeListener, + _changeListener, + ), + returnValueForMissingStub: null, + ); + + @override + _i4.DatabaseExecutor get database => (super.noSuchMethod( + Invocation.getter(#database), + returnValue: _FakeDatabaseExecutor_2( + this, + Invocation.getter(#database), + ), + ) as _i4.DatabaseExecutor); + + @override + set database(_i4.DatabaseExecutor? _database) => super.noSuchMethod( + Invocation.setter( + #database, + _database, + ), + returnValueForMissingStub: null, + ); + + @override + _i3.Future close() => (super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); +} + +/// A class which mocks [ProjectDAO]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockProjectDAO extends _i1.Mock implements _i2.ProjectDAO { + MockProjectDAO() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future insertProject(_i7.Project? project) => (super.noSuchMethod( + Invocation.method( + #insertProject, + [project], + ), + returnValue: _i3.Future.value(0), + ) as _i3.Future); + + @override + _i3.Future> insertProjects(List<_i7.Project>? projects) => + (super.noSuchMethod( + Invocation.method( + #insertProjects, + [projects], + ), + returnValue: _i3.Future>.value([]), + ) as _i3.Future>); + + @override + _i3.Future deleteProject(int? id) => (super.noSuchMethod( + Invocation.method( + #deleteProject, + [id], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future deleteProjects(List<_i7.Project>? projects) => + (super.noSuchMethod( + Invocation.method( + #deleteProjects, + [projects], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future> getProjects() => (super.noSuchMethod( + Invocation.method( + #getProjects, + [], + ), + returnValue: _i3.Future>.value(<_i7.Project>[]), + ) as _i3.Future>); + + @override + _i3.Future<_i7.Project?> getProjectByName(String? name) => + (super.noSuchMethod( + Invocation.method( + #getProjectByName, + [name], + ), + returnValue: _i3.Future<_i7.Project?>.value(), + ) as _i3.Future<_i7.Project?>); +} + +/// A class which mocks [IImageService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIImageService extends _i1.Mock implements _i8.IImageService { + MockIImageService() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i5.Result<_i9.Image, _i8.Failure>> import( + _i10.Uint8List? fileData) => + (super.noSuchMethod( + Invocation.method( + #import, + [fileData], + ), + returnValue: _i3.Future<_i5.Result<_i9.Image, _i8.Failure>>.value( + _FakeResult_3<_i9.Image, _i8.Failure>( + this, + Invocation.method( + #import, + [fileData], + ), + )), + ) as _i3.Future<_i5.Result<_i9.Image, _i8.Failure>>); + + @override + _i3.Future<_i5.Result<_i10.Uint8List, _i8.Failure>> exportAsJpg( + _i9.Image? image, + int? quality, + ) => + (super.noSuchMethod( + Invocation.method( + #exportAsJpg, + [ + image, + quality, + ], + ), + returnValue: _i3.Future<_i5.Result<_i10.Uint8List, _i8.Failure>>.value( + _FakeResult_3<_i10.Uint8List, _i8.Failure>( + this, + Invocation.method( + #exportAsJpg, + [ + image, + quality, + ], + ), + )), + ) as _i3.Future<_i5.Result<_i10.Uint8List, _i8.Failure>>); + + @override + _i3.Future<_i5.Result<_i10.Uint8List, _i8.Failure>> exportAsPng( + _i9.Image? image) => + (super.noSuchMethod( + Invocation.method( + #exportAsPng, + [image], + ), + returnValue: _i3.Future<_i5.Result<_i10.Uint8List, _i8.Failure>>.value( + _FakeResult_3<_i10.Uint8List, _i8.Failure>( + this, + Invocation.method( + #exportAsPng, + [image], + ), + )), + ) as _i3.Future<_i5.Result<_i10.Uint8List, _i8.Failure>>); + + @override + _i5.Result<_i10.Uint8List, _i8.Failure> getProjectPreview(String? path) => + (super.noSuchMethod( + Invocation.method( + #getProjectPreview, + [path], + ), + returnValue: _FakeResult_3<_i10.Uint8List, _i8.Failure>( + this, + Invocation.method( + #getProjectPreview, + [path], + ), + ), + ) as _i5.Result<_i10.Uint8List, _i8.Failure>); +} + +/// A class which mocks [IFileService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIFileService extends _i1.Mock implements _i8.IFileService { + MockIFileService() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.Future<_i5.Result<_i11.File, _i8.Failure>> save( + String? filename, + _i10.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #save, + [ + filename, + data, + ], + ), + returnValue: _i3.Future<_i5.Result<_i11.File, _i8.Failure>>.value( + _FakeResult_3<_i11.File, _i8.Failure>( + this, + Invocation.method( + #save, + [ + filename, + data, + ], + ), + )), + ) as _i3.Future<_i5.Result<_i11.File, _i8.Failure>>); + + @override + _i3.Future<_i5.Result<_i11.File, _i8.Failure>> saveToApplicationDirectory( + String? filename, + _i10.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #saveToApplicationDirectory, + [ + filename, + data, + ], + ), + returnValue: _i3.Future<_i5.Result<_i11.File, _i8.Failure>>.value( + _FakeResult_3<_i11.File, _i8.Failure>( + this, + Invocation.method( + #saveToApplicationDirectory, + [ + filename, + data, + ], + ), + )), + ) as _i3.Future<_i5.Result<_i11.File, _i8.Failure>>); + + @override + _i3.Future<_i5.Result<_i11.File, _i8.Failure>> pick() => (super.noSuchMethod( + Invocation.method( + #pick, + [], + ), + returnValue: _i3.Future<_i5.Result<_i11.File, _i8.Failure>>.value( + _FakeResult_3<_i11.File, _i8.Failure>( + this, + Invocation.method( + #pick, + [], + ), + )), + ) as _i3.Future<_i5.Result<_i11.File, _i8.Failure>>); + + @override + _i5.Result<_i11.File, _i8.Failure> getFile(String? path) => + (super.noSuchMethod( + Invocation.method( + #getFile, + [path], + ), + returnValue: _FakeResult_3<_i11.File, _i8.Failure>( + this, + Invocation.method( + #getFile, + [path], + ), + ), + ) as _i5.Result<_i11.File, _i8.Failure>); + + @override + _i3.Future checkIfFileExistsInApplicationDirectory(String? fileName) => + (super.noSuchMethod( + Invocation.method( + #checkIfFileExistsInApplicationDirectory, + [fileName], + ), + returnValue: _i3.Future.value(false), + ) as _i3.Future); + + @override + _i3.Future<_i5.Result<_i11.FileSystemEntity, _i8.Failure>> + deleteFileInApplicationDirectory(String? fileName) => (super.noSuchMethod( + Invocation.method( + #deleteFileInApplicationDirectory, + [fileName], + ), + returnValue: _i3 + .Future<_i5.Result<_i11.FileSystemEntity, _i8.Failure>>.value( + _FakeResult_3<_i11.FileSystemEntity, _i8.Failure>( + this, + Invocation.method( + #deleteFileInApplicationDirectory, + [fileName], + ), + )), + ) as _i3.Future<_i5.Result<_i11.FileSystemEntity, _i8.Failure>>); +} diff --git a/packages/features/onboarding_screen/.metadata b/packages/features/onboarding_screen/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/features/onboarding_screen/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/features/onboarding_screen/analysis_options.yaml b/packages/features/onboarding_screen/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/features/onboarding_screen/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/packages/features/onboarding_screen/lib/onboarding_screen.dart b/packages/features/onboarding_screen/lib/onboarding_screen.dart new file mode 100644 index 00000000..a1f4acac --- /dev/null +++ b/packages/features/onboarding_screen/lib/onboarding_screen.dart @@ -0,0 +1,13 @@ +library onboarding_screen; + +export 'src/components/bottom_nav_bar_container.dart'; +export 'src/components/onboarding_page_app_bar.dart'; +export 'src/components/onboarding_page_bottom_nav_bar.dart'; + +export 'src/screens/screen1.dart'; +export 'src/screens/screen2.dart'; +export 'src/screens/screen3.dart'; +export 'src/screens/screen4.dart'; +export 'src/screens/screen5.dart'; + +export 'src/onboarding_screen.dart'; diff --git a/lib/ui/onboarding/bottom_nav_bar_container.dart b/packages/features/onboarding_screen/lib/src/components/bottom_nav_bar_container.dart similarity index 83% rename from lib/ui/onboarding/bottom_nav_bar_container.dart rename to packages/features/onboarding_screen/lib/src/components/bottom_nav_bar_container.dart index 543910fa..d043b7ba 100644 --- a/lib/ui/onboarding/bottom_nav_bar_container.dart +++ b/packages/features/onboarding_screen/lib/src/components/bottom_nav_bar_container.dart @@ -1,6 +1,6 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/color_schemes.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page_bottom_nav_bar.dart'; +import 'package:onboarding_screen/onboarding_screen.dart'; class BottomNavigationBarContainer extends StatelessWidget { final List navBarItems; diff --git a/lib/ui/onboarding/onboarding_page_app_bar.dart b/packages/features/onboarding_screen/lib/src/components/onboarding_page_app_bar.dart similarity index 100% rename from lib/ui/onboarding/onboarding_page_app_bar.dart rename to packages/features/onboarding_screen/lib/src/components/onboarding_page_app_bar.dart diff --git a/lib/ui/onboarding/onboarding_page_bottom_nav_bar.dart b/packages/features/onboarding_screen/lib/src/components/onboarding_page_bottom_nav_bar.dart similarity index 94% rename from lib/ui/onboarding/onboarding_page_bottom_nav_bar.dart rename to packages/features/onboarding_screen/lib/src/components/onboarding_page_bottom_nav_bar.dart index 1fa5b6dc..2c4a0b26 100644 --- a/lib/ui/onboarding/onboarding_page_bottom_nav_bar.dart +++ b/packages/features/onboarding_screen/lib/src/components/onboarding_page_bottom_nav_bar.dart @@ -1,5 +1,5 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/color_schemes.dart'; class OnboardingPageBottomNavigationBar extends StatefulWidget { final List onPressedFunctions; diff --git a/lib/ui/onboarding/onboarding_page.dart b/packages/features/onboarding_screen/lib/src/onboarding_screen.dart similarity index 90% rename from lib/ui/onboarding/onboarding_page.dart rename to packages/features/onboarding_screen/lib/src/onboarding_screen.dart index c1b65732..46ff21c8 100644 --- a/lib/ui/onboarding/onboarding_page.dart +++ b/packages/features/onboarding_screen/lib/src/onboarding_screen.dart @@ -1,11 +1,8 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:paintroid/ui/color_schemes.dart'; -import 'package:paintroid/ui/onboarding/onboarding_screens/screen1.dart'; -import 'package:paintroid/ui/onboarding/onboarding_screens/screen2.dart'; -import 'package:paintroid/ui/onboarding/onboarding_screens/screen3.dart'; -import 'package:paintroid/ui/onboarding/onboarding_screens/screen4.dart'; -import 'package:paintroid/ui/onboarding/onboarding_screens/screen5.dart'; +import 'package:onboarding_screen/onboarding_screen.dart'; + import 'package:shared_preferences/shared_preferences.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:toast/toast.dart'; diff --git a/lib/ui/onboarding/onboarding_screens/screen1.dart b/packages/features/onboarding_screen/lib/src/screens/screen1.dart similarity index 94% rename from lib/ui/onboarding/onboarding_screens/screen1.dart rename to packages/features/onboarding_screen/lib/src/screens/screen1.dart index 373e7a8a..a58dec9b 100644 --- a/lib/ui/onboarding/onboarding_screens/screen1.dart +++ b/packages/features/onboarding_screen/lib/src/screens/screen1.dart @@ -1,5 +1,5 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/color_schemes.dart'; class Screen1 extends StatelessWidget { const Screen1({Key? key}) : super(key: key); diff --git a/lib/ui/onboarding/onboarding_screens/screen2.dart b/packages/features/onboarding_screen/lib/src/screens/screen2.dart similarity index 93% rename from lib/ui/onboarding/onboarding_screens/screen2.dart rename to packages/features/onboarding_screen/lib/src/screens/screen2.dart index ac379b06..b9b8628c 100644 --- a/lib/ui/onboarding/onboarding_screens/screen2.dart +++ b/packages/features/onboarding_screen/lib/src/screens/screen2.dart @@ -1,8 +1,7 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page_app_bar.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page_bottom_nav_bar.dart'; -import 'package:paintroid/ui/shared/bottom_nav_bar_icon.dart'; -import 'package:paintroid/workspace/src/ui/drawing_canvas.dart'; +import 'package:onboarding_screen/onboarding_screen.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class Screen2 extends StatefulWidget { const Screen2({Key? key}) : super(key: key); diff --git a/lib/ui/onboarding/onboarding_screens/screen3.dart b/packages/features/onboarding_screen/lib/src/screens/screen3.dart similarity index 94% rename from lib/ui/onboarding/onboarding_screens/screen3.dart rename to packages/features/onboarding_screen/lib/src/screens/screen3.dart index 57721000..a7a2d1a8 100644 --- a/lib/ui/onboarding/onboarding_screens/screen3.dart +++ b/packages/features/onboarding_screen/lib/src/screens/screen3.dart @@ -1,8 +1,6 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:paintroid/ui/color_schemes.dart'; -import 'package:paintroid/ui/onboarding/bottom_nav_bar_container.dart'; -import 'package:paintroid/ui/shared/bottom_nav_bar_icon.dart'; +import 'package:onboarding_screen/onboarding_screen.dart'; class Screen3 extends StatefulWidget { const Screen3({Key? key}) : super(key: key); @@ -107,10 +105,11 @@ class _Screen3State extends State { textAlign: TextAlign.start, ), Container( - padding: const EdgeInsets.only(left: 50), - child: SvgPicture.asset( - icons[i], - height: 24, + padding: const EdgeInsets.only(left: 50.0), + child: IconSvg( + path: icons[i], + height: 24.0, + width: 24.0, color: Theme.of(context).colorScheme.onSurface, ), ), diff --git a/lib/ui/onboarding/onboarding_screens/screen4.dart b/packages/features/onboarding_screen/lib/src/screens/screen4.dart similarity index 86% rename from lib/ui/onboarding/onboarding_screens/screen4.dart rename to packages/features/onboarding_screen/lib/src/screens/screen4.dart index cf8cb6a1..ff72ffa4 100644 --- a/lib/ui/onboarding/onboarding_screens/screen4.dart +++ b/packages/features/onboarding_screen/lib/src/screens/screen4.dart @@ -1,5 +1,5 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/color_schemes.dart'; class Screen4 extends StatelessWidget { const Screen4({Key? key}) : super(key: key); @@ -39,14 +39,11 @@ class Screen4 extends StatelessWidget { ), ), ), - Expanded( + const Expanded( flex: 6, child: SizedBox( width: double.infinity, - child: Image.asset( - 'assets/icon/pocketpaint_intro_landscape.png', - fit: BoxFit.contain, - ), + child: PocketPaintIntroLandscape(), ), ), ], diff --git a/lib/ui/onboarding/onboarding_screens/screen5.dart b/packages/features/onboarding_screen/lib/src/screens/screen5.dart similarity index 88% rename from lib/ui/onboarding/onboarding_screens/screen5.dart rename to packages/features/onboarding_screen/lib/src/screens/screen5.dart index 11c2fbaa..c7b4053e 100644 --- a/lib/ui/onboarding/onboarding_screens/screen5.dart +++ b/packages/features/onboarding_screen/lib/src/screens/screen5.dart @@ -1,5 +1,5 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/ui/color_schemes.dart'; class Screen5 extends StatelessWidget { const Screen5({Key? key}) : super(key: key); @@ -44,10 +44,7 @@ class Screen5 extends StatelessWidget { child: Container( alignment: Alignment.center, padding: const EdgeInsets.only(bottom: 10), - child: Image.asset( - 'assets/icon/pocketpaint_intro_portrait.png', - fit: BoxFit.fitHeight, - ), + child: const PocketPaintIntroPortrait(), ), ), ], diff --git a/packages/features/onboarding_screen/pubspec.yaml b/packages/features/onboarding_screen/pubspec.yaml new file mode 100644 index 00000000..491ce31e --- /dev/null +++ b/packages/features/onboarding_screen/pubspec.yaml @@ -0,0 +1,46 @@ +name: onboarding_screen +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + smooth_page_indicator: ^1.0.0+2 + shared_preferences: ^2.0.15 + toast: ^0.3.0 + flutter_svg: ^1.1.0 + flutter_localization: ^0.1.12 + + # Internal packages + component_library: + path: ../../component_library + tools: + path: ../../tools + workspace_screen: + path: ../workspace_screen + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + +flutter: + uses-material-design: true diff --git a/test/widget/ui/onboarding_page_test.dart b/packages/features/onboarding_screen/test/widget/onboarding_screen_test.dart similarity index 97% rename from test/widget/ui/onboarding_page_test.dart rename to packages/features/onboarding_screen/test/widget/onboarding_screen_test.dart index ffd48867..773ea4a4 100644 --- a/test/widget/ui/onboarding_page_test.dart +++ b/packages/features/onboarding_screen/test/widget/onboarding_screen_test.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page_app_bar.dart'; -import 'package:paintroid/ui/onboarding/onboarding_page_bottom_nav_bar.dart'; +import 'package:onboarding_screen/onboarding_screen.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; void main() { late Widget sut; @@ -49,6 +48,9 @@ void main() { sut = const ProviderScope( child: MaterialApp( home: OnboardingPage(), + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + ], ), ); }); diff --git a/packages/features/workspace_screen/.metadata b/packages/features/workspace_screen/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/features/workspace_screen/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/features/workspace_screen/analysis_options.yaml b/packages/features/workspace_screen/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/features/workspace_screen/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar.dart new file mode 100644 index 00000000..11911f51 --- /dev/null +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar.dart @@ -0,0 +1,89 @@ +import 'package:component_library/component_library.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:l10n/l10n.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; + +class BottomNavBar extends ConsumerWidget { + static const height = 64.0; + + const BottomNavBar({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final localizations = AppLocalizations.of(context); + final currentToolData = getCurrentToolData(ref); + + return NavigationBarTheme( + data: WidgetThemes.bottomNavBarThemeData, + child: NavigationBar( + height: height, + onDestinationSelected: (index) => + _onNavigationItemSelected(index, context, ref), + destinations: [ + NavigationDestination( + label: localizations.tools, + icon: const BottomBarIcon(asset: 'assets/svg/ic_tools.svg'), + ), + NavigationDestination( + label: currentToolData.name, + icon: BottomBarIcon(asset: currentToolData.svgAssetPath), + ), + NavigationDestination( + label: localizations.color, + icon: Icon( + Icons.check_box_outline_blank, + size: 24, + color: Theme.of(context).colorScheme.onSurface, + ), + ), + NavigationDestination( + label: localizations.layers, + icon: const BottomBarIcon(asset: 'assets/svg/ic_layers.svg'), + ), + ], + ), + ); + } + + ToolData getCurrentToolData(WidgetRef ref) { + final ToolType currentToolType = ref.watch( + toolBoxStateProvider.select((value) => value.currentToolType), + ); + + final currentToolData = ToolData.allToolsData.firstWhere( + (toolData) => toolData.type == currentToolType, + orElse: () => ToolData.BRUSH, + ); + return currentToolData; + } +} + +void _onNavigationItemSelected(int index, BuildContext context, WidgetRef ref) { + BottomNavBarItem item = BottomNavBarItem.values[index]; + switch (item) { + case BottomNavBarItem.TOOLS: + _showToolBottomSheet(context); + break; + case BottomNavBarItem.TOOL_OPTIONS: + _handleToolOptionsVisibility(ref); + break; + default: + return; + } +} + +void _showToolBottomSheet(BuildContext context) { + showModalBottomSheet( + context: context, + builder: (BuildContext context) => const SizedBox( + height: 270, + child: ToolsBottomSheet(), + ), + ); +} + +void _handleToolOptionsVisibility(WidgetRef ref) { + ref.read(toolOptionsVisibilityStateProvider.notifier).toggleVisibility(); +} diff --git a/packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar_items.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar_items.dart new file mode 100644 index 00000000..e5f91e2b --- /dev/null +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/bottom_nav_bar_items.dart @@ -0,0 +1,6 @@ +enum BottomNavBarItem { + TOOLS, + TOOL_OPTIONS, + COLOR, + LAYERS, +} diff --git a/lib/ui/drawing_space/bottom_brush_tool_options.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_cap_tool_option.dart similarity index 85% rename from lib/ui/drawing_space/bottom_brush_tool_options.dart rename to packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_cap_tool_option.dart index 70a38307..6b1abb05 100644 --- a/lib/ui/drawing_space/bottom_brush_tool_options.dart +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_cap_tool_option.dart @@ -1,18 +1,17 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart'; -import 'package:paintroid/ui/shared/custom_action_chip.dart'; +import 'package:tools/tools.dart'; -class BottomBrushToolOptions extends ConsumerStatefulWidget { - const BottomBrushToolOptions({super.key}); +class StrokeCapToolOption extends ConsumerStatefulWidget { + const StrokeCapToolOption({super.key}); @override - ConsumerState createState() => - _BottomBrushToolOptionsState(); + ConsumerState createState() => + _StrokeCapToolOptionState(); } -class _BottomBrushToolOptionsState - extends ConsumerState { +class _StrokeCapToolOptionState extends ConsumerState { Color _roundChipBackgroundColor = Colors.blue; Color _squareChipBackgroundColor = Colors.white; @@ -57,12 +56,14 @@ class _BottomBrushToolOptionsState spacing: 8, children: [ CustomActionChip( + hint: 'Round stroke', chipIcon: const Icon(Icons.circle), onPressed: () => _changeActionChipBackgroundColor(StrokeCap.round), chipBackgroundColor: _roundChipBackgroundColor, ), CustomActionChip( + hint: 'Square stroke', chipIcon: const Icon(Icons.square), onPressed: () => _changeActionChipBackgroundColor(StrokeCap.square), diff --git a/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_tool_options.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_tool_options.dart new file mode 100644 index 00000000..7d751026 --- /dev/null +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_tool_options.dart @@ -0,0 +1,14 @@ +import 'package:flutter/cupertino.dart'; +import 'package:workspace_screen/workspace_screen.dart'; + +class StrokeToolOptions extends StatelessWidget { + const StrokeToolOptions({super.key}); + @override + Widget build(BuildContext context) { + return const Column(children: [ + StrokeWidthToolOption(), + Spacer(), + StrokeCapToolOption(), + ]); + } +} diff --git a/lib/ui/drawing_space/top_brush_tool_options.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_width_tool_option.dart similarity index 87% rename from lib/ui/drawing_space/top_brush_tool_options.dart rename to packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_width_tool_option.dart index 4b306c8a..cce4c39f 100644 --- a/lib/ui/drawing_space/top_brush_tool_options.dart +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/stroke_width_tool_option.dart @@ -1,17 +1,18 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart'; -import 'package:paintroid/ui/styles.dart'; +import 'package:tools/tools.dart'; -class TopBrushToolOptions extends ConsumerStatefulWidget { - const TopBrushToolOptions({super.key}); +class StrokeWidthToolOption extends ConsumerStatefulWidget { + const StrokeWidthToolOption({super.key}); @override - ConsumerState createState() => _NumberTextFieldState(); + ConsumerState createState() => + _StrokeWidthToolOptionState(); } -class _NumberTextFieldState extends ConsumerState { +class _StrokeWidthToolOptionState extends ConsumerState { late final TextEditingController _strokeWidthTextController; void _onChangedTextField(String value) { diff --git a/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_option.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_option.dart new file mode 100644 index 00000000..cf627f0b --- /dev/null +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_option.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class ToolOption extends StatelessWidget { + final bool isIgnoring; + final double opacity; + final Widget child; + final Duration duration; + + const ToolOption({ + Key? key, + required this.isIgnoring, + required this.opacity, + required this.child, + this.duration = const Duration(milliseconds: 300), + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + ignoring: isIgnoring, + child: AnimatedOpacity( + opacity: opacity, + duration: duration, + child: child, + ), + ); + } +} diff --git a/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_options.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_options.dart new file mode 100644 index 00000000..b50e3f7f --- /dev/null +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tool_options/tool_options.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; + +class ToolOptions extends ConsumerWidget { + const ToolOptions({super.key}); + final maxOpacity = 1.0; + final minOpacity = 0.0; + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool visible = ref.watch(toolOptionsVisibilityStateProvider); + final currentToolType = ref.watch( + toolBoxStateProvider.select((value) => value.currentToolType), + ); + + return Padding( + padding: const EdgeInsets.all(8), + child: ToolOption( + isIgnoring: !visible, + opacity: visible ? maxOpacity : minOpacity, + child: switch (currentToolType) { + ToolType.BRUSH => const StrokeToolOptions(), + ToolType.ERASER => const StrokeToolOptions(), + _ => Container(), + }), + ); + } +} diff --git a/lib/ui/shared/tool_button.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tools/tool_button.dart similarity index 70% rename from lib/ui/shared/tool_button.dart rename to packages/features/workspace_screen/lib/src/components/bottom_bar/tools/tool_button.dart index bb9b2517..3f346879 100644 --- a/lib/ui/shared/tool_button.dart +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tools/tool_button.dart @@ -1,8 +1,7 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/shared/icon_button_with_label.dart'; +import 'package:tools/tools.dart'; class ToolButton extends StatelessWidget { final ToolData toolData; @@ -17,10 +16,10 @@ class ToolButton extends StatelessWidget { return Consumer( builder: (context, ref, child) { return IconButtonWithLabel( - icon: SvgPicture.asset( - toolData.svgAssetPath, - height: 24, - width: 24, + icon: IconSvg( + path: toolData.svgAssetPath, + height: 24.0, + width: 24.0, color: Colors.white, ), label: toolData.name, diff --git a/lib/ui/drawing_space/tools_bottom_sheet.dart b/packages/features/workspace_screen/lib/src/components/bottom_bar/tools/tools_bottom_sheet.dart similarity index 74% rename from lib/ui/drawing_space/tools_bottom_sheet.dart rename to packages/features/workspace_screen/lib/src/components/bottom_bar/tools/tools_bottom_sheet.dart index ed28055d..3a402b19 100644 --- a/lib/ui/drawing_space/tools_bottom_sheet.dart +++ b/packages/features/workspace_screen/lib/src/components/bottom_bar/tools/tools_bottom_sheet.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/shared/tool_button.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/src/components/bottom_bar/tools/tool_button.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class ToolsBottomSheet extends StatelessWidget { const ToolsBottomSheet({ diff --git a/lib/workspace/src/ui/canvas_painter.dart b/packages/features/workspace_screen/lib/src/components/drawing_surface/canvas_painter.dart similarity index 83% rename from lib/workspace/src/ui/canvas_painter.dart rename to packages/features/workspace_screen/lib/src/components/drawing_surface/canvas_painter.dart index 09663f3e..3de42d23 100644 --- a/lib/workspace/src/ui/canvas_painter.dart +++ b/packages/features/workspace_screen/lib/src/components/drawing_surface/canvas_painter.dart @@ -1,10 +1,7 @@ +import 'package:command/command_providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart'; -import 'package:paintroid/workspace/src/state/canvas_dirty_state.dart'; -import 'package:paintroid/workspace/src/ui/checkerboard_pattern.dart'; -import 'package:paintroid/workspace/src/ui/command_painter.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class CanvasPainter extends ConsumerWidget { const CanvasPainter({super.key}); diff --git a/lib/workspace/src/ui/checkerboard_pattern.dart b/packages/features/workspace_screen/lib/src/components/drawing_surface/checkerboard_pattern.dart similarity index 57% rename from lib/workspace/src/ui/checkerboard_pattern.dart rename to packages/features/workspace_screen/lib/src/components/drawing_surface/checkerboard_pattern.dart index 999750eb..f61c58be 100644 --- a/lib/workspace/src/ui/checkerboard_pattern.dart +++ b/packages/features/workspace_screen/lib/src/components/drawing_surface/checkerboard_pattern.dart @@ -1,3 +1,4 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; class CheckerboardPattern extends StatelessWidget { @@ -9,14 +10,8 @@ class CheckerboardPattern extends StatelessWidget { Widget build(BuildContext context) { return Stack( children: [ - Positioned.fill( - child: Image.asset( - 'assets/img/checkerboard.png', - repeat: ImageRepeat.repeat, - cacheWidth: 50, - cacheHeight: 50, - filterQuality: FilterQuality.none, - ), + const Positioned.fill( + child: CheckerboardImg(), ), if (child != null) child!, ], diff --git a/lib/workspace/src/ui/command_painter.dart b/packages/features/workspace_screen/lib/src/components/drawing_surface/command_painter.dart similarity index 89% rename from lib/workspace/src/ui/command_painter.dart rename to packages/features/workspace_screen/lib/src/components/drawing_surface/command_painter.dart index 6b7cef88..dc50e60b 100644 --- a/lib/workspace/src/ui/command_painter.dart +++ b/packages/features/workspace_screen/lib/src/components/drawing_surface/command_painter.dart @@ -1,5 +1,5 @@ +import 'package:command/command.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/command/command.dart'; class CommandPainter extends CustomPainter { CommandPainter(this.commandManager); diff --git a/lib/workspace/src/ui/drawing_canvas.dart b/packages/features/workspace_screen/lib/src/components/drawing_surface/drawing_canvas.dart similarity index 90% rename from lib/workspace/src/ui/drawing_canvas.dart rename to packages/features/workspace_screen/lib/src/components/drawing_surface/drawing_canvas.dart index def06be6..23c5d02e 100644 --- a/lib/workspace/src/ui/drawing_canvas.dart +++ b/packages/features/workspace_screen/lib/src/components/drawing_surface/drawing_canvas.dart @@ -1,12 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/service/device_service.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; -import 'package:paintroid/tool/src/toolbox/toolbox_state_provider.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart'; -import 'package:paintroid/workspace/src/state/canvas_dirty_state.dart'; -import 'package:paintroid/workspace/src/state/workspace_state_notifier.dart'; -import 'package:paintroid/workspace/src/ui/canvas_painter.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class DrawingCanvas extends ConsumerStatefulWidget { const DrawingCanvas({Key? key}) : super(key: key); diff --git a/lib/ui/drawing_space/exit_fullscreen_button.dart b/packages/features/workspace_screen/lib/src/components/drawing_surface/exit_fullscreen_button.dart similarity index 88% rename from lib/ui/drawing_space/exit_fullscreen_button.dart rename to packages/features/workspace_screen/lib/src/components/drawing_surface/exit_fullscreen_button.dart index 5cda12f2..9acf6e3f 100644 --- a/lib/ui/drawing_space/exit_fullscreen_button.dart +++ b/packages/features/workspace_screen/lib/src/components/drawing_surface/exit_fullscreen_button.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class ExitFullscreenButton extends ConsumerWidget { const ExitFullscreenButton({Key? key}) : super(key: key); diff --git a/lib/ui/shared/overflow_menu.dart b/packages/features/workspace_screen/lib/src/components/top_bar/overflow_menu.dart similarity index 89% rename from lib/ui/shared/overflow_menu.dart rename to packages/features/workspace_screen/lib/src/components/top_bar/overflow_menu.dart index 3eb5f7ca..238c4863 100644 --- a/lib/ui/shared/overflow_menu.dart +++ b/packages/features/workspace_screen/lib/src/components/top_bar/overflow_menu.dart @@ -1,18 +1,12 @@ +import 'package:component_library/component_library.dart'; +import 'package:database/database.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; +import 'package:l10n/l10n.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/app_localizations.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/data/project_database.dart'; -import 'package:paintroid/io/src/entity/image_meta_data.dart'; -import 'package:paintroid/io/src/service/file_service.dart'; -import 'package:paintroid/io/src/ui/overwrite_dialog.dart'; -import 'package:paintroid/io/src/ui/save_image_dialog.dart'; -import 'package:paintroid/ui/io_handler.dart'; -import 'package:paintroid/ui/pop_menu_button.dart'; -import 'package:paintroid/ui/styles.dart'; -import 'package:paintroid/workspace/workspace.dart'; import 'package:toast/toast.dart'; +import 'package:workspace_screen/workspace_screen.dart'; enum OverflowMenuOption { fullscreen, diff --git a/lib/ui/shared/top_app_bar.dart b/packages/features/workspace_screen/lib/src/components/top_bar/top_app_bar.dart similarity index 82% rename from lib/ui/shared/top_app_bar.dart rename to packages/features/workspace_screen/lib/src/components/top_bar/top_app_bar.dart index 0d8d7aa5..fa21bad8 100644 --- a/lib/ui/shared/top_app_bar.dart +++ b/packages/features/workspace_screen/lib/src/components/top_bar/top_app_bar.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:paintroid/ui/shared/overflow_menu.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class TopAppBar extends AppBar { TopAppBar({Key? key, required String title}) diff --git a/lib/core/image_with_pixel_info.dart b/packages/features/workspace_screen/lib/src/models/image_with_pixel_info.dart similarity index 100% rename from lib/core/image_with_pixel_info.dart rename to packages/features/workspace_screen/lib/src/models/image_with_pixel_info.dart diff --git a/lib/service/device_service.dart b/packages/features/workspace_screen/lib/src/service/device_service.dart similarity index 100% rename from lib/service/device_service.dart rename to packages/features/workspace_screen/lib/src/service/device_service.dart diff --git a/lib/workspace/src/state/canvas_dirty_state.dart b/packages/features/workspace_screen/lib/src/states/canvas_dirty_state.dart similarity index 100% rename from lib/workspace/src/state/canvas_dirty_state.dart rename to packages/features/workspace_screen/lib/src/states/canvas_dirty_state.dart diff --git a/lib/workspace/src/state/canvas/canvas_state_data.dart b/packages/features/workspace_screen/lib/src/states/canvas_state_data.dart similarity index 80% rename from lib/workspace/src/state/canvas/canvas_state_data.dart rename to packages/features/workspace_screen/lib/src/states/canvas_state_data.dart index a1d8ccb6..ac45268a 100644 --- a/lib/workspace/src/state/canvas/canvas_state_data.dart +++ b/packages/features/workspace_screen/lib/src/states/canvas_state_data.dart @@ -1,9 +1,8 @@ import 'dart:ui' as ui; +import 'package:command/command.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:paintroid/command/src/command_manager.dart'; -import 'package:paintroid/core/graphic_factory.dart'; part 'canvas_state_data.freezed.dart'; diff --git a/packages/features/workspace_screen/lib/src/states/canvas_state_data.freezed.dart b/packages/features/workspace_screen/lib/src/states/canvas_state_data.freezed.dart new file mode 100644 index 00000000..63ce1189 --- /dev/null +++ b/packages/features/workspace_screen/lib/src/states/canvas_state_data.freezed.dart @@ -0,0 +1,221 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'canvas_state_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$CanvasStateData { + ui.Image? get backgroundImage => throw _privateConstructorUsedError; + ui.Image? get cachedImage => throw _privateConstructorUsedError; + ui.Size get size => throw _privateConstructorUsedError; + CommandManager get commandManager => throw _privateConstructorUsedError; + GraphicFactory get graphicFactory => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $CanvasStateDataCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $CanvasStateDataCopyWith<$Res> { + factory $CanvasStateDataCopyWith( + CanvasStateData value, $Res Function(CanvasStateData) then) = + _$CanvasStateDataCopyWithImpl<$Res, CanvasStateData>; + @useResult + $Res call( + {ui.Image? backgroundImage, + ui.Image? cachedImage, + ui.Size size, + CommandManager commandManager, + GraphicFactory graphicFactory}); +} + +/// @nodoc +class _$CanvasStateDataCopyWithImpl<$Res, $Val extends CanvasStateData> + implements $CanvasStateDataCopyWith<$Res> { + _$CanvasStateDataCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? backgroundImage = freezed, + Object? cachedImage = freezed, + Object? size = null, + Object? commandManager = null, + Object? graphicFactory = null, + }) { + return _then(_value.copyWith( + backgroundImage: freezed == backgroundImage + ? _value.backgroundImage + : backgroundImage // ignore: cast_nullable_to_non_nullable + as ui.Image?, + cachedImage: freezed == cachedImage + ? _value.cachedImage + : cachedImage // ignore: cast_nullable_to_non_nullable + as ui.Image?, + size: null == size + ? _value.size + : size // ignore: cast_nullable_to_non_nullable + as ui.Size, + commandManager: null == commandManager + ? _value.commandManager + : commandManager // ignore: cast_nullable_to_non_nullable + as CommandManager, + graphicFactory: null == graphicFactory + ? _value.graphicFactory + : graphicFactory // ignore: cast_nullable_to_non_nullable + as GraphicFactory, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_CanvasStateDataCopyWith<$Res> + implements $CanvasStateDataCopyWith<$Res> { + factory _$$_CanvasStateDataCopyWith( + _$_CanvasStateData value, $Res Function(_$_CanvasStateData) then) = + __$$_CanvasStateDataCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {ui.Image? backgroundImage, + ui.Image? cachedImage, + ui.Size size, + CommandManager commandManager, + GraphicFactory graphicFactory}); +} + +/// @nodoc +class __$$_CanvasStateDataCopyWithImpl<$Res> + extends _$CanvasStateDataCopyWithImpl<$Res, _$_CanvasStateData> + implements _$$_CanvasStateDataCopyWith<$Res> { + __$$_CanvasStateDataCopyWithImpl( + _$_CanvasStateData _value, $Res Function(_$_CanvasStateData) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? backgroundImage = freezed, + Object? cachedImage = freezed, + Object? size = null, + Object? commandManager = null, + Object? graphicFactory = null, + }) { + return _then(_$_CanvasStateData( + backgroundImage: freezed == backgroundImage + ? _value.backgroundImage + : backgroundImage // ignore: cast_nullable_to_non_nullable + as ui.Image?, + cachedImage: freezed == cachedImage + ? _value.cachedImage + : cachedImage // ignore: cast_nullable_to_non_nullable + as ui.Image?, + size: null == size + ? _value.size + : size // ignore: cast_nullable_to_non_nullable + as ui.Size, + commandManager: null == commandManager + ? _value.commandManager + : commandManager // ignore: cast_nullable_to_non_nullable + as CommandManager, + graphicFactory: null == graphicFactory + ? _value.graphicFactory + : graphicFactory // ignore: cast_nullable_to_non_nullable + as GraphicFactory, + )); + } +} + +/// @nodoc + +class _$_CanvasStateData implements _CanvasStateData { + const _$_CanvasStateData( + {this.backgroundImage, + this.cachedImage, + required this.size, + required this.commandManager, + required this.graphicFactory}); + + @override + final ui.Image? backgroundImage; + @override + final ui.Image? cachedImage; + @override + final ui.Size size; + @override + final CommandManager commandManager; + @override + final GraphicFactory graphicFactory; + + @override + String toString() { + return 'CanvasStateData(backgroundImage: $backgroundImage, cachedImage: $cachedImage, size: $size, commandManager: $commandManager, graphicFactory: $graphicFactory)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_CanvasStateData && + (identical(other.backgroundImage, backgroundImage) || + other.backgroundImage == backgroundImage) && + (identical(other.cachedImage, cachedImage) || + other.cachedImage == cachedImage) && + (identical(other.size, size) || other.size == size) && + (identical(other.commandManager, commandManager) || + other.commandManager == commandManager) && + (identical(other.graphicFactory, graphicFactory) || + other.graphicFactory == graphicFactory)); + } + + @override + int get hashCode => Object.hash(runtimeType, backgroundImage, cachedImage, + size, commandManager, graphicFactory); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_CanvasStateDataCopyWith<_$_CanvasStateData> get copyWith => + __$$_CanvasStateDataCopyWithImpl<_$_CanvasStateData>(this, _$identity); +} + +abstract class _CanvasStateData implements CanvasStateData { + const factory _CanvasStateData( + {final ui.Image? backgroundImage, + final ui.Image? cachedImage, + required final ui.Size size, + required final CommandManager commandManager, + required final GraphicFactory graphicFactory}) = _$_CanvasStateData; + + @override + ui.Image? get backgroundImage; + @override + ui.Image? get cachedImage; + @override + ui.Size get size; + @override + CommandManager get commandManager; + @override + GraphicFactory get graphicFactory; + @override + @JsonKey(ignore: true) + _$$_CanvasStateDataCopyWith<_$_CanvasStateData> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/workspace/src/state/canvas/canvas_state_provider.dart b/packages/features/workspace_screen/lib/src/states/canvas_state_provider.dart similarity index 89% rename from lib/workspace/src/state/canvas/canvas_state_provider.dart rename to packages/features/workspace_screen/lib/src/states/canvas_state_provider.dart index 07db2775..8e13c3c3 100644 --- a/lib/workspace/src/state/canvas/canvas_state_provider.dart +++ b/packages/features/workspace_screen/lib/src/states/canvas_state_provider.dart @@ -1,13 +1,12 @@ import 'dart:ui'; +import 'package:command/command.dart'; +import 'package:command/command_providers.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/widgets.dart' as widgets; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/service/device_service.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_data.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:workspace_screen/src/service/device_service.dart'; +import 'package:workspace_screen/src/states/canvas_state_data.dart'; part 'canvas_state_provider.g.dart'; diff --git a/packages/features/workspace_screen/lib/src/states/canvas_state_provider.g.dart b/packages/features/workspace_screen/lib/src/states/canvas_state_provider.g.dart new file mode 100644 index 00000000..870eb159 --- /dev/null +++ b/packages/features/workspace_screen/lib/src/states/canvas_state_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'canvas_state_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$canvasStateHash() => r'90efe9797953e15366a523a27d015f733d8597db'; + +/// See also [CanvasState]. +@ProviderFor(CanvasState) +final canvasStateProvider = + NotifierProvider.internal( + CanvasState.new, + name: r'canvasStateProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$canvasStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$CanvasState = Notifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.dart b/packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.dart new file mode 100644 index 00000000..60b650ed --- /dev/null +++ b/packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.dart @@ -0,0 +1,15 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'tool_options_visibility_state_provider.g.dart'; + +@riverpod +class ToolOptionsVisibilityState extends _$ToolOptionsVisibilityState { + void toggleVisibility() { + state = !state; + } + + @override + bool build() { + return true; + } +} diff --git a/packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.g.dart b/packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.g.dart new file mode 100644 index 00000000..ebe6c0e7 --- /dev/null +++ b/packages/features/workspace_screen/lib/src/states/tool_options_visibility_state_provider.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tool_options_visibility_state_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$toolOptionsVisibilityStateHash() => + r'affb976d863865ff27bb7683bae487b4337cea8f'; + +/// See also [ToolOptionsVisibilityState]. +@ProviderFor(ToolOptionsVisibilityState) +final toolOptionsVisibilityStateProvider = + AutoDisposeNotifierProvider.internal( + ToolOptionsVisibilityState.new, + name: r'toolOptionsVisibilityStateProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$toolOptionsVisibilityStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ToolOptionsVisibilityState = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/workspace/src/state/workspace_state.dart b/packages/features/workspace_screen/lib/src/states/workspace_state.dart similarity index 100% rename from lib/workspace/src/state/workspace_state.dart rename to packages/features/workspace_screen/lib/src/states/workspace_state.dart diff --git a/lib/workspace/src/state/workspace_state_notifier.dart b/packages/features/workspace_screen/lib/src/states/workspace_state_notifier.dart similarity index 88% rename from lib/workspace/src/state/workspace_state_notifier.dart rename to packages/features/workspace_screen/lib/src/states/workspace_state_notifier.dart index b7b32729..30518c5d 100644 --- a/lib/workspace/src/state/workspace_state_notifier.dart +++ b/packages/features/workspace_screen/lib/src/states/workspace_state_notifier.dart @@ -1,7 +1,7 @@ +import 'package:command/command.dart'; +import 'package:command/command_providers.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; part 'workspace_state.dart'; diff --git a/lib/workspace/src/usecase/render_image_for_export.dart b/packages/features/workspace_screen/lib/src/usecase/render_image_for_export.dart similarity index 88% rename from lib/workspace/src/usecase/render_image_for_export.dart rename to packages/features/workspace_screen/lib/src/usecase/render_image_for_export.dart index e9905202..f27fe205 100644 --- a/lib/workspace/src/usecase/render_image_for_export.dart +++ b/packages/features/workspace_screen/lib/src/usecase/render_image_for_export.dart @@ -1,12 +1,10 @@ import 'dart:ui'; +import 'package:command/command.dart'; +import 'package:command/command_providers.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class RenderImageForExport { final Ref _ref; diff --git a/lib/ui/pocket_paint.dart b/packages/features/workspace_screen/lib/src/workspace_screen.dart similarity index 77% rename from lib/ui/pocket_paint.dart rename to packages/features/workspace_screen/lib/src/workspace_screen.dart index ee487439..9994f891 100644 --- a/lib/ui/pocket_paint.dart +++ b/packages/features/workspace_screen/lib/src/workspace_screen.dart @@ -1,23 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/io/io.dart'; -import 'package:paintroid/ui/drawing_space/bottom_nav_bar.dart'; -import 'package:paintroid/ui/drawing_space/exit_fullscreen_button.dart'; -import 'package:paintroid/ui/drawing_space/tool_options.dart'; -import 'package:paintroid/ui/io_handler.dart'; -import 'package:paintroid/ui/shared/top_app_bar.dart'; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:io_library/io_library.dart'; + import 'package:toast/toast.dart'; +import 'package:workspace_screen/workspace_screen.dart'; -class PocketPaint extends ConsumerStatefulWidget { - const PocketPaint({Key? key}) : super(key: key); +class WorkspaceScreen extends ConsumerStatefulWidget { + const WorkspaceScreen({Key? key}) : super(key: key); @override - ConsumerState createState() => _PocketPaintState(); + ConsumerState createState() => _WorkspaceScreenState(); } -class _PocketPaintState extends ConsumerState { +class _WorkspaceScreenState extends ConsumerState { void _toggleStatusBar(bool isFullscreen) { SystemChrome.setEnabledSystemUIMode( isFullscreen ? SystemUiMode.immersiveSticky : SystemUiMode.manual, diff --git a/packages/features/workspace_screen/lib/workspace_screen.dart b/packages/features/workspace_screen/lib/workspace_screen.dart new file mode 100644 index 00000000..23ebc9e6 --- /dev/null +++ b/packages/features/workspace_screen/lib/workspace_screen.dart @@ -0,0 +1,27 @@ +library workspace_screen; + +export 'src/components/bottom_bar/bottom_nav_bar.dart'; +export 'src/components/bottom_bar/bottom_nav_bar_items.dart'; +export 'src/components/bottom_bar/tool_options/stroke_cap_tool_option.dart'; +export 'src/components/bottom_bar/tool_options/stroke_tool_options.dart'; +export 'src/components/bottom_bar/tool_options/stroke_width_tool_option.dart'; +export 'src/components/bottom_bar/tool_options/tool_option.dart'; +export 'src/components/bottom_bar/tool_options/tool_options.dart'; +export 'src/components/bottom_bar/tools/tool_button.dart'; +export 'src/components/bottom_bar/tools/tools_bottom_sheet.dart'; +export 'src/components/drawing_surface/canvas_painter.dart'; +export 'src/components/drawing_surface/checkerboard_pattern.dart'; +export 'src/components/drawing_surface/command_painter.dart'; +export 'src/components/drawing_surface/drawing_canvas.dart'; +export 'src/components/drawing_surface/exit_fullscreen_button.dart'; +export 'src/components/top_bar/overflow_menu.dart'; +export 'src/components/top_bar/top_app_bar.dart'; +export 'src/models/image_with_pixel_info.dart'; +export 'src/service/device_service.dart'; +export 'src/states/canvas_dirty_state.dart'; +export 'src/states/canvas_state_data.dart'; +export 'src/states/canvas_state_provider.dart'; +export 'src/states/tool_options_visibility_state_provider.dart'; +export 'src/states/workspace_state_notifier.dart'; +export 'src/usecase/render_image_for_export.dart'; +export 'src/workspace_screen.dart'; diff --git a/packages/features/workspace_screen/pubspec.yaml b/packages/features/workspace_screen/pubspec.yaml new file mode 100644 index 00000000..a9f876a7 --- /dev/null +++ b/packages/features/workspace_screen/pubspec.yaml @@ -0,0 +1,55 @@ +name: workspace_screen +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + + flutter_localization: ^0.1.12 + intl: ^0.18.0 + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + freezed_annotation: ^2.4.1 + toast: ^0.3.0 + image: ^3.2.0 + oxidized: ^5.2.0 + + # Internal packages + component_library: + path: ../../component_library + l10n: + path: ../../l10n + database: + path: ../../database + command: + path: ../../command + io_library: + path: ../../io_library + tools: + path: ../../tools + +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + +flutter: + uses-material-design: true diff --git a/test/unit/workspace/usecase/render_image_for_export_test.dart b/packages/features/workspace_screen/test/unit/render_image_for_export_test.dart similarity index 93% rename from test/unit/workspace/usecase/render_image_for_export_test.dart rename to packages/features/workspace_screen/test/unit/render_image_for_export_test.dart index bf6c3f74..e1c8e4ff 100644 --- a/test/unit/workspace/usecase/render_image_for_export_test.dart +++ b/packages/features/workspace_screen/test/unit/render_image_for_export_test.dart @@ -1,17 +1,12 @@ import 'dart:ui'; +import 'package:command/command.dart'; +import 'package:command/command_providers.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/core/image_with_pixel_info.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_data.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart'; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:workspace_screen/workspace_screen.dart'; import 'render_image_for_export_test.mocks.dart'; diff --git a/packages/features/workspace_screen/test/unit/render_image_for_export_test.mocks.dart b/packages/features/workspace_screen/test/unit/render_image_for_export_test.mocks.dart new file mode 100644 index 00000000..9bfa614d --- /dev/null +++ b/packages/features/workspace_screen/test/unit/render_image_for_export_test.mocks.dart @@ -0,0 +1,684 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in workspace_screen/test/unit/render_image_for_export_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:typed_data' as _i3; +import 'dart:ui' as _i2; + +import 'package:command/command.dart' as _i4; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeRect_0 extends _i1.SmartFake implements _i2.Rect { + _FakeRect_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [Canvas]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCanvas extends _i1.Mock implements _i2.Canvas { + MockCanvas() { + _i1.throwOnMissingStub(this); + } + + @override + void save() => super.noSuchMethod( + Invocation.method( + #save, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void saveLayer( + _i2.Rect? bounds, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #saveLayer, + [ + bounds, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void restore() => super.noSuchMethod( + Invocation.method( + #restore, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void restoreToCount(int? count) => super.noSuchMethod( + Invocation.method( + #restoreToCount, + [count], + ), + returnValueForMissingStub: null, + ); + + @override + int getSaveCount() => (super.noSuchMethod( + Invocation.method( + #getSaveCount, + [], + ), + returnValue: 0, + ) as int); + + @override + void translate( + double? dx, + double? dy, + ) => + super.noSuchMethod( + Invocation.method( + #translate, + [ + dx, + dy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void scale( + double? sx, [ + double? sy, + ]) => + super.noSuchMethod( + Invocation.method( + #scale, + [ + sx, + sy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void rotate(double? radians) => super.noSuchMethod( + Invocation.method( + #rotate, + [radians], + ), + returnValueForMissingStub: null, + ); + + @override + void skew( + double? sx, + double? sy, + ) => + super.noSuchMethod( + Invocation.method( + #skew, + [ + sx, + sy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void transform(_i3.Float64List? matrix4) => super.noSuchMethod( + Invocation.method( + #transform, + [matrix4], + ), + returnValueForMissingStub: null, + ); + + @override + _i3.Float64List getTransform() => (super.noSuchMethod( + Invocation.method( + #getTransform, + [], + ), + returnValue: _i3.Float64List(0), + ) as _i3.Float64List); + + @override + void clipRect( + _i2.Rect? rect, { + _i2.ClipOp? clipOp = _i2.ClipOp.intersect, + bool? doAntiAlias = true, + }) => + super.noSuchMethod( + Invocation.method( + #clipRect, + [rect], + { + #clipOp: clipOp, + #doAntiAlias: doAntiAlias, + }, + ), + returnValueForMissingStub: null, + ); + + @override + void clipRRect( + _i2.RRect? rrect, { + bool? doAntiAlias = true, + }) => + super.noSuchMethod( + Invocation.method( + #clipRRect, + [rrect], + {#doAntiAlias: doAntiAlias}, + ), + returnValueForMissingStub: null, + ); + + @override + void clipPath( + _i2.Path? path, { + bool? doAntiAlias = true, + }) => + super.noSuchMethod( + Invocation.method( + #clipPath, + [path], + {#doAntiAlias: doAntiAlias}, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.Rect getLocalClipBounds() => (super.noSuchMethod( + Invocation.method( + #getLocalClipBounds, + [], + ), + returnValue: _FakeRect_0( + this, + Invocation.method( + #getLocalClipBounds, + [], + ), + ), + ) as _i2.Rect); + + @override + _i2.Rect getDestinationClipBounds() => (super.noSuchMethod( + Invocation.method( + #getDestinationClipBounds, + [], + ), + returnValue: _FakeRect_0( + this, + Invocation.method( + #getDestinationClipBounds, + [], + ), + ), + ) as _i2.Rect); + + @override + void drawColor( + _i2.Color? color, + _i2.BlendMode? blendMode, + ) => + super.noSuchMethod( + Invocation.method( + #drawColor, + [ + color, + blendMode, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawLine( + _i2.Offset? p1, + _i2.Offset? p2, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawLine, + [ + p1, + p2, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPaint(_i2.Paint? paint) => super.noSuchMethod( + Invocation.method( + #drawPaint, + [paint], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRect( + _i2.Rect? rect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRect, + [ + rect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRRect( + _i2.RRect? rrect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRRect, + [ + rrect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawDRRect( + _i2.RRect? outer, + _i2.RRect? inner, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawDRRect, + [ + outer, + inner, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawOval( + _i2.Rect? rect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawOval, + [ + rect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawCircle( + _i2.Offset? c, + double? radius, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawCircle, + [ + c, + radius, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawArc( + _i2.Rect? rect, + double? startAngle, + double? sweepAngle, + bool? useCenter, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawArc, + [ + rect, + startAngle, + sweepAngle, + useCenter, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPath( + _i2.Path? path, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawPath, + [ + path, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawImage( + _i2.Image? image, + _i2.Offset? offset, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawImage, + [ + image, + offset, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawImageRect( + _i2.Image? image, + _i2.Rect? src, + _i2.Rect? dst, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawImageRect, + [ + image, + src, + dst, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawImageNine( + _i2.Image? image, + _i2.Rect? center, + _i2.Rect? dst, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawImageNine, + [ + image, + center, + dst, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPicture(_i2.Picture? picture) => super.noSuchMethod( + Invocation.method( + #drawPicture, + [picture], + ), + returnValueForMissingStub: null, + ); + + @override + void drawParagraph( + _i2.Paragraph? paragraph, + _i2.Offset? offset, + ) => + super.noSuchMethod( + Invocation.method( + #drawParagraph, + [ + paragraph, + offset, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawPoints( + _i2.PointMode? pointMode, + List<_i2.Offset>? points, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawPoints, + [ + pointMode, + points, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRawPoints( + _i2.PointMode? pointMode, + _i3.Float32List? points, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRawPoints, + [ + pointMode, + points, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawVertices( + _i2.Vertices? vertices, + _i2.BlendMode? blendMode, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawVertices, + [ + vertices, + blendMode, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawAtlas( + _i2.Image? atlas, + List<_i2.RSTransform>? transforms, + List<_i2.Rect>? rects, + List<_i2.Color>? colors, + _i2.BlendMode? blendMode, + _i2.Rect? cullRect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawAtlas, + [ + atlas, + transforms, + rects, + colors, + blendMode, + cullRect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawRawAtlas( + _i2.Image? atlas, + _i3.Float32List? rstTransforms, + _i3.Float32List? rects, + _i3.Int32List? colors, + _i2.BlendMode? blendMode, + _i2.Rect? cullRect, + _i2.Paint? paint, + ) => + super.noSuchMethod( + Invocation.method( + #drawRawAtlas, + [ + atlas, + rstTransforms, + rects, + colors, + blendMode, + cullRect, + paint, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void drawShadow( + _i2.Path? path, + _i2.Color? color, + double? elevation, + bool? transparentOccluder, + ) => + super.noSuchMethod( + Invocation.method( + #drawShadow, + [ + path, + color, + elevation, + transparentOccluder, + ], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [CommandManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandManager extends _i1.Mock implements _i4.CommandManager { + MockCommandManager() { + _i1.throwOnMissingStub(this); + } + + @override + Iterable<_i4.Command> get history => (super.noSuchMethod( + Invocation.getter(#history), + returnValue: <_i4.Command>[], + ) as Iterable<_i4.Command>); + + @override + int get count => (super.noSuchMethod( + Invocation.getter(#count), + returnValue: 0, + ) as int); + + @override + void addGraphicCommand(_i4.GraphicCommand? command) => super.noSuchMethod( + Invocation.method( + #addGraphicCommand, + [command], + ), + returnValueForMissingStub: null, + ); + + @override + void executeLastCommand(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeLastCommand, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void executeAllCommands(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeAllCommands, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void discardLastCommand() => super.noSuchMethod( + Invocation.method( + #discardLastCommand, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void clearHistory({Iterable<_i4.Command>? newCommands}) => super.noSuchMethod( + Invocation.method( + #clearHistory, + [], + {#newCommands: newCommands}, + ), + returnValueForMissingStub: null, + ); +} diff --git a/packages/features/workspace_screen/test/widget/bottom_control_navigation_bar_test.dart b/packages/features/workspace_screen/test/widget/bottom_control_navigation_bar_test.dart new file mode 100644 index 00000000..328f5e33 --- /dev/null +++ b/packages/features/workspace_screen/test/widget/bottom_control_navigation_bar_test.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:l10n/l10n.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; + +import 'bottom_nav_bar_interactions.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + const VISIBLE = 1.0; + const INVISIBLE = 0.0; + + late Widget sut; + + setUp(() { + sut = const ProviderScope( + child: MaterialApp( + home: WorkspaceScreen(), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], + ), + ); + }); + + group('BottomNavBarItem.TOOLS', () { + testWidgets( + 'Select eraser and brush tool and verify NavigationBarItem changes', + (WidgetTester tester) async { + const eraserToolData = ToolData.ERASER; + const brushToolData = ToolData.BRUSH; + + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + await bottomNavBarInteractions + .selectTool(eraserToolData) + .then((_) => _.checkActiveToolIconAndLabel(eraserToolData)); + + await bottomNavBarInteractions + .selectTool(brushToolData) + .then((_) => _.checkActiveToolIconAndLabel(brushToolData)); + }); + }); + + group('BottomNavBarItem.CURRENT_TOOL', () { + testWidgets('test if width tool-option is visible when starting app', + (WidgetTester tester) async { + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + + final animatedOpacity = bottomNavBarInteractions + .getAnimatedOpacityFinder(StrokeWidthToolOption); + + var animatedOpacityWidget = + tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + }); + + testWidgets('test if width tool-option is invisible after clicking once', + (WidgetTester tester) async { + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + + final animatedOpacity = bottomNavBarInteractions + .getAnimatedOpacityFinder(StrokeWidthToolOption); + + var animatedOpacityWidget = + tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + + await bottomNavBarInteractions.clickCurrentTool(); + + animatedOpacityWidget = tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(INVISIBLE)); + }); + + testWidgets('test if width tool-option is visible after clicking twice', + (WidgetTester tester) async { + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + final animatedOpacity = bottomNavBarInteractions + .getAnimatedOpacityFinder(StrokeWidthToolOption); + + var animatedOpacityWidget = + tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + + await bottomNavBarInteractions.clickCurrentTool(); + + animatedOpacityWidget = tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(INVISIBLE)); + + await bottomNavBarInteractions.clickCurrentTool(); + + animatedOpacityWidget = tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + }); + + testWidgets('test if cap tool-option is visible when starting app', + (WidgetTester tester) async { + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + final animatedOpacity = bottomNavBarInteractions + .getAnimatedOpacityFinder(StrokeCapToolOption); + + var animatedOpacityWidget = + tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + }); + + testWidgets('test if cap tool-option is invisible after clicking once', + (WidgetTester tester) async { + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + final animatedOpacity = bottomNavBarInteractions + .getAnimatedOpacityFinder(StrokeCapToolOption); + + var animatedOpacityWidget = + tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + + await bottomNavBarInteractions.clickCurrentTool(); + + animatedOpacityWidget = tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(INVISIBLE)); + }); + + testWidgets('test if cap tool-option is visible after clicking twice', + (WidgetTester tester) async { + await tester.pumpWidget(sut); + + final bottomNavBarInteractions = BottomNavBarInteractions(tester); + final animatedOpacity = bottomNavBarInteractions + .getAnimatedOpacityFinder(StrokeCapToolOption); + + var animatedOpacityWidget = + tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + + await bottomNavBarInteractions.clickCurrentTool(); + + animatedOpacityWidget = tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(INVISIBLE)); + + await bottomNavBarInteractions.clickCurrentTool(); + + animatedOpacityWidget = tester.widget(animatedOpacity); + expect(animatedOpacityWidget.opacity, equals(VISIBLE)); + }); + }); +} diff --git a/test/widget/utils/interactions/bottom_nav_bar_interactions.dart b/packages/features/workspace_screen/test/widget/bottom_nav_bar_interactions.dart similarity index 72% rename from test/widget/utils/interactions/bottom_nav_bar_interactions.dart rename to packages/features/workspace_screen/test/widget/bottom_nav_bar_interactions.dart index f7f888ce..8b9f3c5a 100644 --- a/test/widget/utils/interactions/bottom_nav_bar_interactions.dart +++ b/packages/features/workspace_screen/test/widget/bottom_nav_bar_interactions.dart @@ -1,8 +1,7 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/shared/bottom_nav_bar_icon.dart'; -import 'package:paintroid/ui/shared/icon_button_with_label.dart'; +import 'package:tools/tools.dart'; class BottomNavBarInteractions { final WidgetTester _tester; @@ -18,6 +17,14 @@ class BottomNavBarInteractions { return this; } + Future clickCurrentTool() async { + final firstNavDestination = find.byType(NavigationDestination).at(1); + expect(firstNavDestination, findsOneWidget); + await _tester.tap(firstNavDestination); + await _tester.pumpAndSettle(); + return this; + } + Future selectTool(ToolData toolData) async { await openBottomToolSheet(); @@ -54,4 +61,14 @@ class BottomNavBarInteractions { matching: find.byType(IconButton), ); } + + Finder getAnimatedOpacityFinder(Type type) { + final child = find.byType(type); + final animatedOpacityFinder = find.ancestor( + of: child, + matching: find.byType(AnimatedOpacity), + ); + expect(animatedOpacityFinder, findsOneWidget); + return animatedOpacityFinder; + } } diff --git a/test/widget/utils/interactions/canvas_interactions.dart b/packages/features/workspace_screen/test/widget/canvas_interactions.dart similarity index 96% rename from test/widget/utils/interactions/canvas_interactions.dart rename to packages/features/workspace_screen/test/widget/canvas_interactions.dart index a1fceb54..bccc2904 100644 --- a/test/widget/utils/interactions/canvas_interactions.dart +++ b/packages/features/workspace_screen/test/widget/canvas_interactions.dart @@ -5,7 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:image/image.dart' as img; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class CanvasInteractions { final WidgetTester _tester; diff --git a/test/widget/tool/eraser_tool_test.dart b/packages/features/workspace_screen/test/widget/eraser_tool_test.dart similarity index 75% rename from test/widget/tool/eraser_tool_test.dart rename to packages/features/workspace_screen/test/widget/eraser_tool_test.dart index 6f41b41e..79610ee7 100644 --- a/test/widget/tool/eraser_tool_test.dart +++ b/packages/features/workspace_screen/test/widget/eraser_tool_test.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:paintroid/service/device_service.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/pocket_paint.dart'; +import 'package:l10n/l10n.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; -import '../utils/utils.dart'; +import 'bottom_nav_bar_interactions.dart'; +import 'canvas_interactions.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -21,7 +23,11 @@ void main() { .overrideWith((ref) => Future.value(const Size(600, 600))) ], child: const MaterialApp( - home: PocketPaint(), + home: WorkspaceScreen(), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], ), ), ); diff --git a/test/widget/tool/hand_tool_test.dart b/packages/features/workspace_screen/test/widget/hand_tool_test.dart similarity index 83% rename from test/widget/tool/hand_tool_test.dart rename to packages/features/workspace_screen/test/widget/hand_tool_test.dart index 0f8edb82..8230eccd 100644 --- a/test/widget/tool/hand_tool_test.dart +++ b/packages/features/workspace_screen/test/widget/hand_tool_test.dart @@ -1,13 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:paintroid/service/device_service.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/pocket_paint.dart'; +import 'package:l10n/l10n.dart'; +import 'package:tools/tools.dart'; +import 'package:workspace_screen/workspace_screen.dart'; -import '../utils/utils.dart'; -import '../utils/interactions/interactive_viewer_interactions.dart'; +import 'bottom_nav_bar_interactions.dart'; +import 'interactive_viewer_interactions.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -21,7 +22,11 @@ void main() { .overrideWith((ref) => Future.value(const Size(600, 600))) ], child: const MaterialApp( - home: PocketPaint(), + home: WorkspaceScreen(), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], ), ); }); diff --git a/test/widget/utils/interactions/interactive_viewer_interactions.dart b/packages/features/workspace_screen/test/widget/interactive_viewer_interactions.dart similarity index 100% rename from test/widget/utils/interactions/interactive_viewer_interactions.dart rename to packages/features/workspace_screen/test/widget/interactive_viewer_interactions.dart diff --git a/test/widget/ui/pocket_paint_test.dart b/packages/features/workspace_screen/test/widget/workspace_screen_test.dart similarity index 83% rename from test/widget/ui/pocket_paint_test.dart rename to packages/features/workspace_screen/test/widget/workspace_screen_test.dart index fb603ad0..bd05bca4 100644 --- a/test/widget/ui/pocket_paint_test.dart +++ b/packages/features/workspace_screen/test/widget/workspace_screen_test.dart @@ -1,11 +1,10 @@ +import 'package:command/command.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/command/command.dart' show CommandManager; -import 'package:paintroid/ui/pocket_paint.dart'; -import 'package:paintroid/ui/shared/overflow_menu.dart'; -import 'package:paintroid/ui/shared/top_app_bar.dart'; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:l10n/l10n.dart'; +import 'package:workspace_screen/workspace_screen.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; class FakeCommandManager extends Fake implements CommandManager {} @@ -15,7 +14,11 @@ void main() { setUp(() { sut = const ProviderScope( child: MaterialApp( - home: PocketPaint(), + home: WorkspaceScreen(), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], ), ); }); @@ -57,7 +60,11 @@ void main() { WorkspaceStateNotifier(testWorkspaceState, fakeCommandManager)) ], child: const MaterialApp( - home: PocketPaint(), + home: WorkspaceScreen(), + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], ), ); }); diff --git a/packages/io_library/.metadata b/packages/io_library/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/io_library/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/io_library/analysis_options.yaml b/packages/io_library/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/io_library/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/packages/io_library/lib/io_library.dart b/packages/io_library/lib/io_library.dart new file mode 100644 index 00000000..beda5731 --- /dev/null +++ b/packages/io_library/lib/io_library.dart @@ -0,0 +1,34 @@ +library io_library; + +export 'src/enums/image_format.dart'; +export 'src/enums/image_location.dart'; +export 'src/failure/load_image_failure.dart'; +export 'src/failure/save_image_failure.dart'; +export 'src/io_handler.dart'; +export 'src/json_serialization/converter/paint_converter.dart'; +export 'src/json_serialization/converter/path_action_converter.dart'; +export 'src/json_serialization/converter/path_with_action_history_converter.dart'; +export 'src/json_serialization/versioning/serializer_version.dart'; +export 'src/json_serialization/versioning/version_strategy.dart'; +export 'src/models/catrobat_image.dart'; +export 'src/models/failure.dart'; +export 'src/models/image_from_file.dart'; +export 'src/models/image_meta_data.dart'; +export 'src/models/loggable_mixin.dart'; +export 'src/service/file_service.dart'; +export 'src/service/image_service.dart'; +export 'src/service/permission_service.dart'; +export 'src/service/photo_library_service.dart'; +export 'src/ui/about_dialog.dart'; +export 'src/ui/delete_project_dialog.dart'; +export 'src/ui/discard_changes_dialog.dart'; +export 'src/ui/generic_dialog.dart'; +export 'src/ui/image_format_info.dart'; +export 'src/ui/load_image_dialog.dart'; +export 'src/ui/overwrite_dialog.dart'; +export 'src/ui/project_details_dialog.dart'; +export 'src/ui/save_image_dialog.dart'; +export 'src/usecase/load_image_from_file_manager.dart'; +export 'src/usecase/load_image_from_photo_library.dart'; +export 'src/usecase/save_as_catrobat_image.dart'; +export 'src/usecase/save_as_raster_image.dart'; diff --git a/lib/io/src/entity/image_format.dart b/packages/io_library/lib/src/enums/image_format.dart similarity index 81% rename from lib/io/src/entity/image_format.dart rename to packages/io_library/lib/src/enums/image_format.dart index 204d6d4b..b0f81771 100644 --- a/lib/io/src/entity/image_format.dart +++ b/packages/io_library/lib/src/enums/image_format.dart @@ -1,5 +1,3 @@ -part of 'image_meta_data.dart'; - enum ImageFormat { png('png'), jpg('jpg'), diff --git a/lib/io/src/entity/image_location.dart b/packages/io_library/lib/src/enums/image_location.dart similarity index 100% rename from lib/io/src/entity/image_location.dart rename to packages/io_library/lib/src/enums/image_location.dart diff --git a/lib/io/src/failure/load_image_failure.dart b/packages/io_library/lib/src/failure/load_image_failure.dart similarity index 90% rename from lib/io/src/failure/load_image_failure.dart rename to packages/io_library/lib/src/failure/load_image_failure.dart index 8d3b9b1c..191a66a0 100644 --- a/lib/io/src/failure/load_image_failure.dart +++ b/packages/io_library/lib/src/failure/load_image_failure.dart @@ -1,4 +1,4 @@ -import 'package:paintroid/core/failure.dart'; +import 'package:io_library/io_library.dart'; class LoadImageFailure extends Failure { const LoadImageFailure._(super.message); diff --git a/lib/io/src/failure/save_image_failure.dart b/packages/io_library/lib/src/failure/save_image_failure.dart similarity index 90% rename from lib/io/src/failure/save_image_failure.dart rename to packages/io_library/lib/src/failure/save_image_failure.dart index e00594bb..a0b4aff4 100644 --- a/lib/io/src/failure/save_image_failure.dart +++ b/packages/io_library/lib/src/failure/save_image_failure.dart @@ -1,4 +1,4 @@ -import 'package:paintroid/core/failure.dart'; +import 'package:io_library/io_library.dart'; class SaveImageFailure extends Failure { const SaveImageFailure._(super.message); diff --git a/lib/ui/io_handler.dart b/packages/io_library/lib/src/io_handler.dart similarity index 89% rename from lib/ui/io_handler.dart rename to packages/io_library/lib/src/io_handler.dart index 4b7ef0e8..72c0a782 100644 --- a/lib/ui/io_handler.dart +++ b/packages/io_library/lib/src/io_handler.dart @@ -1,14 +1,14 @@ +import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; -import 'package:flutter/widgets.dart'; +import 'package:command/command_providers.dart'; +import 'package:component_library/component_library.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/core/toast_utils.dart'; -import 'package:paintroid/io/io.dart'; -import 'package:paintroid/workspace/src/state/canvas/canvas_state_provider.dart'; -import 'package:paintroid/workspace/workspace.dart'; +import 'package:workspace_screen/workspace_screen.dart'; class IOHandler { final Ref ref; @@ -203,8 +203,19 @@ class IOHandler { final canvasState = ref.read(canvasStateProvider); final imgWidth = canvasState.size.width.toInt(); final imgHeight = canvasState.size.height.toInt(); - final catrobatImage = CatrobatImage( - commands, imgWidth, imgHeight, canvasState.backgroundImage); + Uint8List? backgroundImageData; + final backgroundImage = canvasState.backgroundImage; + if (backgroundImage != null) { + final result = + await ref.read(IImageService.provider).exportAsPng(backgroundImage); + backgroundImageData = + result.unwrapOrElse((failure) => throw failure.message); + } + + final String backgroundImageAsString = + backgroundImageData != null ? base64Encode(backgroundImageData) : ''; + final catrobatImage = + CatrobatImage(commands, imgWidth, imgHeight, backgroundImageAsString); final saveAsCatrobatImage = ref.read(SaveAsCatrobatImage.provider); final result = await saveAsCatrobatImage(imageData, catrobatImage, isAProject); diff --git a/packages/io_library/lib/src/json_serialization/converter/paint_converter.dart b/packages/io_library/lib/src/json_serialization/converter/paint_converter.dart new file mode 100644 index 00000000..902a4c0c --- /dev/null +++ b/packages/io_library/lib/src/json_serialization/converter/paint_converter.dart @@ -0,0 +1,50 @@ +import 'dart:ui'; + +import 'package:io_library/io_library.dart'; +import 'package:json_annotation/json_annotation.dart'; + +class PaintConverter implements JsonConverter> { + const PaintConverter(); + + @override + Paint fromJson(Map json) { + Paint paint = Paint(); + + int version = json['version'] as int; + if (version >= Version.v1) { + paint.color = Color(json['color']); + paint.strokeWidth = json['strokeWidth']; + paint.strokeCap = StrokeCap.values[json['strokeCap']]; + paint.isAntiAlias = json['isAntiAlias']; + paint.style = PaintingStyle.values[json['style']]; + paint.strokeJoin = StrokeJoin.values[json['strokeJoin']]; + paint.blendMode = BlendMode.values[json['blendMode']]; + } + if (version >= Version.v2) { + // paint.newAttribute = json['newAttribute']; + } + return paint; + } + + // Never remove attributes, it will cause errors in older versions!! + // Only add new attributes at the end of the map and increase the version number. + @override + Map toJson(Paint paint) { + Map json = {}; + if (SerializerVersion.PAINT_VERSION >= Version.v1) { + json['version'] = SerializerVersion.PAINT_VERSION; + json['color'] = paint.color.value; + json['strokeWidth'] = paint.strokeWidth; + json['strokeCap'] = paint.strokeCap.index; + json['isAntiAlias'] = paint.isAntiAlias; + json['style'] = paint.style.index; + json['strokeJoin'] = paint.strokeJoin.index; + json['blendMode'] = paint.blendMode.index; + } + if (SerializerVersion.PAINT_VERSION >= Version.v2) { + // json['newAttribute'] = paint.newAttribute; + } + + return json; + } +} diff --git a/packages/io_library/lib/src/json_serialization/converter/path_action_converter.dart b/packages/io_library/lib/src/json_serialization/converter/path_action_converter.dart new file mode 100644 index 00000000..d1dd5dd9 --- /dev/null +++ b/packages/io_library/lib/src/json_serialization/converter/path_action_converter.dart @@ -0,0 +1,44 @@ +import 'package:command/command.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:io_library/io_library.dart'; + +class PathActionConverter + implements JsonConverter> { + const PathActionConverter(); + + @override + PathAction fromJson(Map json) { + switch (json['type'] as String) { + case SerializerType.MOVE_TO_ACTION: + return MoveToAction(json['x'] as double, json['y'] as double); + case SerializerType.LINE_TO_ACTION: + return LineToAction(json['x'] as double, json['y'] as double); + case SerializerType.CLOSE_ACTION: + return const CloseAction(); + default: + return const CloseAction(); + } + } + + @override + Map toJson(PathAction action) { + switch (action.runtimeType) { + case MoveToAction: + action as MoveToAction; + return { + 'type': SerializerType.MOVE_TO_ACTION, + 'x': action.x, + 'y': action.y, + }; + case LineToAction: + action as LineToAction; + return { + 'type': SerializerType.LINE_TO_ACTION, + 'x': action.x, + 'y': action.y, + }; + default: + return {'type': SerializerType.CLOSE_ACTION}; + } + } +} diff --git a/packages/io_library/lib/src/json_serialization/converter/path_with_action_history_converter.dart b/packages/io_library/lib/src/json_serialization/converter/path_with_action_history_converter.dart new file mode 100644 index 00000000..0edb3902 --- /dev/null +++ b/packages/io_library/lib/src/json_serialization/converter/path_with_action_history_converter.dart @@ -0,0 +1,34 @@ +import 'package:command/command.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:io_library/io_library.dart'; + +class PathWithActionHistoryConverter + implements JsonConverter> { + const PathWithActionHistoryConverter(); + + @override + PathWithActionHistory fromJson(Map json) { + var pathWithActionHistory = PathWithActionHistory(); + var actionsJson = json['actions'] as List; + for (var actionJson in actionsJson) { + var action = const PathActionConverter() + .fromJson(actionJson as Map); + + if (action is MoveToAction) { + pathWithActionHistory.moveTo(action.x, action.y); + } else if (action is LineToAction) { + pathWithActionHistory.lineTo(action.x, action.y); + } else if (action is CloseAction) { + pathWithActionHistory.close(); + } + } + return pathWithActionHistory; + } + + @override + Map toJson(PathWithActionHistory pathWithActionHistory) => { + 'actions': pathWithActionHistory.actions + .map((action) => const PathActionConverter().toJson(action)) + .toList(), + }; +} diff --git a/packages/io_library/lib/src/json_serialization/versioning/serializer_version.dart b/packages/io_library/lib/src/json_serialization/versioning/serializer_version.dart new file mode 100644 index 00000000..bb592105 --- /dev/null +++ b/packages/io_library/lib/src/json_serialization/versioning/serializer_version.dart @@ -0,0 +1,19 @@ +class SerializerVersion { + static const int PAINT_VERSION = Version.v1; + static const int CATROBAT_IMAGE_VERSION = Version.v1; + static const int DRAW_PATH_COMMAND_VERSION = Version.v1; +} + +class Version { + static const int v1 = 1; + static const int v2 = 2; + static const int v3 = 3; +// ... +} + +class SerializerType { + static const String DRAW_PATH_COMMAND = 'DrawPathCommand'; + static const String MOVE_TO_ACTION = 'MoveToAction'; + static const String LINE_TO_ACTION = 'LineToAction'; + static const String CLOSE_ACTION = 'CloseAction'; +} diff --git a/packages/io_library/lib/src/json_serialization/versioning/version_strategy.dart b/packages/io_library/lib/src/json_serialization/versioning/version_strategy.dart new file mode 100644 index 00000000..0f4ca73f --- /dev/null +++ b/packages/io_library/lib/src/json_serialization/versioning/version_strategy.dart @@ -0,0 +1,25 @@ +import 'package:io_library/io_library.dart'; + +abstract class IVersionStrategy { + int getCatrobatImageVersion(); + int getDrawPathCommandVersion(); +} + +class ProductionVersionStrategy implements IVersionStrategy { + @override + int getCatrobatImageVersion() => SerializerVersion.CATROBAT_IMAGE_VERSION; + + @override + int getDrawPathCommandVersion() => + SerializerVersion.DRAW_PATH_COMMAND_VERSION; +} + +class VersionStrategyManager { + static IVersionStrategy _strategy = ProductionVersionStrategy(); + + static void setStrategy(IVersionStrategy strategy) { + _strategy = strategy; + } + + static IVersionStrategy get strategy => _strategy; +} diff --git a/packages/io_library/lib/src/models/catrobat_image.dart b/packages/io_library/lib/src/models/catrobat_image.dart new file mode 100644 index 00000000..49eb9c04 --- /dev/null +++ b/packages/io_library/lib/src/models/catrobat_image.dart @@ -0,0 +1,57 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:command/command.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:io_library/io_library.dart'; + +part 'catrobat_image.g.dart'; + +@JsonSerializable() +class CatrobatImage { + final String magicValue; + final int version; + final int width; + final int height; + final Iterable commands; + final String backgroundImage; + + CatrobatImage( + this.commands, + this.width, + this.height, + this.backgroundImage, { + int? version, + this.magicValue = 'CATROBAT', + }) : version = version ?? + VersionStrategyManager.strategy.getCatrobatImageVersion(); + + Uint8List toBytes() { + Map jsonMap = toJson(); + String jsonString = json.encode(jsonMap); + return utf8.encode(jsonString) as Uint8List; + } + + static CatrobatImage fromBytes(Uint8List bytes) { + String jsonString = utf8.decode(bytes); + Map jsonMap = json.decode(jsonString); + return CatrobatImage.fromJson(jsonMap); + } + + Map toJson() => _$CatrobatImageToJson(this); + + factory CatrobatImage.fromJson(Map json) { + int version = json['version'] as int; + + switch (version) { + case Version.v1: + return _$CatrobatImageFromJson(json); + case Version.v2: + // For different versions of CatrobatImage the deserialization + // has to be implemented manually. + // Autogenerated code can only be used for one version + default: + return _$CatrobatImageFromJson(json); + } + } +} diff --git a/packages/io_library/lib/src/models/catrobat_image.g.dart b/packages/io_library/lib/src/models/catrobat_image.g.dart new file mode 100644 index 00000000..9d900f86 --- /dev/null +++ b/packages/io_library/lib/src/models/catrobat_image.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'catrobat_image.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CatrobatImage _$CatrobatImageFromJson(Map json) => + CatrobatImage( + (json['commands'] as List) + .map((e) => Command.fromJson(e as Map)), + json['width'] as int, + json['height'] as int, + json['backgroundImage'] as String, + version: json['version'] as int?, + magicValue: json['magicValue'] as String? ?? 'CATROBAT', + ); + +Map _$CatrobatImageToJson(CatrobatImage instance) => + { + 'magicValue': instance.magicValue, + 'version': instance.version, + 'width': instance.width, + 'height': instance.height, + 'commands': instance.commands.toList(), + 'backgroundImage': instance.backgroundImage, + }; diff --git a/lib/core/failure.dart b/packages/io_library/lib/src/models/failure.dart similarity index 100% rename from lib/core/failure.dart rename to packages/io_library/lib/src/models/failure.dart diff --git a/lib/io/src/entity/image_from_file.dart b/packages/io_library/lib/src/models/image_from_file.dart similarity index 89% rename from lib/io/src/entity/image_from_file.dart rename to packages/io_library/lib/src/models/image_from_file.dart index 86a63de1..7183b72c 100644 --- a/lib/io/src/entity/image_from_file.dart +++ b/packages/io_library/lib/src/models/image_from_file.dart @@ -1,6 +1,6 @@ import 'dart:ui'; -import 'package:paintroid/io/io.dart'; +import 'package:io_library/io_library.dart'; class ImageFromFile { final Image? rasterImage; diff --git a/lib/io/src/entity/image_meta_data.dart b/packages/io_library/lib/src/models/image_meta_data.dart similarity index 94% rename from lib/io/src/entity/image_meta_data.dart rename to packages/io_library/lib/src/models/image_meta_data.dart index 4e25638a..ee9385c3 100644 --- a/lib/io/src/entity/image_meta_data.dart +++ b/packages/io_library/lib/src/models/image_meta_data.dart @@ -1,4 +1,4 @@ -part 'image_format.dart'; +import 'package:io_library/io_library.dart'; abstract class ImageMetaData { final String name; diff --git a/lib/core/loggable_mixin.dart b/packages/io_library/lib/src/models/loggable_mixin.dart similarity index 100% rename from lib/core/loggable_mixin.dart rename to packages/io_library/lib/src/models/loggable_mixin.dart diff --git a/lib/io/src/service/file_service.dart b/packages/io_library/lib/src/service/file_service.dart similarity index 96% rename from lib/io/src/service/file_service.dart rename to packages/io_library/lib/src/service/file_service.dart index 26dbafda..2d72e9b9 100644 --- a/lib/io/src/service/file_service.dart +++ b/packages/io_library/lib/src/service/file_service.dart @@ -3,10 +3,9 @@ import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; -import 'package:paintroid/io/io.dart'; + import 'package:path_provider/path_provider.dart'; abstract class IFileService { diff --git a/lib/io/src/service/image_service.dart b/packages/io_library/lib/src/service/image_service.dart similarity index 91% rename from lib/io/src/service/image_service.dart rename to packages/io_library/lib/src/service/image_service.dart index b6a68a57..353a736c 100644 --- a/lib/io/src/service/image_service.dart +++ b/packages/io_library/lib/src/service/image_service.dart @@ -5,11 +5,8 @@ import 'dart:ui' as ui; import 'package:flutter/painting.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image/image.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; -import 'package:paintroid/io/src/failure/load_image_failure.dart'; -import 'package:paintroid/io/src/failure/save_image_failure.dart'; abstract class IImageService { Future> import(Uint8List fileData); diff --git a/lib/io/src/service/permission_service.dart b/packages/io_library/lib/src/service/permission_service.dart similarity index 94% rename from lib/io/src/service/permission_service.dart rename to packages/io_library/lib/src/service/permission_service.dart index 327ae91f..1095f64e 100644 --- a/lib/io/src/service/permission_service.dart +++ b/packages/io_library/lib/src/service/permission_service.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; +import 'package:io_library/io_library.dart'; import 'package:permission_handler/permission_handler.dart'; abstract class IPermissionService { @@ -97,6 +97,10 @@ class PermissionService with LoggableMixin implements IPermissionService { case PermissionStatus.denied: logger.warning('User explicitly denied $permission'); break; + default: + logger + .warning('Undefinied permission status. This should never happen.'); + break; } return false; } diff --git a/lib/io/src/service/photo_library_service.dart b/packages/io_library/lib/src/service/photo_library_service.dart similarity index 88% rename from lib/io/src/service/photo_library_service.dart rename to packages/io_library/lib/src/service/photo_library_service.dart index 853d9556..1d247b55 100644 --- a/lib/io/src/service/photo_library_service.dart +++ b/packages/io_library/lib/src/service/photo_library_service.dart @@ -1,11 +1,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; -import 'package:paintroid/io/src/failure/load_image_failure.dart'; -import 'package:paintroid/io/src/failure/save_image_failure.dart'; abstract class IPhotoLibraryService { Future> save(String filename, Uint8List data); diff --git a/lib/io/src/ui/about_dialog.dart b/packages/io_library/lib/src/ui/about_dialog.dart similarity index 94% rename from lib/io/src/ui/about_dialog.dart rename to packages/io_library/lib/src/ui/about_dialog.dart index 1bd92c5a..f672d6af 100644 --- a/lib/io/src/ui/about_dialog.dart +++ b/packages/io_library/lib/src/ui/about_dialog.dart @@ -1,8 +1,8 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:paintroid/io/src/ui/generic_dialog.dart'; -import 'package:paintroid/ui/util.dart'; +import 'package:io_library/io_library.dart'; Future showMyAboutDialog(BuildContext context, String version) => showGeneralDialog( @@ -58,7 +58,7 @@ class _MyAboutDialogState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Image.asset('assets/icon/pocketpaint_logo_small.png'), + const PocketPaintLogoSmall(), Text( 'Version ${widget.version}', style: const TextStyle(fontSize: 9), diff --git a/lib/io/src/ui/delete_project_dialog.dart b/packages/io_library/lib/src/ui/delete_project_dialog.dart similarity index 93% rename from lib/io/src/ui/delete_project_dialog.dart rename to packages/io_library/lib/src/ui/delete_project_dialog.dart index ccb895a2..667f45e4 100644 --- a/lib/io/src/ui/delete_project_dialog.dart +++ b/packages/io_library/lib/src/ui/delete_project_dialog.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:paintroid/io/src/ui/generic_dialog.dart'; +import 'package:io_library/io_library.dart'; /// Returns [true] if user chose to delete the project or [null] if user /// dismiss the dialog by tapping outside diff --git a/lib/io/src/ui/discard_changes_dialog.dart b/packages/io_library/lib/src/ui/discard_changes_dialog.dart similarity index 94% rename from lib/io/src/ui/discard_changes_dialog.dart rename to packages/io_library/lib/src/ui/discard_changes_dialog.dart index c21875fe..2f2d1e65 100644 --- a/lib/io/src/ui/discard_changes_dialog.dart +++ b/packages/io_library/lib/src/ui/discard_changes_dialog.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:paintroid/io/src/ui/generic_dialog.dart'; +import 'package:io_library/io_library.dart'; /// Returns [true] if user chose to discard changes or [null] if user /// dismissed the dialog by tapping outside diff --git a/lib/io/src/ui/generic_dialog.dart b/packages/io_library/lib/src/ui/generic_dialog.dart similarity index 100% rename from lib/io/src/ui/generic_dialog.dart rename to packages/io_library/lib/src/ui/generic_dialog.dart diff --git a/lib/io/src/ui/image_format_info.dart b/packages/io_library/lib/src/ui/image_format_info.dart similarity index 93% rename from lib/io/src/ui/image_format_info.dart rename to packages/io_library/lib/src/ui/image_format_info.dart index 854ec79b..78fbcd8e 100644 --- a/lib/io/src/ui/image_format_info.dart +++ b/packages/io_library/lib/src/ui/image_format_info.dart @@ -1,4 +1,5 @@ -part of 'save_image_dialog.dart'; +import 'package:flutter/material.dart'; +import 'package:io_library/io_library.dart'; extension on ImageFormat { TextSpan get info { diff --git a/lib/io/src/ui/load_image_dialog.dart b/packages/io_library/lib/src/ui/load_image_dialog.dart similarity index 88% rename from lib/io/src/ui/load_image_dialog.dart rename to packages/io_library/lib/src/ui/load_image_dialog.dart index 9b1140ac..50840894 100644 --- a/lib/io/src/ui/load_image_dialog.dart +++ b/packages/io_library/lib/src/ui/load_image_dialog.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:paintroid/io/src/entity/image_location.dart'; -import 'package:paintroid/io/src/ui/generic_dialog.dart'; +import 'package:io_library/io_library.dart'; /// Returns [null] if user dismissed the dialog by tapping outside Future showLoadImageDialog(BuildContext context) => diff --git a/lib/io/src/ui/overwrite_dialog.dart b/packages/io_library/lib/src/ui/overwrite_dialog.dart similarity index 92% rename from lib/io/src/ui/overwrite_dialog.dart rename to packages/io_library/lib/src/ui/overwrite_dialog.dart index 28d98bc1..e625c53b 100644 --- a/lib/io/src/ui/overwrite_dialog.dart +++ b/packages/io_library/lib/src/ui/overwrite_dialog.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:paintroid/io/src/ui/generic_dialog.dart'; +import 'package:io_library/io_library.dart'; Future showOverwriteDialog(BuildContext context) => showGeneralDialog( diff --git a/lib/io/src/ui/project_details_dialog.dart b/packages/io_library/lib/src/ui/project_details_dialog.dart similarity index 93% rename from lib/io/src/ui/project_details_dialog.dart rename to packages/io_library/lib/src/ui/project_details_dialog.dart index 97d5d163..33687d8f 100644 --- a/lib/io/src/ui/project_details_dialog.dart +++ b/packages/io_library/lib/src/ui/project_details_dialog.dart @@ -1,13 +1,11 @@ +import 'package:component_library/component_library.dart'; +import 'package:database/database.dart'; import 'package:filesize/filesize.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/toast_utils.dart'; -import 'package:paintroid/data/model/project.dart'; -import 'package:paintroid/io/io.dart'; -import 'package:paintroid/io/src/ui/generic_dialog.dart'; -import 'package:paintroid/ui/color_schemes.dart'; Future showDetailsDialog(BuildContext context, Project project) => showGeneralDialog( diff --git a/lib/io/src/ui/save_image_dialog.dart b/packages/io_library/lib/src/ui/save_image_dialog.dart similarity index 97% rename from lib/io/src/ui/save_image_dialog.dart rename to packages/io_library/lib/src/ui/save_image_dialog.dart index 3e036730..104f4bb5 100644 --- a/lib/io/src/ui/save_image_dialog.dart +++ b/packages/io_library/lib/src/ui/save_image_dialog.dart @@ -1,9 +1,6 @@ +import 'package:component_library/component_library.dart'; import 'package:flutter/material.dart'; -import 'package:paintroid/io/io.dart'; -import 'package:paintroid/ui/color_schemes.dart'; -import 'package:paintroid/ui/styles.dart'; - -part 'image_format_info.dart'; +import 'package:io_library/io_library.dart'; /// Returns [null] if user dismissed the dialog by tapping outside Future showSaveImageDialog( diff --git a/lib/io/src/usecase/load_image_from_file_manager.dart b/packages/io_library/lib/src/usecase/load_image_from_file_manager.dart similarity index 66% rename from lib/io/src/usecase/load_image_from_file_manager.dart rename to packages/io_library/lib/src/usecase/load_image_from_file_manager.dart index 328489c2..aaa4d2fd 100644 --- a/lib/io/src/usecase/load_image_from_file_manager.dart +++ b/packages/io_library/lib/src/usecase/load_image_from_file_manager.dart @@ -1,10 +1,11 @@ +import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; -import 'package:paintroid/io/io.dart'; extension on File { String? get extension { @@ -18,19 +19,16 @@ class LoadImageFromFileManager with LoggableMixin { final IFileService fileService; final IImageService imageService; final IPermissionService permissionService; - final CatrobatImageSerializer catrobatImageSerializer; - LoadImageFromFileManager(this.fileService, this.imageService, - this.permissionService, this.catrobatImageSerializer); + LoadImageFromFileManager( + this.fileService, this.imageService, this.permissionService); static final provider = Provider((ref) { final imageService = ref.watch(IImageService.provider); final fileService = ref.watch(IFileService.provider); final permissionService = ref.watch(IPermissionService.provider); - const ver = CatrobatImage.latestVersion; - final serializer = ref.watch(CatrobatImageSerializer.provider(ver)); return LoadImageFromFileManager( - fileService, imageService, permissionService, serializer); + fileService, imageService, permissionService); }); Future> call( @@ -52,11 +50,13 @@ class LoadImageFromFileManager with LoggableMixin { .import(await file.readAsBytes()) .map((img) => ImageFromFile.rasterImage(img)); case 'catrobat-image': - final image = await catrobatImageSerializer - .fromBytes(await file.readAsBytes()); + Uint8List bytes = await file.readAsBytes(); + CatrobatImage catrobatImage = CatrobatImage.fromBytes(bytes); + Image? backgroundImage = + await rebuildBackgroundImage(catrobatImage); return Result.ok(ImageFromFile.catrobatImage( - image, - backgroundImage: image.backgroundImage, + catrobatImage, + backgroundImage: backgroundImage, )); default: return const Result.err(LoadImageFailure.invalidImage); @@ -70,4 +70,14 @@ class LoadImageFromFileManager with LoggableMixin { } }); } + + Future rebuildBackgroundImage(CatrobatImage catrobatImage) async { + if (catrobatImage.backgroundImage.isNotEmpty) { + final backgroundImageData = base64Decode(catrobatImage.backgroundImage); + final result = + await imageService.import(Uint8List.fromList(backgroundImageData)); + return result.unwrapOrElse((failure) => throw failure.message); + } + return null; + } } diff --git a/lib/io/src/usecase/load_image_from_photo_library.dart b/packages/io_library/lib/src/usecase/load_image_from_photo_library.dart similarity index 77% rename from lib/io/src/usecase/load_image_from_photo_library.dart rename to packages/io_library/lib/src/usecase/load_image_from_photo_library.dart index 46faf8b0..92366e3f 100644 --- a/lib/io/src/usecase/load_image_from_photo_library.dart +++ b/packages/io_library/lib/src/usecase/load_image_from_photo_library.dart @@ -1,12 +1,8 @@ import 'dart:ui'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/io/src/failure/load_image_failure.dart'; -import 'package:paintroid/io/src/service/image_service.dart'; -import 'package:paintroid/io/src/service/permission_service.dart'; -import 'package:paintroid/io/src/service/photo_library_service.dart'; class LoadImageFromPhotoLibrary { final IImageService imageService; diff --git a/lib/io/src/usecase/save_as_catrobat_image.dart b/packages/io_library/lib/src/usecase/save_as_catrobat_image.dart similarity index 59% rename from lib/io/src/usecase/save_as_catrobat_image.dart rename to packages/io_library/lib/src/usecase/save_as_catrobat_image.dart index 376ad31a..c97deb6a 100644 --- a/lib/io/src/usecase/save_as_catrobat_image.dart +++ b/packages/io_library/lib/src/usecase/save_as_catrobat_image.dart @@ -1,32 +1,19 @@ import 'dart:io'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/core/loggable_mixin.dart'; -import 'package:paintroid/io/io.dart' - show - CatrobatImage, - CatrobatImageMetaData, - CatrobatImageSerializer, - IFileService, - SaveImageFailure; -import 'package:paintroid/io/src/service/permission_service.dart'; class SaveAsCatrobatImage with LoggableMixin { final IFileService _fileService; final IPermissionService permissionService; - final CatrobatImageSerializer _catrobatImageSerializer; - SaveAsCatrobatImage( - this._fileService, this.permissionService, this._catrobatImageSerializer); + SaveAsCatrobatImage(this._fileService, this.permissionService); static final provider = Provider((ref) { final fileService = ref.watch(IFileService.provider); final permissionService = ref.watch(IPermissionService.provider); - const ver = CatrobatImage.latestVersion; - final serializer = ref.watch(CatrobatImageSerializer.provider(ver)); - return SaveAsCatrobatImage(fileService, permissionService, serializer); + return SaveAsCatrobatImage(fileService, permissionService); }); Future> call( @@ -36,7 +23,7 @@ class SaveAsCatrobatImage with LoggableMixin { } final nameWithExt = '${data.name}.${data.format.extension}'; try { - final bytes = await _catrobatImageSerializer.toBytes(image); + final bytes = image.toBytes(); if (isAProject) { return _fileService.saveToApplicationDirectory(nameWithExt, bytes); } diff --git a/lib/io/src/usecase/save_as_raster_image.dart b/packages/io_library/lib/src/usecase/save_as_raster_image.dart similarity index 93% rename from lib/io/src/usecase/save_as_raster_image.dart rename to packages/io_library/lib/src/usecase/save_as_raster_image.dart index 49227f84..2051142e 100644 --- a/lib/io/src/usecase/save_as_raster_image.dart +++ b/packages/io_library/lib/src/usecase/save_as_raster_image.dart @@ -1,9 +1,8 @@ import 'dart:ui'; import 'package:flutter_riverpod/flutter_riverpod.dart' show Provider; +import 'package:io_library/io_library.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/io/io.dart'; class SaveAsRasterImage { final IImageService imageService; diff --git a/packages/io_library/pubspec.yaml b/packages/io_library/pubspec.yaml new file mode 100644 index 00000000..33e1fe2b --- /dev/null +++ b/packages/io_library/pubspec.yaml @@ -0,0 +1,59 @@ +name: io_library +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + freezed_annotation: ^2.4.1 + logging: ^1.0.2 + protobuf: ^2.1.0 + path_provider: ^2.0.11 + file_picker: ^5.3.1 + image: ^3.2.0 + package_info_plus: ^4.0.1 + device_info_plus: ^9.0.3 + permission_handler: ^10.0.0 + image_picker: ^0.8.5+3 + filesize: ^2.0.1 + oxidized: ^5.2.0 + intl: ^0.18.0 + json_annotation: ^4.8.1 + + # Internal packages: + component_library: + path: ../component_library + command: + path: ../command + database: + path: ../database + workspace_screen: + path: ../features/workspace_screen + + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + json_serializable: ^6.7.1 + +flutter: + uses-material-design: true + assets: + - test/fixture/image/ diff --git a/packages/io_library/test/fixture/image/test.jpg b/packages/io_library/test/fixture/image/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04ff2033529566cf1bd1277a6d96ef80e038c9c9 GIT binary patch literal 1731 zcmbW0c~DbF9LM+NB_tpe0s%#x4Yk&-|lbs^L7^*K*oT~5i<)j z06_p8Mhig3fdh@{?h1gVCC~%_5C8^}1u*I$w15bAfE9TF)X^RQI34;LO~h9VN^i3@+vBGSqL|#o~WqLP94Mu|!?DSRflv%zh8)*PU z0?+|EFhm9zA_Nm5gaee&9pNC6MAOg)!oXOZ7@i<5u?`(jE`x3zhA~(ehZ8OX1)_0) zCE{e2wN1q2Y|h}x-XxuCu^9vv)8a;X+b*7}u8U8wxP-#`4T>Ap)VFDD*VNmycb~q2 z;lV>RGxNh1N9^p6pE&8@_~XxK&$+tM-Ope0^}EbqUhxkJ4GWLBej_q2J|QtFIVCmi zUgrI*?3~=Z-%Co%$}1`#RaG}V;WR&OX>EJf-P7CmvVUN3Xq-Fo=j+L-H`6l( z_;HE<>9ddv{hwd3(Dn=2f4GP!7Y2)kv3MaDgkcE5iCCPnwwSDm4gQR`99icYfn*w+ zQQRo5qHD{Ock$_xP*BwyS6dLGiO9YUEckzst$}^z8U_aeMkE-6o)QeBCxu0Y!{da) z6YwGt{*i=8>x7b86M`mz&>ZMwG4zuX$BVDUzS$rn=(WTmLqHOS(8~l90Tl>V?nP<< zQGL8)V&6M$p6(bfwu+z66&RUS(U0-mv#pG5t88M7Z|hh7FrMNmDff$iQDGuWDV^mj zC^GiQSh42c@yc7-&8Llzn2w(0jfQY;c^14~+FPJnQTtMn;1S^W(`JfE+kA|I84xI; zOTkE|V6J!3a^Inzg7RCgb112X6L)fEk`_)-j#9Xy8ry15v$k+K0LMGuwp|J2w=AD! zM%eSbW)t&hV;vEOl*ZD2wz^Y;dQEk*B3uk>+p=3j1lJ6p|u)bf0Hc1AI4P~}=o3E2?i5Fba1POX!7H>5Vy57)FH zK!UQ<^TVj*iUThxnoE@)GLda|lj?}bH1z9B(9V?*k63C34YUmn+^hQa8m2pikvIR5*=;QD@|#>AYiEDSbmiw#?O-?{cj)1~=5WLH)~J?k9!XG} z*M7bcZxff+|0~NI0oeJQwl|wbC5^Wio?_;3{L3E(Qy84O&3%U`IRg(gbBCVQ?3!`{ z7#(08$4+G_3)1T#eowR3vJp$6d%nCD-<0Ovligd^z9*ZP73X-*tU~Nlbh2s~L4suX z2tTz=58_fK5#VYySTiBrc$%@S(rl>3xx0m#{3Pa0S_db|`5TmT$GN-cC@++B8uQv9Mmj3aeXc`|O&BMhbGy`ql=Sr;%e!DHkft^-2#b z91Epvl6k?&-SL?^CLL1u9p95%_&u}!33*zJz4d&thhd4O29wr%@u6ZE=2m1QWA9N} z&ig0;%_P{+KoHU;SSbvzzaWk0r&%enJ4WSKr0&0(@$U#-RT-G>4{yTX8SC7`e>H3u z=S*?9ubH=H=>j<6!toGbQQ757Aro==cS_zJhaYGg kxW&G39J#@8alE0LHk+G{iPR8PX{7sKuk_1ng^|I(0T;!^UjP6A literal 0 HcmV?d00001 diff --git a/packages/io_library/test/fixture/image/test.png b/packages/io_library/test/fixture/image/test.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e03a09329b7d4e7a056b28eacf63b33208aa44 GIT binary patch literal 1272 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jB19BD*PQ~&?~0ssI20000082|tP zC;$Ke82|tP82|tRcubKn-~a#uL`g(JRA>e4mS1R(Q5eTJTUbQ2WGKH(;)1IwmYNH0 zkPD(@ZY39FrM6O1a^c1WrCmtd4M|btf+XZZDJ5=}6c@9JY(g9J=kuv^>U7$cHcM~b zZ+$xFInR5}`#jHi&-=brRi(-bloco|P*$Lq z6halEyb3=cCh(qqEBsT{ad!{sOPH5MvJSfg+F&s>z&kL3A-D!-;Tf!iDV4#U1V2o9 zptBi!00!0%^`Lw_u0yQ2tySLuej9FrkCt+6{aqSUp=uW04agGQ03_K3@F*CSz}8vX6-i_rWg6b%Fu?Ry^G$uR8$xx-}B5pB;ZI+uafeSaXTqA!%nM)u;$qVvmv5a9 z;5A2bXRtl+?o?dMD&GN%RDrS7jKeu^p=PX$#vAwT@d#Yx3-}gV52>uG!8`}Ay8@~F zQ#}u}!k}xK=zVbKVp6tG;4qA~Q$69@{QeA&uNK_B;0<0wd+1-HO|=Rm2J?pg+{C$^ z{`fX@a0QQA@U8vp?R|2m1qasU7U>PbXFRPB~cC`3^d z$1(XVkp(H)OtP?$Y*?_cP!?=Rb{0OCB4H~QN(o8HN?8$>%0?m^N;VX-p;(Zj5c&H3 zr~7hyo@S`YOYhWg&bjBDd+)ht-n%mi314Lp*DJikI^;ry8SkTQNo4Q@%I&iPwU&Rx z@_%Qh5@>`?&{|iJ1CDfI$UosYqUjV8p5XQs-zLlxYa@hti`GR!2cH~5>$vCQcw zNgK?2B&%DB-5af(B#1?NNIHcj7=v5TS`82v3A3s;&_-#l9a>FH@kI!^;u`9kUWQ9( z0Y|1WG|y&%u^zLy$qp-P!zR}bdRg^?DyECU7QJ_Nv2Tn!tOONY1Z^+?H4t~{W(O6! z;<3}HiOpECZ0s%Le?;p7DS$thd@9h@tfJhvR|!5H@&=MY*XJI{cMiG~d?n89)$CE# zAwCse2hJgmu?xX>(o01ucFv=+DHFRe`UtIW(U($2RXT_|?Ur^Niird8?IEZw^87!c zb^BzNmw84x>bftV{hzdIgJk{p%KOPAKj!N9A?(M(f|`O31RV%E i5Og5uK+u8z(18ymm;N57p2}zd0000Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jB19BD*PQ~&?~0ssI20000082|tP zC;$Ke82|tP82|tRcubKn-~a#uL`g(JRA>e4mS1R(Q5eTJTUbQ2WGKH(;)1IwmYNH0 zkPD(@ZY39FrM6O1a^c1WrCmtd4M|btf+XZZDJ5=}6c@9JY(g9J=kuv^>U7$cHcM~b zZ+$xFInR5}`#jHi&-=brRi(-bloco|P*$Lq z6halEyb3=cCh(qqEBsT{ad!{sOPH5MvJSfg+F&s>z&kL3A-D!-;Tf!iDV4#U1V2o9 zptBi!00!0%^`Lw_u0yQ2tySLuej9FrkCt+6{aqSUp=uW04agGQ03_K3@F*CSz}8vX6-i_rWg6b%Fu?Ry^G$uR8$xx-}B5pB;ZI+uafeSaXTqA!%nM)u;$qVvmv5a9 z;5A2bXRtl+?o?dMD&GN%RDrS7jKeu^p=PX$#vAwT@d#Yx3-}gV52>uG!8`}Ay8@~F zQ#}u}!k}xK=zVbKVp6tG;4qA~Q$69@{QeA&uNK_B;0<0wd+1-HO|=Rm2J?pg+{C$^ z{`fX@a0QQA@U8vp?R|2m1qasU7U>PbXFRPB~cC`3^d z$1(XVkp(H)OtP?$Y*?_cP!?=Rb{0OCB4H~QN(o8HN?8$>%0?m^N;VX-p;(Zj5c&H3 zr~7hyo@S`YOYhWg&bjBDd+)ht-n%mi314Lp*DJikI^;ry8SkTQNo4Q@%I&iPwU&Rx z@_%Qh5@>`?&{|iJ1CDfI$UosYqUjV8p5XQs-zLlxYa@hti`GR!2cH~5>$vCQcw zNgK?2B&%DB-5af(B#1?NNIHcj7=v5TS`82v3A3s;&_-#l9a>FH@kI!^;u`9kUWQ9( z0Y|1WG|y&%u^zLy$qp-P!zR}bdRg^?DyECU7QJ_Nv2Tn!tOONY1Z^+?H4t~{W(O6! z;<3}HiOpECZ0s%Le?;p7DS$thd@9h@tfJhvR|!5H@&=MY*XJI{cMiG~d?n89)$CE# zAwCse2hJgmu?xX>(o01ucFv=+DHFRe`UtIW(U($2RXT_|?Ur^Niird8?IEZw^87!c zb^BzNmw84x>bftV{hzdIgJk{p%KOPAKj!N9A?(M(f|`O31RV%E i5Og5uK+u8z(18ymm;N57p2}zd0000()); + deserializedMoveToAction as MoveToAction; + expect(moveToAction, equals(deserializedMoveToAction)); + }); + + test('Test converter for LineToAction', () { + double xExpected = 1.0; + double yExpected = 2.0; + + LineToAction lineToAction = LineToAction(xExpected, yExpected); + + var json = converter.toJson(lineToAction); + + PathAction deserializedLineToAction = converter.fromJson(json); + + expect(deserializedLineToAction, isA()); + deserializedLineToAction as LineToAction; + expect(lineToAction, equals(deserializedLineToAction)); + }); + + test('Test converter for CloseAction', () { + CloseAction closeAction = const CloseAction(); + + var json = converter.toJson(closeAction); + + PathAction deserializedCloseAction = converter.fromJson(json); + + expect(deserializedCloseAction, isA()); + }); +} diff --git a/packages/io_library/test/unit/serialization/converter/path_with_action_history_converter_test.dart b/packages/io_library/test/unit/serialization/converter/path_with_action_history_converter_test.dart new file mode 100644 index 00000000..1167e2be --- /dev/null +++ b/packages/io_library/test/unit/serialization/converter/path_with_action_history_converter_test.dart @@ -0,0 +1,43 @@ +import 'package:command/command.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:io_library/io_library.dart'; + +import '../utils/dummy_path_factory.dart'; + +void main() { + const PathWithActionHistoryConverter converter = + PathWithActionHistoryConverter(); + + test('Test converter for PathWithActionHistory with one path', () { + PathWithActionHistory path = + DummyPathFactory.createPathWithActionHistory(1); + + var json = converter.toJson(path); + + PathWithActionHistory deserializedPath = converter.fromJson(json); + + expect(path, equals(deserializedPath)); + }); + + test('Test converter for PathWithActionHistory with two paths', () { + PathWithActionHistory path = + DummyPathFactory.createPathWithActionHistory(2); + + var json = converter.toJson(path); + + PathWithActionHistory deserializedPath = converter.fromJson(json); + + expect(path, equals(deserializedPath)); + }); + + test('Test converter for PathWithActionHistory with multiple paths', () { + PathWithActionHistory path = + DummyPathFactory.createPathWithActionHistory(10); + + var json = converter.toJson(path); + + PathWithActionHistory deserializedPath = converter.fromJson(json); + + expect(path, equals(deserializedPath)); + }); +} diff --git a/packages/io_library/test/unit/serialization/image/catrobat_image_serializer_test.dart b/packages/io_library/test/unit/serialization/image/catrobat_image_serializer_test.dart new file mode 100644 index 00000000..10b0cbbd --- /dev/null +++ b/packages/io_library/test/unit/serialization/image/catrobat_image_serializer_test.dart @@ -0,0 +1,55 @@ +import 'dart:typed_data'; + +import 'package:command/command.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:io_library/io_library.dart'; + +import '../utils/dummy_command_factory.dart'; +import '../utils/dummy_version_strategy.dart'; + +void main() { + group('Version 1', () { + test('Test CatrobatImage serialization with 1 path', () { + Iterable commands = + DummyCommandFactory.createCommandList(1, version: Version.v1); + VersionStrategyManager.setStrategy( + DummyVersionStrategy(catrobatImageVersion: Version.v1)); + CatrobatImage originalImage = CatrobatImage(commands, 100, 200, ''); + + Uint8List bytes = originalImage.toBytes(); + CatrobatImage deserializedImage = CatrobatImage.fromBytes(bytes); + + expect( + DummyCommandFactory.compareCommandLists( + commands, deserializedImage.commands), + isTrue); + expect(originalImage.width, equals(deserializedImage.width)); + expect(originalImage.height, equals(deserializedImage.height)); + expect(originalImage.backgroundImage, + equals(deserializedImage.backgroundImage)); + expect(Version.v1, equals(deserializedImage.version)); + expect(originalImage.magicValue, equals(deserializedImage.magicValue)); + }); + + test('Test CatrobatImage serialization with multiple paths', () { + Iterable commands = DummyCommandFactory.createCommandList(5); + VersionStrategyManager.setStrategy( + DummyVersionStrategy(catrobatImageVersion: Version.v1)); + CatrobatImage originalImage = CatrobatImage(commands, 100, 200, ''); + + Uint8List bytes = originalImage.toBytes(); + CatrobatImage deserializedImage = CatrobatImage.fromBytes(bytes); + + expect( + DummyCommandFactory.compareCommandLists( + commands, deserializedImage.commands), + isTrue); + expect(originalImage.width, equals(deserializedImage.width)); + expect(originalImage.height, equals(deserializedImage.height)); + expect(originalImage.backgroundImage, + equals(deserializedImage.backgroundImage)); + expect(Version.v1, equals(deserializedImage.version)); + expect(originalImage.magicValue, equals(deserializedImage.magicValue)); + }); + }); +} diff --git a/packages/io_library/test/unit/serialization/utils/dummy_command_factory.dart b/packages/io_library/test/unit/serialization/utils/dummy_command_factory.dart new file mode 100644 index 00000000..2812ce77 --- /dev/null +++ b/packages/io_library/test/unit/serialization/utils/dummy_command_factory.dart @@ -0,0 +1,63 @@ +import 'dart:ui'; + +import 'package:command/command.dart'; +import 'package:io_library/io_library.dart'; + +import 'dummy_paint_factory.dart'; +import 'dummy_path_factory.dart'; +import 'dummy_version_strategy.dart'; + +class DummyCommandFactory { + static Iterable createCommandList(int numberOfCommands, + {int version = Version.v1}) { + CommandFactory commandFactory = const CommandFactory(); + VersionStrategyManager.setStrategy( + DummyVersionStrategy(drawPathCommandVersion: version)); + List commands = []; + for (int i = 0; i < numberOfCommands; i++) { + PathWithActionHistory originalPath = + DummyPathFactory.createPathWithActionHistory(i * numberOfCommands); + Paint originalPaint = DummyPaintFactory.createPaint(); + DrawPathCommand command = + commandFactory.createDrawPathCommand(originalPath, originalPaint); + commands.add(command); + } + return commands; + } + + static DrawPathCommand createDrawPathCommand( + PathWithActionHistory path, Paint paint, + {int version = Version.v1}) { + CommandFactory commandFactory = const CommandFactory(); + VersionStrategyManager.setStrategy( + DummyVersionStrategy(drawPathCommandVersion: version)); + return commandFactory.createDrawPathCommand(path, paint); + } + + static bool compareCommandLists( + Iterable commands1, Iterable commands2) { + if (commands1.length != commands2.length) { + return false; + } + var iterator1 = commands1.iterator; + var iterator2 = commands2.iterator; + + while (iterator1.moveNext() && iterator2.moveNext()) { + if (!areCommandsEqual(iterator1.current, iterator2.current)) { + return false; + } + } + return true; + } + + static bool areCommandsEqual(Command command1, Command command2) { + if (command1 is DrawPathCommand && command2 is DrawPathCommand) { + return command1.path == command2.path && + DummyPaintFactory.comparePaint( + command1.paint, + command2.paint, + ); + } + return false; + } +} diff --git a/packages/io_library/test/unit/serialization/utils/dummy_paint_factory.dart b/packages/io_library/test/unit/serialization/utils/dummy_paint_factory.dart new file mode 100644 index 00000000..dc53ffa4 --- /dev/null +++ b/packages/io_library/test/unit/serialization/utils/dummy_paint_factory.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:io_library/io_library.dart'; + +class DummyPaintFactory { + static Paint createPaint({int version = Version.v1}) { + Paint paint = Paint(); + if (version >= Version.v1) { + paint.color = Colors.blue; + paint.strokeWidth = 5.0; + paint.strokeCap = StrokeCap.round; + paint.isAntiAlias = true; + paint.style = PaintingStyle.fill; + paint.strokeJoin = StrokeJoin.bevel; + paint.blendMode = BlendMode.clear; + } + if (version >= Version.v2) { + // paint.newAttribute = newAttribute; + } + return paint; + } + + static bool comparePaint(Paint paint1, Paint paint2, + {int version = Version.v1}) { + bool result = true; + if (version >= Version.v1) { + result = paint1.color == paint2.color && + paint1.strokeWidth == paint2.strokeWidth && + paint1.strokeCap == paint2.strokeCap && + paint1.isAntiAlias == paint2.isAntiAlias && + paint1.style == paint2.style && + paint1.strokeJoin == paint2.strokeJoin && + paint1.blendMode == paint2.blendMode; + } + if (version >= Version.v2) { + // result = result && paint1.newAttribute == paint2.newAttribute; + } + return result; + } +} diff --git a/packages/io_library/test/unit/serialization/utils/dummy_path_factory.dart b/packages/io_library/test/unit/serialization/utils/dummy_path_factory.dart new file mode 100644 index 00000000..2f19f5bb --- /dev/null +++ b/packages/io_library/test/unit/serialization/utils/dummy_path_factory.dart @@ -0,0 +1,14 @@ +import 'package:command/command.dart'; + +class DummyPathFactory { + static PathWithActionHistory createPathWithActionHistory( + int numberOfActions) { + PathWithActionHistory pathWithActionHistory = PathWithActionHistory(); + for (int i = 0; i < numberOfActions; i++) { + pathWithActionHistory.moveTo(i.toDouble(), i.toDouble() + 1); + pathWithActionHistory.lineTo(i.toDouble() + 2, i.toDouble() + 3); + } + pathWithActionHistory.close(); + return pathWithActionHistory; + } +} diff --git a/packages/io_library/test/unit/serialization/utils/dummy_version_strategy.dart b/packages/io_library/test/unit/serialization/utils/dummy_version_strategy.dart new file mode 100644 index 00000000..b0ca626f --- /dev/null +++ b/packages/io_library/test/unit/serialization/utils/dummy_version_strategy.dart @@ -0,0 +1,17 @@ +import 'package:io_library/io_library.dart'; + +class DummyVersionStrategy implements IVersionStrategy { + final int drawPathCommandVersion; + final int catrobatImageVersion; + + DummyVersionStrategy( + {this.drawPathCommandVersion = + SerializerVersion.DRAW_PATH_COMMAND_VERSION, + this.catrobatImageVersion = SerializerVersion.CATROBAT_IMAGE_VERSION}); + + @override + int getCatrobatImageVersion() => catrobatImageVersion; + + @override + int getDrawPathCommandVersion() => drawPathCommandVersion; +} diff --git a/test/unit/io/service/file_service_test.dart b/packages/io_library/test/unit/service/file_service_test.dart similarity index 97% rename from test/unit/io/service/file_service_test.dart rename to packages/io_library/test/unit/service/file_service_test.dart index f62e0633..8305160f 100644 --- a/test/unit/io/service/file_service_test.dart +++ b/packages/io_library/test/unit/service/file_service_test.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/io/src/service/file_service.dart'; +import 'package:io_library/io_library.dart'; void main() async { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/test/unit/io/service/image_service_test.dart b/packages/io_library/test/unit/service/image_service_test.dart similarity index 97% rename from test/unit/io/service/image_service_test.dart rename to packages/io_library/test/unit/service/image_service_test.dart index 648b86f6..dc034350 100644 --- a/test/unit/io/service/image_service_test.dart +++ b/packages/io_library/test/unit/service/image_service_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/io/io.dart'; +import 'package:io_library/io_library.dart'; void main() async { TestWidgetsFlutterBinding.ensureInitialized(); diff --git a/test/unit/io/service/photo_library_service_test.dart b/packages/io_library/test/unit/service/photo_library_service_test.dart similarity index 93% rename from test/unit/io/service/photo_library_service_test.dart rename to packages/io_library/test/unit/service/photo_library_service_test.dart index 29d06912..7f0fecae 100644 --- a/test/unit/io/service/photo_library_service_test.dart +++ b/packages/io_library/test/unit/service/photo_library_service_test.dart @@ -2,11 +2,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:io_library/io_library.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/io/io.dart'; import 'photo_library_service_test.mocks.dart'; @@ -108,7 +107,8 @@ void main() { when(mockImagePicker.pickImage(source: anyNamed('source'))) .thenAnswer((_) async => null); final result = await sut.pick(); - expect(result, const Err(LoadImageFailure.userCancelled)); + expect(result, + const Err(LoadImageFailure.userCancelled)); verify(mockImagePicker.pickImage(source: anyNamed('source'))); verifyNoMoreInteractions(mockImagePicker); verifyZeroInteractions(mockMethodChannel); @@ -121,7 +121,8 @@ void main() { when(mockImagePicker.pickImage(source: anyNamed('source'))) .thenThrow(testException); final result = await sut.pick(); - expect(result, const Err(LoadImageFailure.unidentified)); + expect(result, + const Err(LoadImageFailure.unidentified)); verify(mockImagePicker.pickImage(source: anyNamed('source'))); verifyNoMoreInteractions(mockImagePicker); verifyZeroInteractions(mockMethodChannel); @@ -133,7 +134,8 @@ void main() { .thenAnswer((_) async => mockImageXFile); when(mockImageXFile.readAsBytes()).thenThrow(testException); final result = await sut.pick(); - expect(result, const Err(LoadImageFailure.unidentified)); + expect(result, + const Err(LoadImageFailure.unidentified)); verify(mockImagePicker.pickImage(source: anyNamed('source'))); verify(mockImageXFile.readAsBytes()); verifyNoMoreInteractions(mockImageXFile); @@ -148,7 +150,8 @@ void main() { when(mockImagePicker.pickImage(source: anyNamed('source'))) .thenThrow(testPlatformException); final result = await sut.pick(); - expect(result, const Err(LoadImageFailure.unidentified)); + expect(result, + const Err(LoadImageFailure.unidentified)); verify(mockImagePicker.pickImage(source: anyNamed('source'))); verifyNoMoreInteractions(mockImagePicker); verifyZeroInteractions(mockMethodChannel); diff --git a/packages/io_library/test/unit/service/photo_library_service_test.mocks.dart b/packages/io_library/test/unit/service/photo_library_service_test.mocks.dart new file mode 100644 index 00000000..5cc7a2d9 --- /dev/null +++ b/packages/io_library/test/unit/service/photo_library_service_test.mocks.dart @@ -0,0 +1,480 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in io_library/test/unit/service/photo_library_service_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i6; +import 'dart:convert' as _i8; +import 'dart:typed_data' as _i9; + +import 'package:flutter/src/services/binary_messenger.dart' as _i4; +import 'package:flutter/src/services/message_codec.dart' as _i3; +import 'package:flutter/src/services/platform_channel.dart' as _i7; +import 'package:image_picker/image_picker.dart' as _i5; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart' + as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeLostData_0 extends _i1.SmartFake implements _i2.LostData { + _FakeLostData_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeLostDataResponse_1 extends _i1.SmartFake + implements _i2.LostDataResponse { + _FakeLostDataResponse_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeMethodCodec_2 extends _i1.SmartFake implements _i3.MethodCodec { + _FakeMethodCodec_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeBinaryMessenger_3 extends _i1.SmartFake + implements _i4.BinaryMessenger { + _FakeBinaryMessenger_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDateTime_4 extends _i1.SmartFake implements DateTime { + _FakeDateTime_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [ImagePicker]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockImagePicker extends _i1.Mock implements _i5.ImagePicker { + MockImagePicker() { + _i1.throwOnMissingStub(this); + } + + @override + _i6.Future<_i2.PickedFile?> getImage({ + required _i2.ImageSource? source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + }) => + (super.noSuchMethod( + Invocation.method( + #getImage, + [], + { + #source: source, + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #preferredCameraDevice: preferredCameraDevice, + }, + ), + returnValue: _i6.Future<_i2.PickedFile?>.value(), + ) as _i6.Future<_i2.PickedFile?>); + + @override + _i6.Future?> getMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + }) => + (super.noSuchMethod( + Invocation.method( + #getMultiImage, + [], + { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + }, + ), + returnValue: _i6.Future?>.value(), + ) as _i6.Future?>); + + @override + _i6.Future<_i2.PickedFile?> getVideo({ + required _i2.ImageSource? source, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + Duration? maxDuration, + }) => + (super.noSuchMethod( + Invocation.method( + #getVideo, + [], + { + #source: source, + #preferredCameraDevice: preferredCameraDevice, + #maxDuration: maxDuration, + }, + ), + returnValue: _i6.Future<_i2.PickedFile?>.value(), + ) as _i6.Future<_i2.PickedFile?>); + + @override + _i6.Future<_i2.LostData> getLostData() => (super.noSuchMethod( + Invocation.method( + #getLostData, + [], + ), + returnValue: _i6.Future<_i2.LostData>.value(_FakeLostData_0( + this, + Invocation.method( + #getLostData, + [], + ), + )), + ) as _i6.Future<_i2.LostData>); + + @override + _i6.Future<_i2.XFile?> pickImage({ + required _i2.ImageSource? source, + double? maxWidth, + double? maxHeight, + int? imageQuality, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + bool? requestFullMetadata = true, + }) => + (super.noSuchMethod( + Invocation.method( + #pickImage, + [], + { + #source: source, + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #preferredCameraDevice: preferredCameraDevice, + #requestFullMetadata: requestFullMetadata, + }, + ), + returnValue: _i6.Future<_i2.XFile?>.value(), + ) as _i6.Future<_i2.XFile?>); + + @override + _i6.Future> pickMultiImage({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + bool? requestFullMetadata = true, + }) => + (super.noSuchMethod( + Invocation.method( + #pickMultiImage, + [], + { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #requestFullMetadata: requestFullMetadata, + }, + ), + returnValue: _i6.Future>.value(<_i2.XFile>[]), + ) as _i6.Future>); + + @override + _i6.Future<_i2.XFile?> pickMedia({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + bool? requestFullMetadata = true, + }) => + (super.noSuchMethod( + Invocation.method( + #pickMedia, + [], + { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #requestFullMetadata: requestFullMetadata, + }, + ), + returnValue: _i6.Future<_i2.XFile?>.value(), + ) as _i6.Future<_i2.XFile?>); + + @override + _i6.Future> pickMultipleMedia({ + double? maxWidth, + double? maxHeight, + int? imageQuality, + bool? requestFullMetadata = true, + }) => + (super.noSuchMethod( + Invocation.method( + #pickMultipleMedia, + [], + { + #maxWidth: maxWidth, + #maxHeight: maxHeight, + #imageQuality: imageQuality, + #requestFullMetadata: requestFullMetadata, + }, + ), + returnValue: _i6.Future>.value(<_i2.XFile>[]), + ) as _i6.Future>); + + @override + _i6.Future<_i2.XFile?> pickVideo({ + required _i2.ImageSource? source, + _i2.CameraDevice? preferredCameraDevice = _i2.CameraDevice.rear, + Duration? maxDuration, + }) => + (super.noSuchMethod( + Invocation.method( + #pickVideo, + [], + { + #source: source, + #preferredCameraDevice: preferredCameraDevice, + #maxDuration: maxDuration, + }, + ), + returnValue: _i6.Future<_i2.XFile?>.value(), + ) as _i6.Future<_i2.XFile?>); + + @override + _i6.Future<_i2.LostDataResponse> retrieveLostData() => (super.noSuchMethod( + Invocation.method( + #retrieveLostData, + [], + ), + returnValue: + _i6.Future<_i2.LostDataResponse>.value(_FakeLostDataResponse_1( + this, + Invocation.method( + #retrieveLostData, + [], + ), + )), + ) as _i6.Future<_i2.LostDataResponse>); + + @override + bool supportsImageSource(_i2.ImageSource? source) => (super.noSuchMethod( + Invocation.method( + #supportsImageSource, + [source], + ), + returnValue: false, + ) as bool); +} + +/// A class which mocks [MethodChannel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMethodChannel extends _i1.Mock implements _i7.MethodChannel { + MockMethodChannel() { + _i1.throwOnMissingStub(this); + } + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: '', + ) as String); + + @override + _i3.MethodCodec get codec => (super.noSuchMethod( + Invocation.getter(#codec), + returnValue: _FakeMethodCodec_2( + this, + Invocation.getter(#codec), + ), + ) as _i3.MethodCodec); + + @override + _i4.BinaryMessenger get binaryMessenger => (super.noSuchMethod( + Invocation.getter(#binaryMessenger), + returnValue: _FakeBinaryMessenger_3( + this, + Invocation.getter(#binaryMessenger), + ), + ) as _i4.BinaryMessenger); + + @override + _i6.Future invokeMethod( + String? method, [ + dynamic arguments, + ]) => + (super.noSuchMethod( + Invocation.method( + #invokeMethod, + [ + method, + arguments, + ], + ), + returnValue: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future?> invokeListMethod( + String? method, [ + dynamic arguments, + ]) => + (super.noSuchMethod( + Invocation.method( + #invokeListMethod, + [ + method, + arguments, + ], + ), + returnValue: _i6.Future?>.value(), + ) as _i6.Future?>); + + @override + _i6.Future?> invokeMapMethod( + String? method, [ + dynamic arguments, + ]) => + (super.noSuchMethod( + Invocation.method( + #invokeMapMethod, + [ + method, + arguments, + ], + ), + returnValue: _i6.Future?>.value(), + ) as _i6.Future?>); + + @override + void setMethodCallHandler( + _i6.Future Function(_i3.MethodCall)? handler) => + super.noSuchMethod( + Invocation.method( + #setMethodCallHandler, + [handler], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [XFile]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockXFile extends _i1.Mock implements _i2.XFile { + MockXFile() { + _i1.throwOnMissingStub(this); + } + + @override + String get path => (super.noSuchMethod( + Invocation.getter(#path), + returnValue: '', + ) as String); + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: '', + ) as String); + + @override + _i6.Future saveTo(String? path) => (super.noSuchMethod( + Invocation.method( + #saveTo, + [path], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + + @override + _i6.Future length() => (super.noSuchMethod( + Invocation.method( + #length, + [], + ), + returnValue: _i6.Future.value(0), + ) as _i6.Future); + + @override + _i6.Future readAsString( + {_i8.Encoding? encoding = const _i8.Utf8Codec()}) => + (super.noSuchMethod( + Invocation.method( + #readAsString, + [], + {#encoding: encoding}, + ), + returnValue: _i6.Future.value(''), + ) as _i6.Future); + + @override + _i6.Future<_i9.Uint8List> readAsBytes() => (super.noSuchMethod( + Invocation.method( + #readAsBytes, + [], + ), + returnValue: _i6.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + ) as _i6.Future<_i9.Uint8List>); + + @override + _i6.Stream<_i9.Uint8List> openRead([ + int? start, + int? end, + ]) => + (super.noSuchMethod( + Invocation.method( + #openRead, + [ + start, + end, + ], + ), + returnValue: _i6.Stream<_i9.Uint8List>.empty(), + ) as _i6.Stream<_i9.Uint8List>); + + @override + _i6.Future lastModified() => (super.noSuchMethod( + Invocation.method( + #lastModified, + [], + ), + returnValue: _i6.Future.value(_FakeDateTime_4( + this, + Invocation.method( + #lastModified, + [], + ), + )), + ) as _i6.Future); +} diff --git a/test/unit/io/usecase/load_image_from_photo_library_test.dart b/packages/io_library/test/unit/usecase/load_image_from_photo_library_test.dart similarity index 97% rename from test/unit/io/usecase/load_image_from_photo_library_test.dart rename to packages/io_library/test/unit/usecase/load_image_from_photo_library_test.dart index 5a54dead..dea81939 100644 --- a/test/unit/io/usecase/load_image_from_photo_library_test.dart +++ b/packages/io_library/test/unit/usecase/load_image_from_photo_library_test.dart @@ -3,11 +3,10 @@ import 'dart:ui'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:io_library/io_library.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/io/io.dart'; import 'load_image_from_photo_library_test.mocks.dart'; diff --git a/packages/io_library/test/unit/usecase/load_image_from_photo_library_test.mocks.dart b/packages/io_library/test/unit/usecase/load_image_from_photo_library_test.mocks.dart new file mode 100644 index 00000000..2793e86e --- /dev/null +++ b/packages/io_library/test/unit/usecase/load_image_from_photo_library_test.mocks.dart @@ -0,0 +1,211 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in io_library/test/unit/usecase/load_image_from_photo_library_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; +import 'dart:typed_data' as _i6; +import 'dart:ui' as _i5; + +import 'package:io_library/io_library.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; +import 'package:oxidized/oxidized.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeResult_0 extends _i1.SmartFake + implements _i2.Result { + _FakeResult_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [IImageService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIImageService extends _i1.Mock implements _i3.IImageService { + MockIImageService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.Result<_i5.Image, _i3.Failure>> import( + _i6.Uint8List? fileData) => + (super.noSuchMethod( + Invocation.method( + #import, + [fileData], + ), + returnValue: _i4.Future<_i2.Result<_i5.Image, _i3.Failure>>.value( + _FakeResult_0<_i5.Image, _i3.Failure>( + this, + Invocation.method( + #import, + [fileData], + ), + )), + ) as _i4.Future<_i2.Result<_i5.Image, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>> exportAsJpg( + _i5.Image? image, + int? quality, + ) => + (super.noSuchMethod( + Invocation.method( + #exportAsJpg, + [ + image, + quality, + ], + ), + returnValue: _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>.value( + _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #exportAsJpg, + [ + image, + quality, + ], + ), + )), + ) as _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>> exportAsPng( + _i5.Image? image) => + (super.noSuchMethod( + Invocation.method( + #exportAsPng, + [image], + ), + returnValue: _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>.value( + _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #exportAsPng, + [image], + ), + )), + ) as _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>); + + @override + _i2.Result<_i6.Uint8List, _i3.Failure> getProjectPreview(String? path) => + (super.noSuchMethod( + Invocation.method( + #getProjectPreview, + [path], + ), + returnValue: _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #getProjectPreview, + [path], + ), + ), + ) as _i2.Result<_i6.Uint8List, _i3.Failure>); +} + +/// A class which mocks [IPermissionService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIPermissionService extends _i1.Mock + implements _i3.IPermissionService { + MockIPermissionService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future requestAccessToPickPhotos() => (super.noSuchMethod( + Invocation.method( + #requestAccessToPickPhotos, + [], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future requestAccessForSavingToPhotos() => (super.noSuchMethod( + Invocation.method( + #requestAccessForSavingToPhotos, + [], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future requestAccessToSharedFileStorage() => (super.noSuchMethod( + Invocation.method( + #requestAccessToSharedFileStorage, + [], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); +} + +/// A class which mocks [IPhotoLibraryService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIPhotoLibraryService extends _i1.Mock + implements _i3.IPhotoLibraryService { + MockIPhotoLibraryService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.Result<_i2.Unit, _i3.Failure>> save( + String? filename, + _i6.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #save, + [ + filename, + data, + ], + ), + returnValue: _i4.Future<_i2.Result<_i2.Unit, _i3.Failure>>.value( + _FakeResult_0<_i2.Unit, _i3.Failure>( + this, + Invocation.method( + #save, + [ + filename, + data, + ], + ), + )), + ) as _i4.Future<_i2.Result<_i2.Unit, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>> pick() => + (super.noSuchMethod( + Invocation.method( + #pick, + [], + ), + returnValue: _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>.value( + _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #pick, + [], + ), + )), + ) as _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>); +} diff --git a/test/unit/io/usecase/save_as_raster_image_test.dart b/packages/io_library/test/unit/usecase/save_as_raster_image_test.dart similarity index 97% rename from test/unit/io/usecase/save_as_raster_image_test.dart rename to packages/io_library/test/unit/usecase/save_as_raster_image_test.dart index 78a68dda..2fb49a39 100644 --- a/test/unit/io/usecase/save_as_raster_image_test.dart +++ b/packages/io_library/test/unit/usecase/save_as_raster_image_test.dart @@ -3,11 +3,10 @@ import 'dart:ui'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:io_library/io_library.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:oxidized/oxidized.dart'; -import 'package:paintroid/core/failure.dart'; -import 'package:paintroid/io/io.dart'; import 'save_as_raster_image_test.mocks.dart'; @@ -183,7 +182,8 @@ void main() { .thenAnswer((_) async => false); final testMetaData = JpgMetaData(testName, testQuality); final result = await sut(testMetaData, fakeImage); - expect(result, const Err(SaveImageFailure.permissionDenied)); + expect(result, + const Err(SaveImageFailure.permissionDenied)); verify(mockPermissionService.requestAccessForSavingToPhotos()); verifyNoMoreInteractions(mockPermissionService); verifyZeroInteractions(mockImageService); @@ -195,7 +195,8 @@ void main() { .thenAnswer((_) async => false); final testMetaData = PngMetaData(testName); final result = await sut(testMetaData, fakeImage); - expect(result, const Err(SaveImageFailure.permissionDenied)); + expect(result, + const Err(SaveImageFailure.permissionDenied)); verify(mockPermissionService.requestAccessForSavingToPhotos()); verifyNoMoreInteractions(mockPermissionService); verifyZeroInteractions(mockImageService); diff --git a/packages/io_library/test/unit/usecase/save_as_raster_image_test.mocks.dart b/packages/io_library/test/unit/usecase/save_as_raster_image_test.mocks.dart new file mode 100644 index 00000000..9916e150 --- /dev/null +++ b/packages/io_library/test/unit/usecase/save_as_raster_image_test.mocks.dart @@ -0,0 +1,333 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in io_library/test/unit/usecase/save_as_raster_image_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; +import 'dart:io' as _i7; +import 'dart:typed_data' as _i6; +import 'dart:ui' as _i5; + +import 'package:io_library/io_library.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; +import 'package:oxidized/oxidized.dart' as _i2; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeResult_0 extends _i1.SmartFake + implements _i2.Result { + _FakeResult_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [IImageService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIImageService extends _i1.Mock implements _i3.IImageService { + MockIImageService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.Result<_i5.Image, _i3.Failure>> import( + _i6.Uint8List? fileData) => + (super.noSuchMethod( + Invocation.method( + #import, + [fileData], + ), + returnValue: _i4.Future<_i2.Result<_i5.Image, _i3.Failure>>.value( + _FakeResult_0<_i5.Image, _i3.Failure>( + this, + Invocation.method( + #import, + [fileData], + ), + )), + ) as _i4.Future<_i2.Result<_i5.Image, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>> exportAsJpg( + _i5.Image? image, + int? quality, + ) => + (super.noSuchMethod( + Invocation.method( + #exportAsJpg, + [ + image, + quality, + ], + ), + returnValue: _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>.value( + _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #exportAsJpg, + [ + image, + quality, + ], + ), + )), + ) as _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>> exportAsPng( + _i5.Image? image) => + (super.noSuchMethod( + Invocation.method( + #exportAsPng, + [image], + ), + returnValue: _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>.value( + _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #exportAsPng, + [image], + ), + )), + ) as _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>); + + @override + _i2.Result<_i6.Uint8List, _i3.Failure> getProjectPreview(String? path) => + (super.noSuchMethod( + Invocation.method( + #getProjectPreview, + [path], + ), + returnValue: _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #getProjectPreview, + [path], + ), + ), + ) as _i2.Result<_i6.Uint8List, _i3.Failure>); +} + +/// A class which mocks [IPhotoLibraryService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIPhotoLibraryService extends _i1.Mock + implements _i3.IPhotoLibraryService { + MockIPhotoLibraryService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.Result<_i2.Unit, _i3.Failure>> save( + String? filename, + _i6.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #save, + [ + filename, + data, + ], + ), + returnValue: _i4.Future<_i2.Result<_i2.Unit, _i3.Failure>>.value( + _FakeResult_0<_i2.Unit, _i3.Failure>( + this, + Invocation.method( + #save, + [ + filename, + data, + ], + ), + )), + ) as _i4.Future<_i2.Result<_i2.Unit, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>> pick() => + (super.noSuchMethod( + Invocation.method( + #pick, + [], + ), + returnValue: _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>.value( + _FakeResult_0<_i6.Uint8List, _i3.Failure>( + this, + Invocation.method( + #pick, + [], + ), + )), + ) as _i4.Future<_i2.Result<_i6.Uint8List, _i3.Failure>>); +} + +/// A class which mocks [IFileService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIFileService extends _i1.Mock implements _i3.IFileService { + MockIFileService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future<_i2.Result<_i7.File, _i3.Failure>> save( + String? filename, + _i6.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #save, + [ + filename, + data, + ], + ), + returnValue: _i4.Future<_i2.Result<_i7.File, _i3.Failure>>.value( + _FakeResult_0<_i7.File, _i3.Failure>( + this, + Invocation.method( + #save, + [ + filename, + data, + ], + ), + )), + ) as _i4.Future<_i2.Result<_i7.File, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i7.File, _i3.Failure>> saveToApplicationDirectory( + String? filename, + _i6.Uint8List? data, + ) => + (super.noSuchMethod( + Invocation.method( + #saveToApplicationDirectory, + [ + filename, + data, + ], + ), + returnValue: _i4.Future<_i2.Result<_i7.File, _i3.Failure>>.value( + _FakeResult_0<_i7.File, _i3.Failure>( + this, + Invocation.method( + #saveToApplicationDirectory, + [ + filename, + data, + ], + ), + )), + ) as _i4.Future<_i2.Result<_i7.File, _i3.Failure>>); + + @override + _i4.Future<_i2.Result<_i7.File, _i3.Failure>> pick() => (super.noSuchMethod( + Invocation.method( + #pick, + [], + ), + returnValue: _i4.Future<_i2.Result<_i7.File, _i3.Failure>>.value( + _FakeResult_0<_i7.File, _i3.Failure>( + this, + Invocation.method( + #pick, + [], + ), + )), + ) as _i4.Future<_i2.Result<_i7.File, _i3.Failure>>); + + @override + _i2.Result<_i7.File, _i3.Failure> getFile(String? path) => + (super.noSuchMethod( + Invocation.method( + #getFile, + [path], + ), + returnValue: _FakeResult_0<_i7.File, _i3.Failure>( + this, + Invocation.method( + #getFile, + [path], + ), + ), + ) as _i2.Result<_i7.File, _i3.Failure>); + + @override + _i4.Future checkIfFileExistsInApplicationDirectory(String? fileName) => + (super.noSuchMethod( + Invocation.method( + #checkIfFileExistsInApplicationDirectory, + [fileName], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future<_i2.Result<_i7.FileSystemEntity, _i3.Failure>> + deleteFileInApplicationDirectory(String? fileName) => (super.noSuchMethod( + Invocation.method( + #deleteFileInApplicationDirectory, + [fileName], + ), + returnValue: + _i4.Future<_i2.Result<_i7.FileSystemEntity, _i3.Failure>>.value( + _FakeResult_0<_i7.FileSystemEntity, _i3.Failure>( + this, + Invocation.method( + #deleteFileInApplicationDirectory, + [fileName], + ), + )), + ) as _i4.Future<_i2.Result<_i7.FileSystemEntity, _i3.Failure>>); +} + +/// A class which mocks [IPermissionService]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIPermissionService extends _i1.Mock + implements _i3.IPermissionService { + MockIPermissionService() { + _i1.throwOnMissingStub(this); + } + + @override + _i4.Future requestAccessToPickPhotos() => (super.noSuchMethod( + Invocation.method( + #requestAccessToPickPhotos, + [], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future requestAccessForSavingToPhotos() => (super.noSuchMethod( + Invocation.method( + #requestAccessForSavingToPhotos, + [], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future requestAccessToSharedFileStorage() => (super.noSuchMethod( + Invocation.method( + #requestAccessToSharedFileStorage, + [], + ), + returnValue: _i4.Future.value(false), + ) as _i4.Future); +} diff --git a/packages/l10n/.metadata b/packages/l10n/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/l10n/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/l10n/analysis_options.yaml b/packages/l10n/analysis_options.yaml new file mode 100644 index 00000000..068a0a7e --- /dev/null +++ b/packages/l10n/analysis_options.yaml @@ -0,0 +1,25 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart + - lib/src/l10n/app_localizations.dart + - lib/src/l10n/app_localizations_en.dart diff --git a/packages/l10n/l10n.yaml b/packages/l10n/l10n.yaml new file mode 100644 index 00000000..160abcd7 --- /dev/null +++ b/packages/l10n/l10n.yaml @@ -0,0 +1,6 @@ +arb-dir: lib/src/l10n +template-arb-file: app_translations_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +synthetic-package: false +nullable-getter: false diff --git a/packages/l10n/lib/l10n.dart b/packages/l10n/lib/l10n.dart new file mode 100644 index 00000000..5ef74664 --- /dev/null +++ b/packages/l10n/lib/l10n.dart @@ -0,0 +1,4 @@ +library l10n; + +export 'src/l10n/app_localizations.dart'; +export 'src/l10n/app_localizations_en.dart'; diff --git a/packages/l10n/lib/src/l10n/app_localizations.dart b/packages/l10n/lib/src/l10n/app_localizations.dart new file mode 100644 index 00000000..7825f1ae --- /dev/null +++ b/packages/l10n/lib/src/l10n/app_localizations.dart @@ -0,0 +1,179 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [Locale('en')]; + + /// No description provided for @fullscreen. + /// + /// In en, this message translates to: + /// **'Fullscreen'** + String get fullscreen; + + /// No description provided for @saveImage. + /// + /// In en, this message translates to: + /// **'Save image'** + String get saveImage; + + /// No description provided for @loadImage. + /// + /// In en, this message translates to: + /// **'Load image'** + String get loadImage; + + /// No description provided for @newImage. + /// + /// In en, this message translates to: + /// **'New image'** + String get newImage; + + /// No description provided for @saveProject. + /// + /// In en, this message translates to: + /// **'Save project'** + String get saveProject; + + /// No description provided for @tools. + /// + /// In en, this message translates to: + /// **'Tools'** + String get tools; + + /// No description provided for @brush. + /// + /// In en, this message translates to: + /// **'Brush'** + String get brush; + + /// No description provided for @color. + /// + /// In en, this message translates to: + /// **'Color'** + String get color; + + /// No description provided for @layers. + /// + /// In en, this message translates to: + /// **'Layers'** + String get layers; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} \ No newline at end of file diff --git a/packages/l10n/lib/src/l10n/app_localizations_en.dart b/packages/l10n/lib/src/l10n/app_localizations_en.dart new file mode 100644 index 00000000..3d07e5cc --- /dev/null +++ b/packages/l10n/lib/src/l10n/app_localizations_en.dart @@ -0,0 +1,33 @@ +import 'app_localizations.dart'; + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get fullscreen => 'Fullscreen'; + + @override + String get saveImage => 'Save image'; + + @override + String get loadImage => 'Load image'; + + @override + String get newImage => 'New image'; + + @override + String get saveProject => 'Save project'; + + @override + String get tools => 'Tools'; + + @override + String get brush => 'Brush'; + + @override + String get color => 'Color'; + + @override + String get layers => 'Layers'; +} diff --git a/assets/l10n/en.arb b/packages/l10n/lib/src/l10n/app_translations_en.arb similarity index 100% rename from assets/l10n/en.arb rename to packages/l10n/lib/src/l10n/app_translations_en.arb diff --git a/packages/l10n/pubspec.yaml b/packages/l10n/pubspec.yaml new file mode 100644 index 00000000..3510cdb1 --- /dev/null +++ b/packages/l10n/pubspec.yaml @@ -0,0 +1,26 @@ +name: l10n +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + + flutter_localization: ^0.1.12 + intl: ^0.18.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + + build_runner: ^2.2.0 + +flutter: diff --git a/packages/tools/.metadata b/packages/tools/.metadata new file mode 100644 index 00000000..9596faee --- /dev/null +++ b/packages/tools/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 796c8ef79279f9c774545b3771238c3098dbefab + channel: stable + +project_type: package diff --git a/packages/tools/analysis_options.yaml b/packages/tools/analysis_options.yaml new file mode 100644 index 00000000..1ee68bd9 --- /dev/null +++ b/packages/tools/analysis_options.yaml @@ -0,0 +1,23 @@ +include: package:flutter_lints/flutter.yaml +linter: + rules: + always_use_package_imports: true + avoid_relative_lib_imports: true + prefer_relative_imports: false + prefer_single_quotes: true + avoid_void_async: true + constant_identifier_names: false + +analyzer: + errors: + missing_enum_constant_in_switch: error + exhaustive_cases: error + unused_element: error + type_annotate_public_apis: error + missing_required_param: error + invalid_use_of_protected_member: error + unused_import: error + + exclude: + - lib/src/**.pb*.dart + - lib/src/data/*.g.dart diff --git a/lib/tool/src/brush_tool/brush_tool.dart b/packages/tools/lib/src/brush_tool/brush_tool.dart similarity index 82% rename from lib/tool/src/brush_tool/brush_tool.dart rename to packages/tools/lib/src/brush_tool/brush_tool.dart index 983dc824..65379ac2 100644 --- a/lib/tool/src/brush_tool/brush_tool.dart +++ b/packages/tools/lib/src/brush_tool/brush_tool.dart @@ -1,13 +1,9 @@ import 'dart:ui'; +import 'package:command/command.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart'; -import 'package:paintroid/command/src/command_factory.dart'; -import 'package:paintroid/command/src/command_manager.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; -import 'package:paintroid/tool/src/tool.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:tools/tools.dart'; class BrushTool extends Tool with EquatableMixin { BrushTool({ diff --git a/lib/tool/src/brush_tool/brush_tool_provider.dart b/packages/tools/lib/src/brush_tool/brush_tool_provider.dart similarity index 52% rename from lib/tool/src/brush_tool/brush_tool_provider.dart rename to packages/tools/lib/src/brush_tool/brush_tool_provider.dart index aad06315..66d9b048 100644 --- a/lib/tool/src/brush_tool/brush_tool_provider.dart +++ b/packages/tools/lib/src/brush_tool/brush_tool_provider.dart @@ -1,10 +1,6 @@ -import 'package:paintroid/command/src/command_factory_provider.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:command/command_providers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:tools/tools.dart'; part 'brush_tool_provider.g.dart'; diff --git a/packages/tools/lib/src/brush_tool/brush_tool_provider.g.dart b/packages/tools/lib/src/brush_tool/brush_tool_provider.g.dart new file mode 100644 index 00000000..b08cd4a0 --- /dev/null +++ b/packages/tools/lib/src/brush_tool/brush_tool_provider.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'brush_tool_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$brushToolHash() => r'dfa4f2e7a9cb8734828ec99dd983c7904c231e46'; + +/// See also [brushTool]. +@ProviderFor(brushTool) +final brushToolProvider = AutoDisposeProvider.internal( + brushTool, + name: r'brushToolProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$brushToolHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef BrushToolRef = AutoDisposeProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/tool/src/brush_tool/brush_tool_state_data.dart b/packages/tools/lib/src/brush_tool/brush_tool_state_data.dart similarity index 100% rename from lib/tool/src/brush_tool/brush_tool_state_data.dart rename to packages/tools/lib/src/brush_tool/brush_tool_state_data.dart diff --git a/packages/tools/lib/src/brush_tool/brush_tool_state_data.freezed.dart b/packages/tools/lib/src/brush_tool/brush_tool_state_data.freezed.dart new file mode 100644 index 00000000..f48b047f --- /dev/null +++ b/packages/tools/lib/src/brush_tool/brush_tool_state_data.freezed.dart @@ -0,0 +1,134 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'brush_tool_state_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$BrushToolStateData { + Paint get paint => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $BrushToolStateDataCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $BrushToolStateDataCopyWith<$Res> { + factory $BrushToolStateDataCopyWith( + BrushToolStateData value, $Res Function(BrushToolStateData) then) = + _$BrushToolStateDataCopyWithImpl<$Res, BrushToolStateData>; + @useResult + $Res call({Paint paint}); +} + +/// @nodoc +class _$BrushToolStateDataCopyWithImpl<$Res, $Val extends BrushToolStateData> + implements $BrushToolStateDataCopyWith<$Res> { + _$BrushToolStateDataCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? paint = null, + }) { + return _then(_value.copyWith( + paint: null == paint + ? _value.paint + : paint // ignore: cast_nullable_to_non_nullable + as Paint, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_BrushToolStateDataCopyWith<$Res> + implements $BrushToolStateDataCopyWith<$Res> { + factory _$$_BrushToolStateDataCopyWith(_$_BrushToolStateData value, + $Res Function(_$_BrushToolStateData) then) = + __$$_BrushToolStateDataCopyWithImpl<$Res>; + @override + @useResult + $Res call({Paint paint}); +} + +/// @nodoc +class __$$_BrushToolStateDataCopyWithImpl<$Res> + extends _$BrushToolStateDataCopyWithImpl<$Res, _$_BrushToolStateData> + implements _$$_BrushToolStateDataCopyWith<$Res> { + __$$_BrushToolStateDataCopyWithImpl( + _$_BrushToolStateData _value, $Res Function(_$_BrushToolStateData) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? paint = null, + }) { + return _then(_$_BrushToolStateData( + paint: null == paint + ? _value.paint + : paint // ignore: cast_nullable_to_non_nullable + as Paint, + )); + } +} + +/// @nodoc + +class _$_BrushToolStateData implements _BrushToolStateData { + const _$_BrushToolStateData({required this.paint}); + + @override + final Paint paint; + + @override + String toString() { + return 'BrushToolStateData(paint: $paint)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_BrushToolStateData && + (identical(other.paint, paint) || other.paint == paint)); + } + + @override + int get hashCode => Object.hash(runtimeType, paint); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_BrushToolStateDataCopyWith<_$_BrushToolStateData> get copyWith => + __$$_BrushToolStateDataCopyWithImpl<_$_BrushToolStateData>( + this, _$identity); +} + +abstract class _BrushToolStateData implements BrushToolStateData { + const factory _BrushToolStateData({required final Paint paint}) = + _$_BrushToolStateData; + + @override + Paint get paint; + @override + @JsonKey(ignore: true) + _$$_BrushToolStateDataCopyWith<_$_BrushToolStateData> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/tool/src/brush_tool/brush_tool_state_provider.dart b/packages/tools/lib/src/brush_tool/brush_tool_state_provider.dart similarity index 89% rename from lib/tool/src/brush_tool/brush_tool_state_provider.dart rename to packages/tools/lib/src/brush_tool/brush_tool_state_provider.dart index dd3c224f..52921062 100644 --- a/lib/tool/src/brush_tool/brush_tool_state_provider.dart +++ b/packages/tools/lib/src/brush_tool/brush_tool_state_provider.dart @@ -1,8 +1,8 @@ import 'dart:ui'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_data.dart'; +import 'package:command/graphic_factory/graphic_factory_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:tools/tools.dart'; part 'brush_tool_state_provider.g.dart'; diff --git a/packages/tools/lib/src/brush_tool/brush_tool_state_provider.g.dart b/packages/tools/lib/src/brush_tool/brush_tool_state_provider.g.dart new file mode 100644 index 00000000..4bffdbde --- /dev/null +++ b/packages/tools/lib/src/brush_tool/brush_tool_state_provider.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'brush_tool_state_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$brushToolStateHash() => r'1dbb16d53ddafba863e739874849eb790b4db667'; + +/// See also [BrushToolState]. +@ProviderFor(BrushToolState) +final brushToolStateProvider = + AutoDisposeNotifierProvider.internal( + BrushToolState.new, + name: r'brushToolStateProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$brushToolStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$BrushToolState = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/tool/src/tool_types.dart b/packages/tools/lib/src/enums/tool_types.dart similarity index 100% rename from lib/tool/src/tool_types.dart rename to packages/tools/lib/src/enums/tool_types.dart diff --git a/lib/tool/src/eraser_tool/eraser_tool_provider.dart b/packages/tools/lib/src/eraser_tool/eraser_tool_provider.dart similarity index 52% rename from lib/tool/src/eraser_tool/eraser_tool_provider.dart rename to packages/tools/lib/src/eraser_tool/eraser_tool_provider.dart index 434e2cf3..5b28e24c 100644 --- a/lib/tool/src/eraser_tool/eraser_tool_provider.dart +++ b/packages/tools/lib/src/eraser_tool/eraser_tool_provider.dart @@ -1,10 +1,6 @@ -import 'package:paintroid/command/src/command_factory_provider.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/core/graphic_factory_provider.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:command/command_providers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:tools/tools.dart'; part 'eraser_tool_provider.g.dart'; diff --git a/packages/tools/lib/src/eraser_tool/eraser_tool_provider.g.dart b/packages/tools/lib/src/eraser_tool/eraser_tool_provider.g.dart new file mode 100644 index 00000000..6de891a9 --- /dev/null +++ b/packages/tools/lib/src/eraser_tool/eraser_tool_provider.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'eraser_tool_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$eraserToolHash() => r'a92e9502cc061e5298bc9c8a20fcb30f8759e1e4'; + +/// See also [eraserTool]. +@ProviderFor(eraserTool) +final eraserToolProvider = AutoDisposeProvider.internal( + eraserTool, + name: r'eraserToolProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$eraserToolHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef EraserToolRef = AutoDisposeProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/tool/src/hand_tool/hand_tool.dart b/packages/tools/lib/src/hand_tool/hand_tool.dart similarity index 76% rename from lib/tool/src/hand_tool/hand_tool.dart rename to packages/tools/lib/src/hand_tool/hand_tool.dart index cd60dad9..b130941b 100644 --- a/lib/tool/src/hand_tool/hand_tool.dart +++ b/packages/tools/lib/src/hand_tool/hand_tool.dart @@ -1,11 +1,8 @@ import 'dart:ui'; +import 'package:command/command.dart'; import 'package:equatable/equatable.dart'; -import 'package:paintroid/command/src/command_factory.dart'; -import 'package:paintroid/command/src/command_manager.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/tool/src/tool.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:tools/tools.dart'; class HandTool extends Tool with EquatableMixin { HandTool({ diff --git a/lib/tool/src/hand_tool/hand_tool_provider.dart b/packages/tools/lib/src/hand_tool/hand_tool_provider.dart similarity index 53% rename from lib/tool/src/hand_tool/hand_tool_provider.dart rename to packages/tools/lib/src/hand_tool/hand_tool_provider.dart index fc3e5ed2..334d58e9 100644 --- a/lib/tool/src/hand_tool/hand_tool_provider.dart +++ b/packages/tools/lib/src/hand_tool/hand_tool_provider.dart @@ -1,9 +1,6 @@ -import 'package:paintroid/command/src/command_factory_provider.dart'; -import 'package:paintroid/command/src/command_manager_provider.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart'; -import 'package:paintroid/tool/src/hand_tool/hand_tool.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:command/command_providers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:tools/tools.dart'; part 'hand_tool_provider.g.dart'; diff --git a/packages/tools/lib/src/hand_tool/hand_tool_provider.g.dart b/packages/tools/lib/src/hand_tool/hand_tool_provider.g.dart new file mode 100644 index 00000000..9788d25b --- /dev/null +++ b/packages/tools/lib/src/hand_tool/hand_tool_provider.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'hand_tool_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$handToolHash() => r'693afd99b7c1ad8fb5857ebf4c75d1482f0facc5'; + +/// See also [handTool]. +@ProviderFor(handTool) +final handToolProvider = AutoDisposeProvider.internal( + handTool, + name: r'handToolProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$handToolHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef HandToolRef = AutoDisposeProviderRef; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/tool/src/tool.dart b/packages/tools/lib/src/tool.dart similarity index 80% rename from lib/tool/src/tool.dart rename to packages/tools/lib/src/tool.dart index 9a4bdd76..78ac0831 100644 --- a/lib/tool/src/tool.dart +++ b/packages/tools/lib/src/tool.dart @@ -1,7 +1,7 @@ import 'dart:ui'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:command/command.dart'; +import 'package:tools/src/enums/tool_types.dart'; abstract class Tool { const Tool({ diff --git a/lib/tool/src/tool_data.dart b/packages/tools/lib/src/tool_data.dart similarity index 97% rename from lib/tool/src/tool_data.dart rename to packages/tools/lib/src/tool_data.dart index 968809e0..bb0a161e 100644 --- a/lib/tool/src/tool_data.dart +++ b/packages/tools/lib/src/tool_data.dart @@ -1,4 +1,4 @@ -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:tools/src/enums/tool_types.dart'; class ToolData { final String name; diff --git a/lib/tool/src/toolbox/toolbox_state_data.dart b/packages/tools/lib/src/toolbox/toolbox_state_data.dart similarity index 88% rename from lib/tool/src/toolbox/toolbox_state_data.dart rename to packages/tools/lib/src/toolbox/toolbox_state_data.dart index 9833c65b..6d0719de 100644 --- a/lib/tool/src/toolbox/toolbox_state_data.dart +++ b/packages/tools/lib/src/toolbox/toolbox_state_data.dart @@ -1,5 +1,5 @@ import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:paintroid/tool/tool.dart'; +import 'package:tools/tools.dart'; part 'toolbox_state_data.freezed.dart'; diff --git a/packages/tools/lib/src/toolbox/toolbox_state_data.freezed.dart b/packages/tools/lib/src/toolbox/toolbox_state_data.freezed.dart new file mode 100644 index 00000000..a7c007ad --- /dev/null +++ b/packages/tools/lib/src/toolbox/toolbox_state_data.freezed.dart @@ -0,0 +1,173 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'toolbox_state_data.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$ToolBoxStateData { + Tool get currentTool => throw _privateConstructorUsedError; + ToolType get currentToolType => throw _privateConstructorUsedError; + bool get isDown => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $ToolBoxStateDataCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ToolBoxStateDataCopyWith<$Res> { + factory $ToolBoxStateDataCopyWith( + ToolBoxStateData value, $Res Function(ToolBoxStateData) then) = + _$ToolBoxStateDataCopyWithImpl<$Res, ToolBoxStateData>; + @useResult + $Res call({Tool currentTool, ToolType currentToolType, bool isDown}); +} + +/// @nodoc +class _$ToolBoxStateDataCopyWithImpl<$Res, $Val extends ToolBoxStateData> + implements $ToolBoxStateDataCopyWith<$Res> { + _$ToolBoxStateDataCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? currentTool = null, + Object? currentToolType = null, + Object? isDown = null, + }) { + return _then(_value.copyWith( + currentTool: null == currentTool + ? _value.currentTool + : currentTool // ignore: cast_nullable_to_non_nullable + as Tool, + currentToolType: null == currentToolType + ? _value.currentToolType + : currentToolType // ignore: cast_nullable_to_non_nullable + as ToolType, + isDown: null == isDown + ? _value.isDown + : isDown // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_ToolBoxStateDataCopyWith<$Res> + implements $ToolBoxStateDataCopyWith<$Res> { + factory _$$_ToolBoxStateDataCopyWith( + _$_ToolBoxStateData value, $Res Function(_$_ToolBoxStateData) then) = + __$$_ToolBoxStateDataCopyWithImpl<$Res>; + @override + @useResult + $Res call({Tool currentTool, ToolType currentToolType, bool isDown}); +} + +/// @nodoc +class __$$_ToolBoxStateDataCopyWithImpl<$Res> + extends _$ToolBoxStateDataCopyWithImpl<$Res, _$_ToolBoxStateData> + implements _$$_ToolBoxStateDataCopyWith<$Res> { + __$$_ToolBoxStateDataCopyWithImpl( + _$_ToolBoxStateData _value, $Res Function(_$_ToolBoxStateData) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? currentTool = null, + Object? currentToolType = null, + Object? isDown = null, + }) { + return _then(_$_ToolBoxStateData( + currentTool: null == currentTool + ? _value.currentTool + : currentTool // ignore: cast_nullable_to_non_nullable + as Tool, + currentToolType: null == currentToolType + ? _value.currentToolType + : currentToolType // ignore: cast_nullable_to_non_nullable + as ToolType, + isDown: null == isDown + ? _value.isDown + : isDown // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$_ToolBoxStateData implements _ToolBoxStateData { + const _$_ToolBoxStateData( + {required this.currentTool, + required this.currentToolType, + required this.isDown}); + + @override + final Tool currentTool; + @override + final ToolType currentToolType; + @override + final bool isDown; + + @override + String toString() { + return 'ToolBoxStateData(currentTool: $currentTool, currentToolType: $currentToolType, isDown: $isDown)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_ToolBoxStateData && + (identical(other.currentTool, currentTool) || + other.currentTool == currentTool) && + (identical(other.currentToolType, currentToolType) || + other.currentToolType == currentToolType) && + (identical(other.isDown, isDown) || other.isDown == isDown)); + } + + @override + int get hashCode => + Object.hash(runtimeType, currentTool, currentToolType, isDown); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_ToolBoxStateDataCopyWith<_$_ToolBoxStateData> get copyWith => + __$$_ToolBoxStateDataCopyWithImpl<_$_ToolBoxStateData>(this, _$identity); +} + +abstract class _ToolBoxStateData implements ToolBoxStateData { + const factory _ToolBoxStateData( + {required final Tool currentTool, + required final ToolType currentToolType, + required final bool isDown}) = _$_ToolBoxStateData; + + @override + Tool get currentTool; + @override + ToolType get currentToolType; + @override + bool get isDown; + @override + @JsonKey(ignore: true) + _$$_ToolBoxStateDataCopyWith<_$_ToolBoxStateData> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/tool/src/toolbox/toolbox_state_provider.dart b/packages/tools/lib/src/toolbox/toolbox_state_provider.dart similarity index 94% rename from lib/tool/src/toolbox/toolbox_state_provider.dart rename to packages/tools/lib/src/toolbox/toolbox_state_provider.dart index 5c08008b..e2a98847 100644 --- a/lib/tool/src/toolbox/toolbox_state_provider.dart +++ b/packages/tools/lib/src/toolbox/toolbox_state_provider.dart @@ -1,9 +1,8 @@ import 'dart:ui'; -import 'package:paintroid/tool/src/hand_tool/hand_tool_provider.dart'; -import 'package:paintroid/tool/tool.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:toast/toast.dart'; +import 'package:tools/tools.dart'; part 'toolbox_state_provider.g.dart'; diff --git a/packages/tools/lib/src/toolbox/toolbox_state_provider.g.dart b/packages/tools/lib/src/toolbox/toolbox_state_provider.g.dart new file mode 100644 index 00000000..ef1abc1d --- /dev/null +++ b/packages/tools/lib/src/toolbox/toolbox_state_provider.g.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'toolbox_state_provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$toolBoxStateHash() => r'2a54e69ebf608acfacc008c0d05cbd140315c6b0'; + +/// See also [ToolBoxState]. +@ProviderFor(ToolBoxState) +final toolBoxStateProvider = + AutoDisposeNotifierProvider.internal( + ToolBoxState.new, + name: r'toolBoxStateProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$toolBoxStateHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ToolBoxState = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member diff --git a/lib/tool/tool.dart b/packages/tools/lib/tools.dart similarity index 73% rename from lib/tool/tool.dart rename to packages/tools/lib/tools.dart index 07c23993..6a434f3c 100644 --- a/lib/tool/tool.dart +++ b/packages/tools/lib/tools.dart @@ -1,10 +1,19 @@ +library tools; + export 'src/brush_tool/brush_tool.dart'; export 'src/brush_tool/brush_tool_provider.dart'; export 'src/brush_tool/brush_tool_state_data.dart'; export 'src/brush_tool/brush_tool_state_provider.dart'; + +export 'src/enums/tool_types.dart'; + +export 'src/hand_tool/hand_tool.dart'; +export 'src/hand_tool/hand_tool_provider.dart'; + export 'src/eraser_tool/eraser_tool_provider.dart'; -export 'src/tool.dart'; -export 'src/tool_data.dart'; -export 'src/tool_types.dart'; + export 'src/toolbox/toolbox_state_data.dart'; export 'src/toolbox/toolbox_state_provider.dart'; + +export 'src/tool_data.dart'; +export 'src/tool.dart'; diff --git a/packages/tools/pubspec.yaml b/packages/tools/pubspec.yaml new file mode 100644 index 00000000..d00c2f2a --- /dev/null +++ b/packages/tools/pubspec.yaml @@ -0,0 +1,45 @@ +name: tools +description: A new Flutter package project. +version: 0.0.1 +publish_to: "none" + +environment: + sdk: ">=3.0.5 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + flutter_riverpod: ^2.3.6 + riverpod_annotation: ^2.1.1 + freezed_annotation: ^2.4.1 + + equatable: ^2.0.3 + toast: ^0.3.0 + + # Internal packages + component_library: + path: ../component_library + database: + path: ../database + command: + path: ../command + workspace_screen: + path: ../features/workspace_screen + +dev_dependencies: + flutter_test: + sdk: flutter + + mockito: ^5.2.0 + flutter_launcher_icons: ^0.9.3 + flutter_lints: ^2.0.1 + floor_generator: ^1.2.0 + riverpod_generator: ^2.2.3 + riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 + freezed: ^2.4.1 + +flutter: + uses-material-design: true diff --git a/test/unit/tool/brush_tool_state_test.dart b/packages/tools/test/unit/brush_tool_state_test.dart similarity index 88% rename from test/unit/tool/brush_tool_state_test.dart rename to packages/tools/test/unit/brush_tool_state_test.dart index d6de7653..0f48b5c6 100644 --- a/test/unit/tool/brush_tool_state_test.dart +++ b/packages/tools/test/unit/brush_tool_state_test.dart @@ -1,9 +1,9 @@ -import 'package:flutter/material.dart'; // needed for Paint, Color, etc. +import 'package:command/graphic_factory/graphic_factory.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/tool/src/brush_tool/brush_tool_state_provider.dart'; -import 'package:riverpod/riverpod.dart'; +import 'package:tools/tools.dart'; class MockGraphicFactory extends Mock implements GraphicFactory {} diff --git a/test/unit/tool/brush_tool_test.dart b/packages/tools/test/unit/brush_tool_test.dart similarity index 95% rename from test/unit/tool/brush_tool_test.dart rename to packages/tools/test/unit/brush_tool_test.dart index 8a6b9141..06a21b60 100644 --- a/test/unit/tool/brush_tool_test.dart +++ b/packages/tools/test/unit/brush_tool_test.dart @@ -1,12 +1,10 @@ import 'dart:ui'; +import 'package:command/command.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:paintroid/command/command.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/core/path_with_action_history.dart'; -import 'package:paintroid/tool/tool.dart'; +import 'package:tools/tools.dart'; import 'brush_tool_test.mocks.dart'; diff --git a/packages/tools/test/unit/brush_tool_test.mocks.dart b/packages/tools/test/unit/brush_tool_test.mocks.dart new file mode 100644 index 00000000..381528e4 --- /dev/null +++ b/packages/tools/test/unit/brush_tool_test.mocks.dart @@ -0,0 +1,1108 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in tools/test/unit/brush_tool_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:typed_data' as _i4; +import 'dart:ui' as _i2; + +import 'package:command/command.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakePath_0 extends _i1.SmartFake implements _i2.Path { + _FakePath_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeRect_1 extends _i1.SmartFake implements _i2.Rect { + _FakeRect_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePathMetrics_2 extends _i1.SmartFake implements _i2.PathMetrics { + _FakePathMetrics_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeOffset_3 extends _i1.SmartFake implements _i2.Offset { + _FakeOffset_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePathWithActionHistory_4 extends _i1.SmartFake + implements _i3.PathWithActionHistory { + _FakePathWithActionHistory_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePaint_5 extends _i1.SmartFake implements _i2.Paint { + _FakePaint_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDrawPathCommand_6 extends _i1.SmartFake + implements _i3.DrawPathCommand { + _FakeDrawPathCommand_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePictureRecorder_7 extends _i1.SmartFake + implements _i2.PictureRecorder { + _FakePictureRecorder_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeCanvas_8 extends _i1.SmartFake implements _i2.Canvas { + _FakeCanvas_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [PathWithActionHistory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPathWithActionHistory extends _i1.Mock + implements _i3.PathWithActionHistory { + MockPathWithActionHistory() { + _i1.throwOnMissingStub(this); + } + + @override + List<_i3.PathAction> get actions => (super.noSuchMethod( + Invocation.getter(#actions), + returnValue: <_i3.PathAction>[], + ) as List<_i3.PathAction>); + + @override + _i2.PathFillType get fillType => (super.noSuchMethod( + Invocation.getter(#fillType), + returnValue: _i2.PathFillType.nonZero, + ) as _i2.PathFillType); + + @override + set fillType(_i2.PathFillType? value) => super.noSuchMethod( + Invocation.setter( + #fillType, + value, + ), + returnValueForMissingStub: null, + ); + + @override + void moveTo( + double? x, + double? y, + ) => + super.noSuchMethod( + Invocation.method( + #moveTo, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void lineTo( + double? x, + double? y, + ) => + super.noSuchMethod( + Invocation.method( + #lineTo, + [ + x, + y, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void close() => super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValueForMissingStub: null, + ); + + @override + Map toJson() => (super.noSuchMethod( + Invocation.method( + #toJson, + [], + ), + returnValue: {}, + ) as Map); + + @override + void relativeMoveTo( + double? dx, + double? dy, + ) => + super.noSuchMethod( + Invocation.method( + #relativeMoveTo, + [ + dx, + dy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void relativeLineTo( + double? dx, + double? dy, + ) => + super.noSuchMethod( + Invocation.method( + #relativeLineTo, + [ + dx, + dy, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void quadraticBezierTo( + double? x1, + double? y1, + double? x2, + double? y2, + ) => + super.noSuchMethod( + Invocation.method( + #quadraticBezierTo, + [ + x1, + y1, + x2, + y2, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void relativeQuadraticBezierTo( + double? x1, + double? y1, + double? x2, + double? y2, + ) => + super.noSuchMethod( + Invocation.method( + #relativeQuadraticBezierTo, + [ + x1, + y1, + x2, + y2, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void cubicTo( + double? x1, + double? y1, + double? x2, + double? y2, + double? x3, + double? y3, + ) => + super.noSuchMethod( + Invocation.method( + #cubicTo, + [ + x1, + y1, + x2, + y2, + x3, + y3, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void relativeCubicTo( + double? x1, + double? y1, + double? x2, + double? y2, + double? x3, + double? y3, + ) => + super.noSuchMethod( + Invocation.method( + #relativeCubicTo, + [ + x1, + y1, + x2, + y2, + x3, + y3, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void conicTo( + double? x1, + double? y1, + double? x2, + double? y2, + double? w, + ) => + super.noSuchMethod( + Invocation.method( + #conicTo, + [ + x1, + y1, + x2, + y2, + w, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void relativeConicTo( + double? x1, + double? y1, + double? x2, + double? y2, + double? w, + ) => + super.noSuchMethod( + Invocation.method( + #relativeConicTo, + [ + x1, + y1, + x2, + y2, + w, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void arcTo( + _i2.Rect? rect, + double? startAngle, + double? sweepAngle, + bool? forceMoveTo, + ) => + super.noSuchMethod( + Invocation.method( + #arcTo, + [ + rect, + startAngle, + sweepAngle, + forceMoveTo, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void arcToPoint( + _i2.Offset? arcEnd, { + _i2.Radius? radius = _i2.Radius.zero, + double? rotation = 0.0, + bool? largeArc = false, + bool? clockwise = true, + }) => + super.noSuchMethod( + Invocation.method( + #arcToPoint, + [arcEnd], + { + #radius: radius, + #rotation: rotation, + #largeArc: largeArc, + #clockwise: clockwise, + }, + ), + returnValueForMissingStub: null, + ); + + @override + void relativeArcToPoint( + _i2.Offset? arcEndDelta, { + _i2.Radius? radius = _i2.Radius.zero, + double? rotation = 0.0, + bool? largeArc = false, + bool? clockwise = true, + }) => + super.noSuchMethod( + Invocation.method( + #relativeArcToPoint, + [arcEndDelta], + { + #radius: radius, + #rotation: rotation, + #largeArc: largeArc, + #clockwise: clockwise, + }, + ), + returnValueForMissingStub: null, + ); + + @override + void addRect(_i2.Rect? rect) => super.noSuchMethod( + Invocation.method( + #addRect, + [rect], + ), + returnValueForMissingStub: null, + ); + + @override + void addOval(_i2.Rect? oval) => super.noSuchMethod( + Invocation.method( + #addOval, + [oval], + ), + returnValueForMissingStub: null, + ); + + @override + void addArc( + _i2.Rect? oval, + double? startAngle, + double? sweepAngle, + ) => + super.noSuchMethod( + Invocation.method( + #addArc, + [ + oval, + startAngle, + sweepAngle, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void addPolygon( + List<_i2.Offset>? points, + bool? close, + ) => + super.noSuchMethod( + Invocation.method( + #addPolygon, + [ + points, + close, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void addRRect(_i2.RRect? rrect) => super.noSuchMethod( + Invocation.method( + #addRRect, + [rrect], + ), + returnValueForMissingStub: null, + ); + + @override + void addPath( + _i2.Path? path, + _i2.Offset? offset, { + _i4.Float64List? matrix4, + }) => + super.noSuchMethod( + Invocation.method( + #addPath, + [ + path, + offset, + ], + {#matrix4: matrix4}, + ), + returnValueForMissingStub: null, + ); + + @override + void extendWithPath( + _i2.Path? path, + _i2.Offset? offset, { + _i4.Float64List? matrix4, + }) => + super.noSuchMethod( + Invocation.method( + #extendWithPath, + [ + path, + offset, + ], + {#matrix4: matrix4}, + ), + returnValueForMissingStub: null, + ); + + @override + void reset() => super.noSuchMethod( + Invocation.method( + #reset, + [], + ), + returnValueForMissingStub: null, + ); + + @override + bool contains(_i2.Offset? point) => (super.noSuchMethod( + Invocation.method( + #contains, + [point], + ), + returnValue: false, + ) as bool); + + @override + _i2.Path shift(_i2.Offset? offset) => (super.noSuchMethod( + Invocation.method( + #shift, + [offset], + ), + returnValue: _FakePath_0( + this, + Invocation.method( + #shift, + [offset], + ), + ), + ) as _i2.Path); + + @override + _i2.Path transform(_i4.Float64List? matrix4) => (super.noSuchMethod( + Invocation.method( + #transform, + [matrix4], + ), + returnValue: _FakePath_0( + this, + Invocation.method( + #transform, + [matrix4], + ), + ), + ) as _i2.Path); + + @override + _i2.Rect getBounds() => (super.noSuchMethod( + Invocation.method( + #getBounds, + [], + ), + returnValue: _FakeRect_1( + this, + Invocation.method( + #getBounds, + [], + ), + ), + ) as _i2.Rect); + + @override + _i2.PathMetrics computeMetrics({bool? forceClosed = false}) => + (super.noSuchMethod( + Invocation.method( + #computeMetrics, + [], + {#forceClosed: forceClosed}, + ), + returnValue: _FakePathMetrics_2( + this, + Invocation.method( + #computeMetrics, + [], + {#forceClosed: forceClosed}, + ), + ), + ) as _i2.PathMetrics); +} + +/// A class which mocks [Offset]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockOffset extends _i1.Mock implements _i2.Offset { + MockOffset() { + _i1.throwOnMissingStub(this); + } + + @override + double get dx => (super.noSuchMethod( + Invocation.getter(#dx), + returnValue: 0.0, + ) as double); + + @override + double get dy => (super.noSuchMethod( + Invocation.getter(#dy), + returnValue: 0.0, + ) as double); + + @override + double get distance => (super.noSuchMethod( + Invocation.getter(#distance), + returnValue: 0.0, + ) as double); + + @override + double get distanceSquared => (super.noSuchMethod( + Invocation.getter(#distanceSquared), + returnValue: 0.0, + ) as double); + + @override + double get direction => (super.noSuchMethod( + Invocation.getter(#direction), + returnValue: 0.0, + ) as double); + + @override + bool get isInfinite => (super.noSuchMethod( + Invocation.getter(#isInfinite), + returnValue: false, + ) as bool); + + @override + bool get isFinite => (super.noSuchMethod( + Invocation.getter(#isFinite), + returnValue: false, + ) as bool); + + @override + _i2.Offset scale( + double? scaleX, + double? scaleY, + ) => + (super.noSuchMethod( + Invocation.method( + #scale, + [ + scaleX, + scaleY, + ], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #scale, + [ + scaleX, + scaleY, + ], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset translate( + double? translateX, + double? translateY, + ) => + (super.noSuchMethod( + Invocation.method( + #translate, + [ + translateX, + translateY, + ], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #translate, + [ + translateX, + translateY, + ], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator -() => (super.noSuchMethod( + Invocation.method( + #-, + [], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #-, + [], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator -(_i2.Offset? other) => (super.noSuchMethod( + Invocation.method( + #-, + [other], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #-, + [other], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator +(_i2.Offset? other) => (super.noSuchMethod( + Invocation.method( + #+, + [other], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #+, + [other], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator *(double? operand) => (super.noSuchMethod( + Invocation.method( + #*, + [operand], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #*, + [operand], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator /(double? operand) => (super.noSuchMethod( + Invocation.method( + #/, + [operand], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #/, + [operand], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator ~/(double? operand) => (super.noSuchMethod( + Invocation.method( + #~/, + [operand], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #~/, + [operand], + ), + ), + ) as _i2.Offset); + + @override + _i2.Offset operator %(double? operand) => (super.noSuchMethod( + Invocation.method( + #%, + [operand], + ), + returnValue: _FakeOffset_3( + this, + Invocation.method( + #%, + [operand], + ), + ), + ) as _i2.Offset); + + @override + _i2.Rect operator &(_i2.Size? other) => (super.noSuchMethod( + Invocation.method( + #&, + [other], + ), + returnValue: _FakeRect_1( + this, + Invocation.method( + #&, + [other], + ), + ), + ) as _i2.Rect); + + @override + bool operator <(_i2.OffsetBase? other) => (super.noSuchMethod( + Invocation.method( + #<, + [other], + ), + returnValue: false, + ) as bool); + + @override + bool operator <=(_i2.OffsetBase? other) => (super.noSuchMethod( + Invocation.method( + #<=, + [other], + ), + returnValue: false, + ) as bool); + + @override + bool operator >(_i2.OffsetBase? other) => (super.noSuchMethod( + Invocation.method( + #>, + [other], + ), + returnValue: false, + ) as bool); + + @override + bool operator >=(_i2.OffsetBase? other) => (super.noSuchMethod( + Invocation.method( + #>=, + [other], + ), + returnValue: false, + ) as bool); +} + +/// A class which mocks [DrawPathCommand]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockDrawPathCommand extends _i1.Mock implements _i3.DrawPathCommand { + MockDrawPathCommand() { + _i1.throwOnMissingStub(this); + } + + @override + String get type => (super.noSuchMethod( + Invocation.getter(#type), + returnValue: '', + ) as String); + + @override + int get version => (super.noSuchMethod( + Invocation.getter(#version), + returnValue: 0, + ) as int); + + @override + _i3.PathWithActionHistory get path => (super.noSuchMethod( + Invocation.getter(#path), + returnValue: _FakePathWithActionHistory_4( + this, + Invocation.getter(#path), + ), + ) as _i3.PathWithActionHistory); + + @override + List get props => (super.noSuchMethod( + Invocation.getter(#props), + returnValue: [], + ) as List); + + @override + _i2.Paint get paint => (super.noSuchMethod( + Invocation.getter(#paint), + returnValue: _FakePaint_5( + this, + Invocation.getter(#paint), + ), + ) as _i2.Paint); + + @override + void call(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #call, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + Map toJson() => (super.noSuchMethod( + Invocation.method( + #toJson, + [], + ), + returnValue: {}, + ) as Map); +} + +/// A class which mocks [CommandManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandManager extends _i1.Mock implements _i3.CommandManager { + MockCommandManager() { + _i1.throwOnMissingStub(this); + } + + @override + Iterable<_i3.Command> get history => (super.noSuchMethod( + Invocation.getter(#history), + returnValue: <_i3.Command>[], + ) as Iterable<_i3.Command>); + + @override + int get count => (super.noSuchMethod( + Invocation.getter(#count), + returnValue: 0, + ) as int); + + @override + void addGraphicCommand(_i3.GraphicCommand? command) => super.noSuchMethod( + Invocation.method( + #addGraphicCommand, + [command], + ), + returnValueForMissingStub: null, + ); + + @override + void executeLastCommand(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeLastCommand, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void executeAllCommands(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeAllCommands, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void discardLastCommand() => super.noSuchMethod( + Invocation.method( + #discardLastCommand, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void clearHistory({Iterable<_i3.Command>? newCommands}) => super.noSuchMethod( + Invocation.method( + #clearHistory, + [], + {#newCommands: newCommands}, + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [CommandFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandFactory extends _i1.Mock implements _i3.CommandFactory { + MockCommandFactory() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.DrawPathCommand createDrawPathCommand( + _i3.PathWithActionHistory? path, + _i2.Paint? paint, + ) => + (super.noSuchMethod( + Invocation.method( + #createDrawPathCommand, + [ + path, + paint, + ], + ), + returnValue: _FakeDrawPathCommand_6( + this, + Invocation.method( + #createDrawPathCommand, + [ + path, + paint, + ], + ), + ), + ) as _i3.DrawPathCommand); +} + +/// A class which mocks [GraphicFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockGraphicFactory extends _i1.Mock implements _i3.GraphicFactory { + MockGraphicFactory() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.Paint createPaint() => (super.noSuchMethod( + Invocation.method( + #createPaint, + [], + ), + returnValue: _FakePaint_5( + this, + Invocation.method( + #createPaint, + [], + ), + ), + ) as _i2.Paint); + + @override + _i3.PathWithActionHistory createPathWithActionHistory() => + (super.noSuchMethod( + Invocation.method( + #createPathWithActionHistory, + [], + ), + returnValue: _FakePathWithActionHistory_4( + this, + Invocation.method( + #createPathWithActionHistory, + [], + ), + ), + ) as _i3.PathWithActionHistory); + + @override + _i2.PictureRecorder createPictureRecorder() => (super.noSuchMethod( + Invocation.method( + #createPictureRecorder, + [], + ), + returnValue: _FakePictureRecorder_7( + this, + Invocation.method( + #createPictureRecorder, + [], + ), + ), + ) as _i2.PictureRecorder); + + @override + _i2.Canvas createCanvasWithRecorder(_i2.PictureRecorder? recorder) => + (super.noSuchMethod( + Invocation.method( + #createCanvasWithRecorder, + [recorder], + ), + returnValue: _FakeCanvas_8( + this, + Invocation.method( + #createCanvasWithRecorder, + [recorder], + ), + ), + ) as _i2.Canvas); + + @override + _i2.Paint copyPaint(_i2.Paint? original) => (super.noSuchMethod( + Invocation.method( + #copyPaint, + [original], + ), + returnValue: _FakePaint_5( + this, + Invocation.method( + #copyPaint, + [original], + ), + ), + ) as _i2.Paint); +} diff --git a/test/unit/tool/hand_tool_test.dart b/packages/tools/test/unit/hand_tool_test.dart similarity index 88% rename from test/unit/tool/hand_tool_test.dart rename to packages/tools/test/unit/hand_tool_test.dart index c6caa5b3..8bb28a7b 100644 --- a/test/unit/tool/hand_tool_test.dart +++ b/packages/tools/test/unit/hand_tool_test.dart @@ -1,12 +1,10 @@ import 'dart:ui'; +import 'package:command/command.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:paintroid/command/src/command_factory.dart'; -import 'package:paintroid/command/src/command_manager.dart'; -import 'package:paintroid/tool/src/hand_tool/hand_tool.dart'; -import 'package:paintroid/tool/src/tool_types.dart'; +import 'package:tools/tools.dart'; import 'hand_tool_test.mocks.dart'; diff --git a/packages/tools/test/unit/hand_tool_test.mocks.dart b/packages/tools/test/unit/hand_tool_test.mocks.dart new file mode 100644 index 00000000..7fc51b53 --- /dev/null +++ b/packages/tools/test/unit/hand_tool_test.mocks.dart @@ -0,0 +1,340 @@ +// Mocks generated by Mockito 5.4.2 from annotations +// in tools/test/unit/hand_tool_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:ui' as _i2; + +import 'package:command/command.dart' as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeColor_0 extends _i1.SmartFake implements _i2.Color { + _FakeColor_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeDrawPathCommand_1 extends _i1.SmartFake + implements _i3.DrawPathCommand { + _FakeDrawPathCommand_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [Paint]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockPaint extends _i1.Mock implements _i2.Paint { + MockPaint() { + _i1.throwOnMissingStub(this); + } + + @override + bool get isAntiAlias => (super.noSuchMethod( + Invocation.getter(#isAntiAlias), + returnValue: false, + ) as bool); + + @override + set isAntiAlias(bool? value) => super.noSuchMethod( + Invocation.setter( + #isAntiAlias, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.Color get color => (super.noSuchMethod( + Invocation.getter(#color), + returnValue: _FakeColor_0( + this, + Invocation.getter(#color), + ), + ) as _i2.Color); + + @override + set color(_i2.Color? value) => super.noSuchMethod( + Invocation.setter( + #color, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.BlendMode get blendMode => (super.noSuchMethod( + Invocation.getter(#blendMode), + returnValue: _i2.BlendMode.clear, + ) as _i2.BlendMode); + + @override + set blendMode(_i2.BlendMode? value) => super.noSuchMethod( + Invocation.setter( + #blendMode, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.PaintingStyle get style => (super.noSuchMethod( + Invocation.getter(#style), + returnValue: _i2.PaintingStyle.fill, + ) as _i2.PaintingStyle); + + @override + set style(_i2.PaintingStyle? value) => super.noSuchMethod( + Invocation.setter( + #style, + value, + ), + returnValueForMissingStub: null, + ); + + @override + double get strokeWidth => (super.noSuchMethod( + Invocation.getter(#strokeWidth), + returnValue: 0.0, + ) as double); + + @override + set strokeWidth(double? value) => super.noSuchMethod( + Invocation.setter( + #strokeWidth, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.StrokeCap get strokeCap => (super.noSuchMethod( + Invocation.getter(#strokeCap), + returnValue: _i2.StrokeCap.butt, + ) as _i2.StrokeCap); + + @override + set strokeCap(_i2.StrokeCap? value) => super.noSuchMethod( + Invocation.setter( + #strokeCap, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.StrokeJoin get strokeJoin => (super.noSuchMethod( + Invocation.getter(#strokeJoin), + returnValue: _i2.StrokeJoin.miter, + ) as _i2.StrokeJoin); + + @override + set strokeJoin(_i2.StrokeJoin? value) => super.noSuchMethod( + Invocation.setter( + #strokeJoin, + value, + ), + returnValueForMissingStub: null, + ); + + @override + double get strokeMiterLimit => (super.noSuchMethod( + Invocation.getter(#strokeMiterLimit), + returnValue: 0.0, + ) as double); + + @override + set strokeMiterLimit(double? value) => super.noSuchMethod( + Invocation.setter( + #strokeMiterLimit, + value, + ), + returnValueForMissingStub: null, + ); + + @override + set maskFilter(_i2.MaskFilter? value) => super.noSuchMethod( + Invocation.setter( + #maskFilter, + value, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.FilterQuality get filterQuality => (super.noSuchMethod( + Invocation.getter(#filterQuality), + returnValue: _i2.FilterQuality.none, + ) as _i2.FilterQuality); + + @override + set filterQuality(_i2.FilterQuality? value) => super.noSuchMethod( + Invocation.setter( + #filterQuality, + value, + ), + returnValueForMissingStub: null, + ); + + @override + set shader(_i2.Shader? value) => super.noSuchMethod( + Invocation.setter( + #shader, + value, + ), + returnValueForMissingStub: null, + ); + + @override + set colorFilter(_i2.ColorFilter? value) => super.noSuchMethod( + Invocation.setter( + #colorFilter, + value, + ), + returnValueForMissingStub: null, + ); + + @override + set imageFilter(_i2.ImageFilter? value) => super.noSuchMethod( + Invocation.setter( + #imageFilter, + value, + ), + returnValueForMissingStub: null, + ); + + @override + bool get invertColors => (super.noSuchMethod( + Invocation.getter(#invertColors), + returnValue: false, + ) as bool); + + @override + set invertColors(bool? value) => super.noSuchMethod( + Invocation.setter( + #invertColors, + value, + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [CommandManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandManager extends _i1.Mock implements _i3.CommandManager { + MockCommandManager() { + _i1.throwOnMissingStub(this); + } + + @override + Iterable<_i3.Command> get history => (super.noSuchMethod( + Invocation.getter(#history), + returnValue: <_i3.Command>[], + ) as Iterable<_i3.Command>); + + @override + int get count => (super.noSuchMethod( + Invocation.getter(#count), + returnValue: 0, + ) as int); + + @override + void addGraphicCommand(_i3.GraphicCommand? command) => super.noSuchMethod( + Invocation.method( + #addGraphicCommand, + [command], + ), + returnValueForMissingStub: null, + ); + + @override + void executeLastCommand(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeLastCommand, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void executeAllCommands(_i2.Canvas? canvas) => super.noSuchMethod( + Invocation.method( + #executeAllCommands, + [canvas], + ), + returnValueForMissingStub: null, + ); + + @override + void discardLastCommand() => super.noSuchMethod( + Invocation.method( + #discardLastCommand, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void clearHistory({Iterable<_i3.Command>? newCommands}) => super.noSuchMethod( + Invocation.method( + #clearHistory, + [], + {#newCommands: newCommands}, + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [CommandFactory]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockCommandFactory extends _i1.Mock implements _i3.CommandFactory { + MockCommandFactory() { + _i1.throwOnMissingStub(this); + } + + @override + _i3.DrawPathCommand createDrawPathCommand( + _i3.PathWithActionHistory? path, + _i2.Paint? paint, + ) => + (super.noSuchMethod( + Invocation.method( + #createDrawPathCommand, + [ + path, + paint, + ], + ), + returnValue: _FakeDrawPathCommand_1( + this, + Invocation.method( + #createDrawPathCommand, + [ + path, + paint, + ], + ), + ), + ) as _i3.DrawPathCommand); +} diff --git a/pubspec.lock b/pubspec.lock index b9ed3f0d..64341a9c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,22 +25,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.11.2" + ansi_styles: + dependency: transitive + description: + name: ansi_styles + sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" + url: "https://pub.dev" + source: hosted + version: "0.3.2+1" archive: dependency: transitive description: name: archive - sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.3.2" + version: "3.4.9" args: dependency: transitive description: name: args - sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" async: dependency: transitive description: @@ -61,10 +69,10 @@ packages: dependency: transitive description: name: build - sha256: "3fbda25365741f8251b39f3917fb3c8e286a96fd068a5a242e11c2012d495777" + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.4.1" build_config: dependency: transitive description: @@ -77,34 +85,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95 + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.7" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "0671ad4162ed510b70d0eb4ad6354c249f8429cab4ae7a4cec86bbc2886eb76e" + sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 url: "https://pub.dev" source: hosted - version: "7.2.7+1" + version: "7.2.11" built_collection: dependency: transitive description: @@ -117,10 +125,10 @@ packages: dependency: transitive description: name: built_value - sha256: "7dd62d9faf105c434f3d829bbe9c4be02ec67f5ed94832222116122df67c5452" + sha256: c9aabae0718ec394e5bc3c7272e6bb0dc0b32201a08fe185ec1d8401d3e39309 url: "https://pub.dev" source: hosted - version: "8.6.0" + version: "8.8.1" characters: dependency: transitive description: @@ -153,14 +161,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + cli_launcher: + dependency: transitive + description: + name: cli_launcher + sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" + url: "https://pub.dev" + source: hosted + version: "0.3.1" cli_util: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -173,18 +189,40 @@ packages: dependency: transitive description: name: code_builder - sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe" + sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84 url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "4.9.0" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + command: + dependency: "direct main" + description: + path: "packages/command" + relative: true + source: path + version: "0.0.1" + component_library: + dependency: "direct main" + description: + path: "packages/component_library" + relative: true + source: path + version: "0.0.1" + conventional_commit: + dependency: transitive + description: + name: conventional_commit + sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "0.6.0+1" convert: dependency: transitive description: @@ -197,18 +235,18 @@ packages: dependency: transitive description: name: cross_file - sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" + sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" url: "https://pub.dev" source: hosted - version: "0.3.3+4" + version: "0.3.3+7" crypto: dependency: transitive description: name: crypto - sha256: aa274aa7774f8964e4f4f38cc994db7b6158dd36e9187aaceaddc994b35c6c67 + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" custom_lint: dependency: transitive description: @@ -237,18 +275,25 @@ packages: dependency: transitive description: name: dart_style - sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" + database: + dependency: transitive + description: + path: "packages/database" + relative: true + source: path + version: "0.0.1" device_info_plus: - dependency: "direct main" + dependency: transitive description: name: device_info_plus - sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659" + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" url: "https://pub.dev" source: hosted - version: "9.0.3" + version: "9.1.1" device_info_plus_platform_interface: dependency: transitive description: @@ -258,7 +303,7 @@ packages: source: hosted version: "7.0.0" equatable: - dependency: "direct main" + dependency: transitive description: name: equatable sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 @@ -277,10 +322,10 @@ packages: dependency: transitive description: name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.0" file: dependency: transitive description: @@ -290,15 +335,47 @@ packages: source: hosted version: "6.1.4" file_picker: - dependency: "direct main" + dependency: transitive description: name: file_picker - sha256: "9d6e95ec73abbd31ec54d0e0df8a961017e165aba1395e462e5b31ea0c165daf" + sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030 + url: "https://pub.dev" + source: hosted + version: "5.5.0" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.dev" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 + url: "https://pub.dev" + source: hosted + version: "0.9.3+3" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: "0aa47a725c346825a2bd396343ce63ac00bda6eff2fbc43eabe99737dede8262" + url: "https://pub.dev" + source: hosted + version: "2.6.1" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0 url: "https://pub.dev" source: hosted - version: "5.3.1" + version: "0.9.3+1" filesize: - dependency: "direct main" + dependency: transitive description: name: filesize sha256: f53df1f27ff60e466eefcd9df239e02d4722d5e2debee92a87dfd99ac66de2af @@ -314,7 +391,7 @@ packages: source: hosted version: "1.1.0" floor: - dependency: "direct main" + dependency: transitive description: name: floor sha256: "52a8eac2c8d274e7c0c54251226f59786bb5b749365a2d8537d8095aa5132d92" @@ -359,20 +436,20 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" flutter_localization: dependency: "direct main" description: name: flutter_localization - sha256: "9afed0ae301ee3ca547bceb5132b50d0d2d539ad4da4131ea7fc89e3bdeb67d3" + sha256: a65ae241b865f2780f2ef624a4cdd019ca894d0b7df0a0b77190746ef0b9c8fa url: "https://pub.dev" source: hosted - version: "0.1.12" + version: "0.1.14" flutter_localizations: - dependency: transitive + dependency: "direct main" description: flutter source: sdk version: "0.0.0" @@ -380,10 +457,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.17" flutter_riverpod: dependency: "direct main" description: @@ -393,7 +470,7 @@ packages: source: hosted version: "2.4.9" flutter_svg: - dependency: "direct main" + dependency: transitive description: name: flutter_svg sha256: "6ff9fa12892ae074092de2fa6a9938fb21dbabfdaa2ff57dc697ff912fc8d4b2" @@ -467,10 +544,10 @@ packages: dependency: transitive description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -488,7 +565,7 @@ packages: source: hosted version: "4.0.2" image: - dependency: "direct main" + dependency: transitive description: name: image sha256: "8e9d133755c3e84c73288363e6343157c383a0c6c56fc51afcc5d4d7180306d6" @@ -496,45 +573,69 @@ packages: source: hosted version: "3.3.0" image_picker: - dependency: "direct main" + dependency: transitive description: name: image_picker - sha256: "9978d3510af4e6a902e545ce19229b926e6de6a1828d6134d3aab2e129a4d270" + sha256: b6951e25b795d053a6ba03af5f710069c99349de9341af95155d52665cb4607c url: "https://pub.dev" source: hosted - version: "0.8.7+5" + version: "0.8.9" image_picker_android: dependency: transitive description: name: image_picker_android - sha256: c2f3c66400649bd132f721c88218945d6406f693092b2f741b79ae9cdb046e59 + sha256: ecdc963d2aa67af5195e723a40580f802d4392e31457a12a562b3e2bd6a396fe url: "https://pub.dev" source: hosted - version: "0.8.6+16" + version: "0.8.9+1" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: "98f50d6b9f294c8ba35e25cc0d13b04bfddd25dbc8d32fa9d566a6572f2c081c" + sha256: "869fe8a64771b7afbc99fc433a5f7be2fea4d1cb3d7c11a48b6b579eb9c797f0" url: "https://pub.dev" source: hosted - version: "2.1.12" + version: "2.2.0" image_picker_ios: dependency: transitive description: name: image_picker_ios - sha256: d779210bda268a03b57e923fb1e410f32f5c5e708ad256348bcbf1f44f558fd0 + sha256: eac0a62104fa12feed213596df0321f57ce5a572562f72a68c4ff81e9e4caacf url: "https://pub.dev" source: hosted - version: "0.8.7+4" + version: "0.8.9" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.dev" + source: hosted + version: "0.2.1+1" image_picker_platform_interface: dependency: transitive description: name: image_picker_platform_interface - sha256: "1991219d9dbc42a99aff77e663af8ca51ced592cd6685c9485e3458302d3d4f8" + sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514 + url: "https://pub.dev" + source: hosted + version: "2.9.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" url: "https://pub.dev" source: hosted - version: "2.6.3" + version: "0.2.1+1" integration_test: dependency: "direct dev" description: flutter @@ -556,6 +657,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + io_library: + dependency: "direct main" + description: + path: "packages/io_library" + relative: true + source: path + version: "0.0.1" js: dependency: transitive description: @@ -572,14 +680,36 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + l10n: + dependency: "direct main" + description: + path: "packages/l10n" + relative: true + source: path + version: "0.0.1" + landing_page_screen: + dependency: "direct main" + description: + path: "packages/features/landing_page_screen" + relative: true + source: path + version: "0.0.1" + launch_review: + dependency: transitive + description: + name: launch_review + sha256: "04cdaf752033cefd53bc0fa9c22105801ef53791a93d8b6cdd00fcb3c1c1604b" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.1.1" lists: dependency: transitive description: @@ -612,14 +742,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + melos: + dependency: "direct main" + description: + name: melos + sha256: "96e64bbade5712c3f010137e195bca9f1b351fac34ab1f322af492ae34032067" + url: "https://pub.dev" + source: hosted + version: "3.4.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: @@ -632,12 +770,27 @@ packages: dependency: "direct dev" description: name: mockito - sha256: "8b46d7eb40abdda92d62edd01546051f0c27365e65608c284de336dccfef88cc" + sha256: "7d5b53bcd556c1bc7ffbe4e4d5a19c3e112b7e925e9e172dd7c6ad0630812616" url: "https://pub.dev" source: hosted - version: "5.4.1" - oxidized: + version: "5.4.2" + mustache_template: + dependency: transitive + description: + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c + url: "https://pub.dev" + source: hosted + version: "2.0.0" + onboarding_screen: dependency: "direct main" + description: + path: "packages/features/onboarding_screen" + relative: true + source: path + version: "0.0.1" + oxidized: + dependency: transitive description: name: oxidized sha256: "2058b9818815546124f6d0fee813483b571ca133b4db6f63c9628897f191e6b5" @@ -653,13 +806,13 @@ packages: source: hosted version: "2.1.0" package_info_plus: - dependency: "direct main" + dependency: transitive description: name: package_info_plus - sha256: "28386bbe89ab5a7919a47cea99cdd1128e5a6e0bbd7eaafe20440ead84a15de3" + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.2.0" package_info_plus_platform_interface: dependency: transitive description: @@ -669,7 +822,7 @@ packages: source: hosted version: "2.0.1" path: - dependency: "direct main" + dependency: transitive description: name: path sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" @@ -693,117 +846,125 @@ packages: source: hosted version: "1.0.1" path_provider: - dependency: "direct main" + dependency: transitive description: name: path_provider - sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec" + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.0.6" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6 + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.2.1" permission_handler: - dependency: "direct main" + dependency: transitive description: name: permission_handler - sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5 url: "https://pub.dev" source: hosted - version: "10.2.0" + version: "10.4.5" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: d8cc6a62ded6d0f49c6eac337e080b066ee3bce4d405bd9439a61e1f1927bfe8 + sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47" url: "https://pub.dev" source: hosted - version: "10.2.1" + version: "10.3.6" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: ee96ac32f5a8e6f80756e25b25b9f8e535816c8e6665a96b6d70681f8c4f7e85 + sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" url: "https://pub.dev" source: hosted - version: "9.0.8" + version: "9.1.4" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" url: "https://pub.dev" source: hosted - version: "3.9.0" + version: "3.12.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.3" petitparser: dependency: transitive description: name: petitparser - sha256: "49392a45ced973e8d94a85fdb21293fbb40ba805fc49f2965101ae748a3683b4" + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.4.0" platform: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc" + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.7" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" pool: dependency: transitive description: @@ -820,8 +981,16 @@ packages: url: "https://pub.dev" source: hosted version: "4.2.4" + prompts: + dependency: transitive + description: + name: prompts + sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" + url: "https://pub.dev" + source: hosted + version: "2.0.0" protobuf: - dependency: "direct main" + dependency: transitive description: name: protobuf sha256: "01dd9bd0fa02548bf2ceee13545d4a0ec6046459d847b6b061d8a27237108a08" @@ -836,6 +1005,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + pub_updater: + dependency: transitive + description: + name: pub_updater + sha256: b06600619c8c219065a548f8f7c192b3e080beff95488ed692780f48f69c0625 + url: "https://pub.dev" + source: hosted + version: "0.3.1" + pubspec: + dependency: transitive + description: + name: pubspec + sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e + url: "https://pub.dev" + source: hosted + version: "2.3.0" pubspec_parse: dependency: transitive description: @@ -844,6 +1029,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.3" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" riverpod: dependency: "direct dev" description: @@ -864,26 +1057,26 @@ packages: dependency: "direct main" description: name: riverpod_annotation - sha256: cedd6a54b6f5764ffd5c05df57b6676bfc8c01978e14ee60a2c16891038820fe + sha256: b70e95fbd5ca7ce42f5148092022971bb2e9843b6ab71e97d479e8ab52e98979 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.3.3" riverpod_generator: dependency: "direct dev" description: name: riverpod_generator - sha256: cd0595de57ccf5d944ff4b0f68289e11ac6a2eff1e3dfd1d884a43f6f3bcee5e + sha256: "691180275664a5420c87d72c1ed26ef8404d32b823807540172bfd1660425376" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.4" riverpod_lint: dependency: "direct dev" description: name: riverpod_lint - sha256: "043ff016011be4c5887b3239bfbca05d284bdb68db0a5363cee0242b7567e250" + sha256: "17ad319914ac6863c64524e598913c0f17e30688aca8f5b7509e96d6e372d493" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" rxdart: dependency: transitive description: @@ -896,58 +1089,58 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022" + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.2.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749" + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.1" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa" + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173" + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" shelf: dependency: transitive description: @@ -970,7 +1163,7 @@ packages: source: sdk version: "0.0.99" smooth_page_indicator: - dependency: "direct main" + dependency: transitive description: name: smooth_page_indicator sha256: "725bc638d5e79df0c84658e1291449996943f93bacbc2cec49963dbbab48d8ae" @@ -981,10 +1174,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.5.0" source_span: dependency: transitive description: @@ -994,37 +1187,37 @@ packages: source: hosted version: "1.10.0" sqflite: - dependency: "direct main" + dependency: transitive description: name: sqflite - sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9 + sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" url: "https://pub.dev" source: hosted - version: "2.2.8+4" + version: "2.3.0" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555 + sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.5.0+2" sqflite_common_ffi: dependency: transitive description: name: sqflite_common_ffi - sha256: f86de82d37403af491b21920a696b19f01465b596f545d1acd4d29a0a72418ad + sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f" url: "https://pub.dev" source: hosted - version: "2.2.5" + version: "2.3.0+4" sqlite3: dependency: transitive description: name: sqlite3 - sha256: "2cef47b59d310e56f8275b13734ee80a9cf4a48a43172020cb55a620121fbf66" + sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "2.1.0" sqlparser: dependency: transitive description: @@ -1037,26 +1230,26 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" state_notifier: dependency: transitive description: name: state_notifier - sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289" + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb url: "https://pub.dev" source: hosted - version: "0.7.2+1" + version: "1.0.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1093,10 +1286,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -1109,10 +1302,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" timing: dependency: transitive description: @@ -1122,21 +1315,28 @@ packages: source: hosted version: "1.0.1" toast: - dependency: "direct main" + dependency: transitive description: name: toast sha256: "12433091e3e5a25b3a25f670126e42547c9ade135de30ad9ace45d1ddccd57c9" url: "https://pub.dev" source: hosted version: "0.3.0" + tools: + dependency: "direct main" + description: + path: "packages/tools" + relative: true + source: path + version: "0.0.1" typed_data: dependency: transitive description: name: typed_data - sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" unicode: dependency: transitive description: @@ -1145,14 +1345,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + uri: + dependency: transitive + description: + name: uri + sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" + url: "https://pub.dev" + source: hosted + version: "1.0.0" url_launcher: - dependency: "direct main" + dependency: transitive description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.1.14" url_launcher_android: dependency: transitive description: @@ -1197,10 +1405,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.0.19" url_launcher_windows: dependency: transitive description: @@ -1229,26 +1437,26 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" watcher: dependency: transitive description: name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.1.0" web: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1269,34 +1477,41 @@ packages: dependency: transitive description: name: win32 - sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "5.0.9" win32_registry: dependency: transitive description: name: win32_registry - sha256: "1c52f994bdccb77103a6231ad4ea331a244dbcef5d1f37d8462f713143b0bfae" + sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" + workspace_screen: + dependency: "direct main" + description: + path: "packages/features/workspace_screen" + relative: true + source: path + version: "0.0.1" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" url: "https://pub.dev" source: hosted - version: "1.0.0" + version: "1.0.3" xml: dependency: transitive description: name: xml - sha256: "979ee37d622dec6365e2efa4d906c37470995871fe9ae080d967e192d88286b5" + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" yaml: dependency: transitive description: @@ -1305,6 +1520,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: "1579d4a0340a83cf9e4d580ea51a16329c916973bffd5bd4b45e911b25d46bfd" + url: "https://pub.dev" + source: hosted + version: "2.1.1" sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.2.0-194.0.dev <4.0.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index d6b6f555..5a441042 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: paintroid description: The standard image manipulation app for Catroid. -publish_to: 'none' +publish_to: "none" version: 1.0.0+1 @@ -11,53 +11,54 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter + flutter_localization: ^0.1.12 intl: ^0.18.0 - equatable: ^2.0.3 - flutter_svg: ^1.1.0 - image: ^3.2.0 - protobuf: ^2.1.0 - path: ^1.8.1 - path_provider: ^2.0.11 + logging: ^1.0.2 flutter_riverpod: ^2.3.6 riverpod_annotation: ^2.1.1 - freezed_annotation: ^2.4.1 - image_picker: ^0.8.5+3 - oxidized: ^5.2.0 - logging: ^1.0.2 - file_picker: ^5.3.1 - floor: ^1.2.0 - filesize: ^2.0.1 - toast: ^0.3.0 - sqflite: - permission_handler: ^10.0.0 - url_launcher: ^6.2.2 - package_info_plus: ^4.0.1 - device_info_plus: ^9.0.3 - smooth_page_indicator: ^1.0.0+2 shared_preferences: ^2.0.15 + freezed_annotation: ^2.4.1 + melos: ^3.4.0 + + # Internal packages: + component_library: + path: ./packages/component_library + l10n: + path: ./packages/l10n + command: + path: ./packages/command + io_library: + path: ./packages/io_library + tools: + path: ./packages/tools + workspace_screen: + path: ./packages/features/workspace_screen + landing_page_screen: + path: ./packages/features/landing_page_screen + onboarding_screen: + path: ./packages/features/onboarding_screen dev_dependencies: flutter_test: sdk: flutter integration_test: sdk: flutter + mockito: ^5.2.0 - build_runner: ^2.2.0 flutter_launcher_icons: ^0.9.3 flutter_lints: ^2.0.1 floor_generator: ^1.2.0 riverpod_generator: ^2.2.3 riverpod_lint: ^1.3.2 + build_runner: ^2.2.0 freezed: ^2.4.1 riverpod: ^2.3.7 flutter: - generate: true uses-material-design: true assets: - assets/icon/ - - assets/svg/ - - assets/img/ - - test/fixture/image/ diff --git a/setup_melos.sh b/setup_melos.sh new file mode 100755 index 00000000..10cfa6e3 --- /dev/null +++ b/setup_melos.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Check if the 'melos' command is available +if ! command -v melos &> /dev/null; then + echo "Melos is not installed. Installing Melos globally using Dart." + make melos + + if [ $? -eq 0 ]; then + echo "Successfully installed Melos." + else + echo "Failed to install Melos." + exit 1 + fi +fi \ No newline at end of file diff --git a/setup_sdk.sh b/setup_sdk.sh new file mode 100755 index 00000000..680ef818 --- /dev/null +++ b/setup_sdk.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +MELOS_CONFIG="melos.yaml" +MAKEFILE="Makefile" + +# Try running 'fvm flutter pub get' to check if FVM is properly configured +if fvm flutter pub get; then + echo "FVM Flutter SDK is properly configured." + + # Use sed to set the sdkPath to '.fvm/flutter_sdk' in melos.yaml + sed -i.bak 's|sdkPath: .*|sdkPath: .fvm/flutter_sdk|' "$MELOS_CONFIG" && rm "${MELOS_CONFIG}.bak" + + if [ $? -eq 0 ]; then + echo " -> Successfully set sdkPath to .fvm/flutter_sdk in melos.yaml." + else + echo " -> Failed to set sdkPath in melos.yaml." + exit 1 + fi + + # Use sed to set the FLUTTER variable to 'fvm flutter' in Makefile + sed -i.bak 's|FLUTTER := .*|FLUTTER := fvm flutter|' "$MAKEFILE" && rm "${MAKEFILE}.bak" + + if [ $? -eq 0 ]; then + echo " -> Successfully set FLUTTER to 'fvm flutter' in Makefile." + else + echo " -> Failed to set FLUTTER in Makefile." + exit 1 + fi + + sed -i.bak 's|DART := .*|DART := fvm dart|' "$MAKEFILE" && rm "${MAKEFILE}.bak" + + if [ $? -eq 0 ]; then + echo " -> Successfully set DART to 'fvm dart' in Makefile." + else + echo " -> Failed to set DART in Makefile." + exit 1 + fi + +else + echo "FVM Flutter SDK not properly configured." + + # Use sed to replace the sdkPath with 'auto' in melos.yaml + sed -i.bak 's|sdkPath: .*|sdkPath: auto|' "$MELOS_CONFIG" && rm "${MELOS_CONFIG}.bak" + + if [ $? -eq 0 ]; then + echo " -> Successfully updated sdkPath to auto in melos.yaml." + else + echo " -> Failed to update sdkPath in melos.yaml." + exit 1 + fi + + # Use sed to replace the FLUTTER with 'flutter' in Makefile + sed -i.bak 's|FLUTTER := .*|FLUTTER := flutter|' "$MAKEFILE" && rm "${MAKEFILE}.bak" + + if [ $? -eq 0 ]; then + echo " -> Successfully updated FLUTTER to 'flutter' in Makefile." + else + echo " -> Failed to update FLUTTER in Makefile." + exit 1 + fi + + sed -i.bak 's|DART := .*|DART := dart|' "$MAKEFILE" && rm "${MAKEFILE}.bak" + + if [ $? -eq 0 ]; then + echo " -> Successfully updated DART to 'dart' in Makefile." + else + echo " -> Failed to update DART in Makefile." + exit 1 + fi + + flutter pub get +fi diff --git a/test/unit/io/serialization/paint_serializer_test.dart b/test/unit/io/serialization/paint_serializer_test.dart deleted file mode 100644 index bd56f871..00000000 --- a/test/unit/io/serialization/paint_serializer_test.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:paintroid/core/graphic_factory.dart'; -import 'package:paintroid/io/serialization.dart'; - -void main() { - final mockGraphicFactory = MockGraphicFactory(); - final paintSerializer = PaintSerializer(1, mockGraphicFactory); - - group('PaintSerializer', () { - test( - 'serialize and deserialize with a Paint object having all default values', - () async { - final paint = Paint(); - - final serialized = - await paintSerializer.serializeWithLatestVersion(paint); - final deserialized = - await paintSerializer.deserializeWithLatestVersion(serialized); - - expect(deserialized.color, Colors.black); - expect(deserialized.strokeWidth, 0.0); - expect(deserialized.blendMode, BlendMode.srcOver); - expect(deserialized.strokeCap, StrokeCap.butt); - expect(deserialized.strokeJoin, StrokeJoin.miter); - expect(deserialized.style, PaintingStyle.fill); - }); - - test('serialize and deserialize with different values', () async { - final paint = Paint() - ..blendMode = BlendMode.srcOver - ..color = Colors.red - ..strokeCap = StrokeCap.round - ..strokeJoin = StrokeJoin.round - ..strokeWidth = 25.0 - ..style = PaintingStyle.stroke; - - final serialized = - await paintSerializer.serializeWithLatestVersion(paint); - final deserialized = - await paintSerializer.deserializeWithLatestVersion(serialized); - - expect(deserialized.blendMode, paint.blendMode); - expect(deserialized.color, paint.color); - expect(deserialized.strokeCap, paint.strokeCap); - expect(deserialized.strokeJoin, paint.strokeJoin); - expect(deserialized.strokeWidth, paint.strokeWidth); - expect(deserialized.style, paint.style); - }); - }); -} - -class MockGraphicFactory extends GraphicFactory {} diff --git a/test/widget/ui/bottom_control_navigation_bar_test.dart b/test/widget/ui/bottom_control_navigation_bar_test.dart deleted file mode 100644 index 8b80c7dc..00000000 --- a/test/widget/ui/bottom_control_navigation_bar_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:paintroid/tool/tool.dart'; -import 'package:paintroid/ui/pocket_paint.dart'; - -import '../utils/utils.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - testWidgets( - 'Select eraser and brush tool and verify NavigationBarItem changes', - (WidgetTester tester) async { - const eraserToolData = ToolData.ERASER; - const brushToolData = ToolData.BRUSH; - - await tester.pumpWidget( - const ProviderScope( - child: MaterialApp( - home: PocketPaint(), - ), - ), - ); - - final bottomNavBarInteractions = BottomNavBarInteractions(tester); - await bottomNavBarInteractions - .selectTool(eraserToolData) - .then((_) => _.checkActiveToolIconAndLabel(eraserToolData)); - - await bottomNavBarInteractions - .selectTool(brushToolData) - .then((_) => _.checkActiveToolIconAndLabel(brushToolData)); - }); -} diff --git a/test/widget/utils/utils.dart b/test/widget/utils/utils.dart deleted file mode 100644 index 7a4db9b7..00000000 --- a/test/widget/utils/utils.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'interactions/bottom_nav_bar_interactions.dart'; -export 'interactions/canvas_interactions.dart'; From 7918a286529e9c0dc835e5bbb2772abc97d368c0 Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Fri, 23 Feb 2024 23:42:47 +0530 Subject: [PATCH 4/7] remove package --- packages/features/landing_page_screen/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/features/landing_page_screen/pubspec.yaml b/packages/features/landing_page_screen/pubspec.yaml index 836bb160..f78cd309 100644 --- a/packages/features/landing_page_screen/pubspec.yaml +++ b/packages/features/landing_page_screen/pubspec.yaml @@ -19,7 +19,6 @@ dependencies: toast: ^0.3.0 oxidized: ^5.2.0 flutter_svg: ^1.1.0 - launch_review: ^3.0.1 package_info_plus: ^4.0.1 filesize: ^2.0.1 url_launcher: ^6.1.5 From 7e2e4002d28ce39a75f390929c6ec38818fcbe0c Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Fri, 23 Feb 2024 23:47:03 +0530 Subject: [PATCH 5/7] revert app_localizations file --- packages/l10n/lib/src/l10n/app_localizations.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/l10n/lib/src/l10n/app_localizations.dart b/packages/l10n/lib/src/l10n/app_localizations.dart index 80afd2f0..7825f1ae 100644 --- a/packages/l10n/lib/src/l10n/app_localizations.dart +++ b/packages/l10n/lib/src/l10n/app_localizations.dart @@ -176,5 +176,4 @@ AppLocalizations lookupAppLocalizations(Locale locale) { 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' 'that was used.'); - -} +} \ No newline at end of file From 28d2d723d533a364696f2a9ecc0ba4356072bf4a Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Tue, 5 Mar 2024 00:42:43 +0530 Subject: [PATCH 6/7] address requested changes --- .../src/components/main_overflow_menu.dart | 28 +++++---- pubspec.lock | 62 +++++++------------ 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart b/packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart index ff2ca061..af121654 100644 --- a/packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart +++ b/packages/features/landing_page_screen/lib/src/components/main_overflow_menu.dart @@ -42,23 +42,27 @@ class _MainOverFlowMenuState extends ConsumerState { ); } + void _openStore() { + if (Platform.isAndroid || Platform.isIOS) { + final appId = Platform.isAndroid ? androidAppId : iOSAppId; + final url = Uri.parse( + Platform.isAndroid + ? 'market://details?id=$appId' + : 'https://apps.apple.com/at/app/pocket-code/id1117935892', + ); + launchUrl( + url, + mode: LaunchMode.externalApplication, + ); + } + } + Future _handleSelectedOption(MainOverflowMenuOption option) async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); String version = packageInfo.version; switch (option) { case MainOverflowMenuOption.rate: - if (Platform.isAndroid || Platform.isIOS) { - final appId = Platform.isAndroid ? androidAppId : iOSAppId; - final url = Uri.parse( - Platform.isAndroid - ? 'market://details?id=$appId' - : 'https://apps.apple.com/app/id$appId', - ); - launchUrl( - url, - mode: LaunchMode.externalApplication, - ); - } + _openStore(); break; case MainOverflowMenuOption.help: if (mounted) { diff --git a/pubspec.lock b/pubspec.lock index 64341a9c..be464b6f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.1" command: dependency: "direct main" description: @@ -645,10 +645,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.18.0" io: dependency: transitive description: @@ -694,14 +694,6 @@ packages: relative: true source: path version: "0.0.1" - launch_review: - dependency: transitive - description: - name: launch_review - sha256: "04cdaf752033cefd53bc0fa9c22105801ef53791a93d8b6cdd00fcb3c1c1604b" - url: "https://pub.dev" - source: hosted - version: "3.0.1" lints: dependency: transitive description: @@ -730,18 +722,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.15" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.2.0" melos: dependency: "direct main" description: @@ -754,10 +746,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" mime: dependency: transitive description: @@ -945,10 +937,10 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -1182,10 +1174,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" sqflite: dependency: transitive description: @@ -1230,10 +1222,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" state_notifier: dependency: transitive description: @@ -1246,10 +1238,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -1302,10 +1294,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.5.1" timing: dependency: transitive description: @@ -1437,10 +1429,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "11.3.0" watcher: dependency: transitive description: @@ -1449,14 +1441,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.dev" - source: hosted - version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1529,5 +1513,5 @@ packages: source: hosted version: "2.1.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.0.5 <4.0.0" flutter: ">=3.10.0" From d98c5bd7945ba77e19658560114d8513d2f1df7d Mon Sep 17 00:00:00 2001 From: Bhav Khurana Date: Tue, 5 Mar 2024 00:44:33 +0530 Subject: [PATCH 7/7] revert app_localization file --- packages/l10n/lib/src/l10n/app_localizations.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/l10n/lib/src/l10n/app_localizations.dart b/packages/l10n/lib/src/l10n/app_localizations.dart index 7825f1ae..86fb2fbb 100644 --- a/packages/l10n/lib/src/l10n/app_localizations.dart +++ b/packages/l10n/lib/src/l10n/app_localizations.dart @@ -176,4 +176,4 @@ AppLocalizations lookupAppLocalizations(Locale locale) { 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' 'that was used.'); -} \ No newline at end of file +}