├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── analysis_options.yaml ├── assets ├── bundles.json ├── bundles │ ├── Gaming.png │ ├── Graphic_design.png │ └── Software_development.png ├── db │ └── flathub_database.db ├── icons │ └── Archive.zip ├── images │ └── no-image.png ├── json │ └── userSettings.json ├── localizations │ ├── ar.json │ ├── br.json │ ├── en.json │ ├── es.json │ ├── fr.json │ └── it.json ├── logos │ ├── 512x512.png │ ├── logodupot.svg │ └── splash.png ├── recipies.json ├── recipies │ ├── com.discordapp.Discord.json │ ├── com.heroicgameslauncher.hgl.json │ ├── com.usebottles.bottles.json │ ├── com.valvesoftware.Steam.json │ └── net.lutris.Lutris.json └── settings.json ├── build.sh ├── buildAppImage.sh ├── buildAppimage.sh ├── buildAsset ├── flatpak │ └── settings.json └── other │ └── settings.json ├── buildDeb.sh ├── buildDebug.sh ├── buildForFlatPak.sh ├── buildForOther.sh ├── buildRpm.sh ├── buildZip.sh ├── checkLocalization.php ├── distribute_options.yaml ├── export ├── deb │ └── 512x512.png ├── flatpak │ ├── 16x16.png │ ├── 24x24.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ ├── 64x64.png │ ├── org.dupot.easyflatpak.appdata.xml │ └── org.dupot.easyflatpak.desktop ├── rpm │ └── 512x512.png └── screenshots │ ├── Screenshot_application_steam.png │ ├── Screenshot_application_steam_confirm_install.png │ ├── Screenshot_application_steam_install_setup_path.png │ ├── Screenshot_category.png │ ├── Screenshot_category_grid.png │ ├── Screenshot_home.png │ ├── Screenshot_installed_applications.png │ ├── Screenshot_override_view.png │ ├── Screenshot_search.png │ └── Screenshot_update_availables.png ├── lib ├── Domain │ └── Entity │ │ ├── application_installed_entity.dart │ │ ├── application_update_entity.dart │ │ ├── bundle_entity.dart │ │ ├── db │ │ ├── application_category_entity.dart │ │ ├── application_entity.dart │ │ └── category_entity.dart │ │ ├── recipe │ │ ├── permission_entity.dart │ │ ├── permission_overrided_entity.dart │ │ └── recipe_entity.dart │ │ ├── settings_entity.dart │ │ └── user_settings_entity.dart ├── Infrastructure │ ├── Api │ │ ├── bundle_api.dart │ │ ├── command_api.dart │ │ ├── flathub_api.dart │ │ ├── localization_api.dart │ │ ├── logger_api.dart │ │ ├── path_api.dart │ │ └── recipe_api.dart │ ├── Control │ │ ├── Model │ │ │ ├── SubView │ │ │ │ └── override_control.dart │ │ │ └── View │ │ │ │ ├── application_view_model.dart │ │ │ │ ├── home_view_model.dart │ │ │ │ ├── search_view_model.dart │ │ │ │ └── side_menu_view_model.dart │ │ └── Process │ │ │ └── update_from_flathub_process.dart │ ├── Entity │ │ ├── menu_item_entity.dart │ │ ├── navigation_entity.dart │ │ ├── override_form_control.dart │ │ ├── radio_bool_entity.dart │ │ └── radio_string_entity.dart │ ├── Repository │ │ └── application_repository.dart │ ├── Screen │ │ ├── Layout │ │ │ ├── only_content_layout.dart │ │ │ └── side_menu_with_content_and_subcontent.dart │ │ ├── SharedComponents │ │ │ ├── Button │ │ │ │ ├── add_to_cart_button.dart │ │ │ │ ├── close_subview_button.dart │ │ │ │ ├── dialog_cancel_button.dart │ │ │ │ ├── dialog_confirm_button.dart │ │ │ │ ├── install_all_button.dart │ │ │ │ ├── install_button.dart │ │ │ │ ├── install_with_recipe_button.dart │ │ │ │ ├── override_button.dart │ │ │ │ ├── remove_from_cart_button.dart │ │ │ │ ├── run_button.dart │ │ │ │ ├── uninstall_button.dart │ │ │ │ ├── update_all_button.dart │ │ │ │ └── update_button.dart │ │ │ ├── Card │ │ │ │ ├── card_application_component.dart │ │ │ │ └── card_output_component.dart │ │ │ ├── Content │ │ │ │ └── application_list_content.dart │ │ │ ├── Group │ │ │ │ └── block_app_list_component.dart │ │ │ ├── List │ │ │ │ ├── datatable_application_list_component.dart │ │ │ │ ├── grid_application_list_component.dart │ │ │ │ └── listview_application_list_component.dart │ │ │ └── SubForm │ │ │ │ ├── radio_bool_list_subform.dart │ │ │ │ └── radio_string_list_subform.dart │ │ ├── SubView │ │ │ ├── bundle_subview.dart │ │ │ ├── cart_install_all_subview.dart │ │ │ ├── cart_override_subview.dart │ │ │ ├── export_subview.dart │ │ │ ├── import_subview.dart │ │ │ ├── install_subview.dart │ │ │ ├── install_with_recipe_subview.dart │ │ │ ├── override_subview.dart │ │ │ ├── uninstall_subview.dart │ │ │ ├── update_available_processing_all_subview.dart │ │ │ └── update_available_processing_subview.dart │ │ ├── Theme │ │ │ ├── theme_button_style.dart │ │ │ └── theme_text_style.dart │ │ └── View │ │ │ ├── UserSettings │ │ │ └── Form │ │ │ │ ├── darkmode_form.dart │ │ │ │ ├── flathubapi_form.dart │ │ │ │ ├── language_form.dart │ │ │ │ ├── parameter_page_form.dart │ │ │ │ └── scope_form.dart │ │ │ ├── about_view.dart │ │ │ ├── application_view.dart │ │ │ ├── bundles_view.dart │ │ │ ├── cart_view.dart │ │ │ ├── category_view.dart │ │ │ ├── home_view.dart │ │ │ ├── installed_applications_view.dart │ │ │ ├── loading_view.dart │ │ │ ├── moreactions_view.dart │ │ │ ├── reload_view.dart │ │ │ ├── search_view.dart │ │ │ ├── side_menu_view.dart │ │ │ ├── updates_availables_view.dart │ │ │ └── user_settings_view.dart │ ├── Service │ │ └── localisation_delegate_service.dart │ └── application.dart └── main.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc ├── my_application.h └── packaging │ ├── appimage │ └── make_config.yaml │ ├── deb │ └── make_config.yaml │ └── rpm │ └── make_config.yaml ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | ios/ 41 | macos/ 42 | android/ 43 | windows/ 44 | web/favicon.png 45 | web/index.html 46 | web/manifest.json 47 | web/icons/Icon-192.png 48 | web/icons/Icon-512.png 49 | web/icons/Icon-maskable-192.png 50 | web/icons/Icon-maskable-512.png 51 | dist/ 52 | remoteBuildAndDownload.sh 53 | todo.txt 54 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "b0850beeb25f6d5b10426284f506557f66181b36" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 17 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 18 | - platform: android 19 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 20 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 21 | - platform: ios 22 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 23 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 24 | - platform: linux 25 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 26 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 27 | - platform: macos 28 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 29 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 30 | - platform: web 31 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 32 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 33 | - platform: windows 34 | create_revision: b0850beeb25f6d5b10426284f506557f66181b36 35 | base_revision: b0850beeb25f6d5b10426284f506557f66181b36 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "type": "dart", 10 | "request": "launch", 11 | "program": "lib/main.dart" 12 | }, 13 | { 14 | "name": "dupotEasyFlatpak", 15 | "request": "launch", 16 | "type": "dart" 17 | }, 18 | { 19 | "name": "dupotEasyFlatpak (profile mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "profile" 23 | }, 24 | { 25 | "name": "dupotEasyFlatpak (release mode)", 26 | "request": "launch", 27 | "type": "dart", 28 | "flutterMode": "release" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dupot Easy Flatpak 2 | 3 | Flatpak store interface 4 | 5 | This application will help you install Flatpak apps in a user-friendly way. We have recipes for some of them to install them even better. 6 | 7 | ![Application](export/screenshots/Screenshot_home.png) 8 | 9 | ![Application](export/screenshots/Screenshot_category.png) 10 | 11 | ![Application](export/screenshots/Screenshot_installed_applications.png) 12 | 13 | ![Application](export/screenshots/Screenshot_update_availables.png) 14 | 15 | ![Application](export/screenshots/Screenshot_search.png) 16 | 17 | ![Application](export/screenshots/Screenshot_application_steam.png) 18 | 19 | ![Application](export/screenshots/Screenshot_application_steam_confirm_install.png) 20 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /assets/bundles.json: -------------------------------------------------------------------------------- 1 | { 2 | "Gaming": { 3 | "icon": "assets/bundles/Gaming.png", 4 | "applicationList": [ 5 | "com.valvesoftware.Steam", 6 | "com.discordapp.Discord", 7 | "net.lutris.Lutris", 8 | "com.heroicgameslauncher.hgl", 9 | "net.davidotek.pupgui2", 10 | "org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/24.08" 11 | ] 12 | }, 13 | "Graphic_design": { 14 | "icon": "assets/bundles/Graphic_design.png", 15 | "applicationList": [ 16 | "org.kde.kdenlive", 17 | "org.audacityteam.Audacity", 18 | "com.obsproject.Studio", 19 | "fr.handbrake.ghb", 20 | "org.kde.krita", 21 | "org.gimp.GIMP", 22 | "org.inkscape.Inkscape" 23 | ] 24 | }, 25 | "Software_development": { 26 | "icon": "assets/bundles/Software_development.png", 27 | "applicationList": [ 28 | "com.visualstudio.code", 29 | "com.google.AndroidStudio", 30 | "org.pgadmin.pgadmin4", 31 | "rest.insomnia.Insomnia", 32 | "io.dbeaver.DBeaverCommunity", 33 | "org.sqlitebrowser.sqlitebrowser" 34 | ] 35 | } 36 | } -------------------------------------------------------------------------------- /assets/bundles/Gaming.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/bundles/Gaming.png -------------------------------------------------------------------------------- /assets/bundles/Graphic_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/bundles/Graphic_design.png -------------------------------------------------------------------------------- /assets/bundles/Software_development.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/bundles/Software_development.png -------------------------------------------------------------------------------- /assets/db/flathub_database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/db/flathub_database.db -------------------------------------------------------------------------------- /assets/icons/Archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/icons/Archive.zip -------------------------------------------------------------------------------- /assets/images/no-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/images/no-image.png -------------------------------------------------------------------------------- /assets/json/userSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "applicationDataPath": "", 4 | "userOverrideLanguageCode": false, 5 | "languageCode": "en", 6 | "userOverrideDarkModeEnabled": true, 7 | "darkModeEnabled": false, 8 | "userInstallationScopeEnabled": false, 9 | "displayApplicationInstalledNumberInSideMenu": false, 10 | "displayApplicationInstalledNumberInPage": false, 11 | "flathubApiEnabled": true, 12 | "displayAppsMode": "displayModeList" 13 | } -------------------------------------------------------------------------------- /assets/localizations/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading": "جاري التحميل", 3 | "applications_available": "البرامج المتوفرة", 4 | "installation_finished": "اكتمل التثبيت", 5 | "installation_successfully": "تم التثبيت بنجاح", 6 | "uninstallation_finished": "اكتملت إزالة التثبيت", 7 | "uninstallation_successfully": "تمت إزالة التثبيت بنجاح", 8 | "details": "التفاصيل", 9 | "installation_already_installed": "مثبت بالفعل", 10 | "installing": "جاري التثبيت ...", 11 | "cancel": "إلغاء", 12 | "confirm": "تأكيد", 13 | "confirmation_title": "تأكيد", 14 | "do_you_confirm_installation_of": "هل تؤكد تثبيت ", 15 | "do_you_confirm_uninstallation_of": "هل تؤكد إزالة تثبيت ", 16 | "install": "تثبيت", 17 | "install_with_recipe": "تثبيت باستخدام الوصفة", 18 | "uninstall": "إزالة التثبيت", 19 | "application": "تطبيق", 20 | "applications": "البرامج", 21 | "description": "الوصف", 22 | "output": "المخرجات", 23 | "add_new_application": "إضافة تطبيق جديد", 24 | "add": "إضافة", 25 | "field_should_not_be_empty": "يجب ملء الحقل", 26 | "application_name": "اسم التطبيق", 27 | "application_json": "وصفة إعداد التطبيق (JSON)", 28 | "processing_form": "جاري المعالجة", 29 | "save": "حفظ", 30 | "close": "إغلاق", 31 | "Search": "بحث", 32 | "Home": "الرئيسية", 33 | "AudioVideo": "صوت / فيديو", 34 | "Development": "تطوير", 35 | "Education": "تعليم", 36 | "Game": "ألعاب", 37 | "Graphics": "رسومات", 38 | "Network": "الشبكات", 39 | "Office": "البرامج المكتبية", 40 | "Science": "علوم", 41 | "System": "برانج النظام", 42 | "Utility": "أدوات مساعدة", 43 | "Search...": "بحث...", 44 | "Author": "المؤلف", 45 | "Website": "الموقع الإلكتروني", 46 | "License": "الترخيص", 47 | "InstalledApps": "التطبيقات المثبتة", 48 | "Run": "تشغيل", 49 | "By": "بواسطة", 50 | "English": "الإنجليزية", 51 | "French": "الفرنسية", 52 | "Italian": "الإيطالية", 53 | "Spanish": "الإسبانية", 54 | "Brazilian": "البرتغالية (البرازيل)", 55 | "Arabic": "العربية", 56 | "Language": "اللغة", 57 | "More": "المزيد...", 58 | "Updates": "التحديثات المتوفرة", 59 | "NoUpdates": "لا توجد تحديثات متوفرة", 60 | "Update": "تحديث", 61 | "update_finished": "اكتمل التحديث", 62 | "delete_all_app_data": "حذف جميع بيانات التطبيق", 63 | "Edit_override": "تعديل أذونات التجاوز", 64 | "Screenshots": "لقطات الشاشة", 65 | "Links": "روابط", 66 | "Last_releases": "أحدث الإصدارات", 67 | "Yes": "نعم", 68 | "No": "لا", 69 | "Installation_scope": "نطاق التثبيت", 70 | "scopeSystem": "نطاق النظام", 71 | "scopeUser": "نطاق المستخدم", 72 | "do_you_confirm_update_all": "هل تؤكد تحديث الكل؟", 73 | "update_all": "تحديث الكل", 74 | "successfully_saved": "تم الحفظ بنجاح", 75 | "do_you_confirm_update_selected": "هل تؤكد تحديث العنصر (العناصر) المحدد؟", 76 | "Settings": "الإعدادات", 77 | "Override_language": "تجاوز اللغة", 78 | "Use_system_language": "استخدام لغة النظام", 79 | "DarkMode": "الوضع الداكن", 80 | "Use_system_darkmode": "استخدام الوضع الداكن للنظام", 81 | "Override_darkmode": "تجاوز الوضع الداكن", 82 | "Display_number_of_installedapps_in_sidemenu": "عرض عدد التطبيقات المثبتة في القائمة الجانبية", 83 | "About": "حول", 84 | "Version": "الإصدار", 85 | "Total": "الإجمالي", 86 | "Infos": "معلومات", 87 | "Installed_Size": "الحجم المثبت", 88 | "Download_Size": "حجم التنزيل", 89 | "add_to_cart": "إضافة إلى سلة البرامج", 90 | "Cart": "سلة البرامج", 91 | "remove_from_cart": "إزالة من سلة البرامج", 92 | "do_you_confirm_installation_of_all": "هل تؤكد تثبيت الكل؟", 93 | "install_all": "تثبيت الكل", 94 | "cart_is_empty": "سلة البرامج فارغة", 95 | "Name": "الاسم", 96 | "Description": "الوصف", 97 | "No_result_corresponding_to_this_research": "لا توجد نتائج مطابقة لهذا البحث :(", 98 | "loading_Check_installation": "التحقق من التثبيت", 99 | "loading_Installation_ok": "التثبيت جيد", 100 | "loading_Should_update_application_list_from_Flathub_api": "هل يجب تحديث قائمة التطبيقات من واجهة Flathub؟", 101 | "loading_Starting_update_application_list_from_Flathub_api": "بدء تحديث قائمة التطبيقات من واجهة Flathub", 102 | "loading_Update_application_list_from_Flathub_api_finished": "اكتمل تحديث قائمة التطبيقات من واجهة Flathub", 103 | "loading_Last_applications_updates_from_API_is_newer_than_7_days_not_need": "آخر تحديثات للتطبيقات من الواجهة أحدث من 7 أيام، لا حاجة للتحديث", 104 | "loading_Load_application_localizations": "تحميل ترجمات التطبيق", 105 | "loading_Looking_for_applications_updates": "البحث عن تحديثات للبرامج", 106 | "FlathubApiEnabled": "واجهة Flathub مفعلة (يمكن تعطيلها في حال وجود مشاكل بالشبكة، مشاكل بالبروكسي..)", 107 | "More_actions": "المزيد من الإجراءات", 108 | "Bundles": "الحزم", 109 | "export_finished": "اكتمل التصدير", 110 | "Export": "تصدير", 111 | "Export_installed_apps": "تصدير التطبيقات المثبتة إلى ملف JSON", 112 | "Import": "استيراد", 113 | "Import_installed_apps_from_json": "استيراد التطبيقات المثبتة من ملف JSON", 114 | "Exported_to_pattern_filePath": "تم التصدير إلى _filePath_", 115 | "Missing_import_pattern_filePath": "ملف الاستيراد مفقود _filePath_", 116 | "Successfully_imported_in_cart": "تم الاستيراد بنجاح في السلة", 117 | "Already_synced_nothing_to_import": "مزامنة بالفعل، لا يوجد شيء للاستيراد", 118 | "bundle_Gaming": "حزمة الألعاب", 119 | "bundle_Graphic_design": "حزمة التصميم الجرافيكي", 120 | "bundle_Software_development": "حزمة تطوير البرمجيات", 121 | "recipe_share_your_home_label": "للمعلومة: يجب السماح لديسكورد بالوصول إلى مجلدك الرئيسي (دعه مضبوطًا)", 122 | "recipe_indicate_your_game_path_label": "أشر إلى مسار المجلد الذي تخزن فيه ألعابك", 123 | "recipe_should_installMangoHud": "تثبيت MangoHud", 124 | "recipe_enable_mangohud": "تفعيل MangoHud" 125 | } -------------------------------------------------------------------------------- /assets/localizations/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "loading": "Loading", 3 | "applications_available": "Applications availables", 4 | "installation_finished": "Installation finished", 5 | "installation_successfully": "Installation successfully", 6 | "uninstallation_finished": "Uninstallation finished", 7 | "uninstallation_successfully": "Uninstallation successfully", 8 | "details": "Details", 9 | "installation_already_installed": "Already installed", 10 | "installing": "Installing ...", 11 | "cancel": "Cancel", 12 | "confirm": "Confirm", 13 | "confirmation_title": "Confirmation", 14 | "do_you_confirm_installation_of": "Do you confirm installation of ", 15 | "do_you_confirm_uninstallation_of": "Do you confirm uninstallation of ", 16 | "install": "Install", 17 | "install_with_recipe": "Install with recipe", 18 | "uninstall": "Uninstall", 19 | "application": "Application", 20 | "applications": "Applications", 21 | "description": "Decription", 22 | "output": "Output", 23 | "add_new_application": "Add new application", 24 | "add": "Add", 25 | "field_should_not_be_empty": "Field should be filled", 26 | "application_name": "Application's name", 27 | "application_json": "Application's json setup recipie", 28 | "processing_form": "Processing", 29 | "save": "Save", 30 | "close": "close", 31 | "Search": "Search", 32 | "Home": "Home", 33 | "AudioVideo": "Audio / Video", 34 | "Development": "Development", 35 | "Education": "Education", 36 | "Game": "Game", 37 | "Graphics": "Graphics", 38 | "Network": "Network", 39 | "Office": "Office", 40 | "Science": "Science", 41 | "System": "System", 42 | "Utility": "Utility", 43 | "Search...": "Search...", 44 | "Author": "Author", 45 | "Website": "Website", 46 | "License": "License", 47 | "InstalledApps": "Installed applications", 48 | "Run": "Run", 49 | "By": "By", 50 | "English": "English", 51 | "French": "Français", 52 | "Italian": "Italiano", 53 | "Spanish": "Español", 54 | "Brazilian": "Brazilian", 55 | "Arabic": "Arabic", 56 | "Language": "Language", 57 | "More": "More...", 58 | "Updates": "Updates availables", 59 | "NoUpdates": "No updates availables", 60 | "Update": "Update", 61 | "update_finished": "Update finished", 62 | "delete_all_app_data": "Delete all application's data", 63 | "Edit_override": "Edit override permissions", 64 | "Screenshots": "Screenshots", 65 | "Links": "Links", 66 | "Last_releases": "Last releases", 67 | "Yes": "Yes", 68 | "No": "No", 69 | "Installation_scope": "Installation scope", 70 | "scopeSystem": "scope system", 71 | "scopeUser": "scope user", 72 | "do_you_confirm_update_all": "Do you confirm to update all", 73 | "update_all": "Update all", 74 | "successfully_saved": "Successfully saved", 75 | "do_you_confirm_update_selected": "Do you confirm to update selected line(s)", 76 | "Settings": "Settings", 77 | "Override_language": "Override language", 78 | "Use_system_language": "Use system language", 79 | "DarkMode": "DarkMode", 80 | "Use_system_darkmode": "Use system darkmode", 81 | "Override_darkmode": "Override darkmode", 82 | "Display_number_of_installedapps_in_sidemenu": "Display number of installed applications in side menu", 83 | "About": "About", 84 | "Version": "Version", 85 | "Total": "Total", 86 | "Infos": "Infos", 87 | "Installed_Size": "Installed Size", 88 | "Download_Size": "Download Size", 89 | "add_to_cart": "Add to cart", 90 | "Cart": "Cart", 91 | "remove_from_cart": "Remove from Cart", 92 | "do_you_confirm_installation_of_all": "Do you confirm installation of all", 93 | "install_all": "Install all", 94 | "cart_is_empty": "Cart is empty", 95 | "Name": "Name", 96 | "Description": "Description", 97 | "No_result_corresponding_to_this_research": "No result corresponding to this research :(", 98 | "loading_Check_installation": "Check installation", 99 | "loading_Installation_ok": "Installation ok", 100 | "loading_Should_update_application_list_from_Flathub_api": "Should update application list from Flathub api ?", 101 | "loading_Starting_update_application_list_from_Flathub_api": "Starting update application list from Flathub api", 102 | "loading_Update_application_list_from_Flathub_api_finished": "Update application list from Flathub api finished", 103 | "loading_Last_applications_updates_from_API_is_newer_than_7_days_not_need": "Last applications updates from API is newer than 7 days, not need", 104 | "loading_Load_application_localizations": "Load application localizations", 105 | "loading_Looking_for_applications_updates": "Looking for applications updates", 106 | "FlathubApiEnabled": "Flathub api enabled (can be disabled if network issue, proxies issues..)", 107 | "More_actions": "More actions", 108 | "Bundles": "Bundles", 109 | "export_finished": "Export finished", 110 | "Export": "Export", 111 | "Export_installed_apps": "Export installed applications into a json file", 112 | "Import": "Import", 113 | "Import_installed_apps_from_json": "Import installed applications from a json file", 114 | "Exported_to_pattern_filePath": "Exported to _filePath_", 115 | "Missing_import_pattern_filePath": "Missing import file _filePath_", 116 | "Successfully_imported_in_cart": "Successfully imported in cart", 117 | "Already_synced_nothing_to_import": "Already synced, nothing to import", 118 | "bundle_Gaming": "Bundle Gaming", 119 | "bundle_Graphic_design": "Bundle Graphic design", 120 | "bundle_Software_development": "Bundle Software development", 121 | "recipe_share_your_home_label": "For information: should allow discord to access your home directory (let it setuped)", 122 | "recipe_indicate_your_game_path_label": "Indicate directory path where you store your games", 123 | "recipe_should_installMangoHud": "Install mango hud", 124 | "recipe_enable_mangohud": "Enable mangohud" 125 | } -------------------------------------------------------------------------------- /assets/logos/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/logos/512x512.png -------------------------------------------------------------------------------- /assets/logos/logodupot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 65 | 73 | 74 | 80 | dupot.org 91 | 92 | 98 | Une production 109 | Arrêtez de tourner autour... 120 | 121 | 122 | -------------------------------------------------------------------------------- /assets/logos/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/assets/logos/splash.png -------------------------------------------------------------------------------- /assets/recipies.json: -------------------------------------------------------------------------------- 1 | [ 2 | "com.valvesoftware.Steam", 3 | "com.usebottles.bottles", 4 | "com.heroicgameslauncher.hgl", 5 | "net.lutris.Lutris", 6 | "com.discordapp.Discord" 7 | ] -------------------------------------------------------------------------------- /assets/recipies/com.discordapp.Discord.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Discord", 3 | "description": "Discord", 4 | "flatpak": "com.discordapp.Discord", 5 | "flatpakPermissionToOverrideList": [ 6 | { 7 | "label": "recipe_share_your_home_label", 8 | "type": "filesystem_noprompt", 9 | "value": "home" 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /assets/recipies/com.heroicgameslauncher.hgl.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Heroic Games Launcher", 3 | "description": "An Open Source Epic Games, GOG and Amazon Prime Games Launcher.", 4 | "flatpak": "com.heroicgameslauncher.hgl", 5 | "flatpakPermissionToOverrideList": [ 6 | { 7 | "label": "recipe_indicate_your_game_path_label", 8 | "type": "filesystem" 9 | }, 10 | { 11 | "label": "recipe_should_installMangoHud", 12 | "type": "install_flatpak_yesno", 13 | "value": "org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/24.08" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /assets/recipies/com.usebottles.bottles.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Bottles", 3 | "description": "Run Windows Software", 4 | "description_fr": "Permet de lancer des logiciels Windows", 5 | "flatpak": "com.usebottles.bottles", 6 | "flatpakPermissionToOverrideList": [ 7 | { 8 | "label": "recipe_indicate_your_game_path_label", 9 | "type": "filesystem" 10 | }, 11 | { 12 | "label": "recipe_should_installMangoHud", 13 | "type": "install_flatpak_yesno", 14 | "value": "org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/24.08" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /assets/recipies/com.valvesoftware.Steam.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Steam", 3 | "description": "Package to play steam", 4 | "flatpak": "com.valvesoftware.Steam", 5 | "flatpakPermissionToOverrideList": [ 6 | { 7 | "label": "recipe_indicate_your_game_path_label", 8 | "type": "filesystem" 9 | }, 10 | { 11 | "label": "recipe_should_installMangoHud", 12 | "type": "install_flatpak_yesno", 13 | "value": "org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/24.08" 14 | }, 15 | { 16 | "label": "recipe_enable_mangohud", 17 | "type": "env_yesno", 18 | "value": "MANGOHUD", 19 | "subValueYes": "1", 20 | "subValueNo": "0" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /assets/recipies/net.lutris.Lutris.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Lutris", 3 | "description": "Video game preservation platform.", 4 | "flatpak": "net.lutris.Lutris", 5 | "flatpakPermissionToOverrideList": [ 6 | { 7 | "label": "recipe_indicate_your_game_path_label", 8 | "type": "filesystem" 9 | }, 10 | { 11 | "label": "recipe_should_installMangoHud", 12 | "type": "install_flatpak_yesno", 13 | "value": "org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/24.08" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /assets/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "useFlatpakSpawn":false 3 | } -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | flutter build linux --release -------------------------------------------------------------------------------- /buildAppImage.sh: -------------------------------------------------------------------------------- 1 | flutter_distributor release --name=dev --jobs=dupot-easy-flatpak-appimage 2 | -------------------------------------------------------------------------------- /buildAppimage.sh: -------------------------------------------------------------------------------- 1 | flutter_distributor release --name=dev --jobs=dupot-easy-flatpak-zip 2 | -------------------------------------------------------------------------------- /buildAsset/flatpak/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "useFlatpakSpawn":true 3 | } -------------------------------------------------------------------------------- /buildAsset/other/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "useFlatpakSpawn":false 3 | } -------------------------------------------------------------------------------- /buildDeb.sh: -------------------------------------------------------------------------------- 1 | flutter_distributor release --name=dev --jobs=dupot-easy-flatpak-deb 2 | -------------------------------------------------------------------------------- /buildDebug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | flutter build linux --debug -------------------------------------------------------------------------------- /buildForFlatPak.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cp buildAsset/flatpak/settings.json assets/ 3 | ./build.sh 4 | rm -rf dist/bundle 5 | cp -r build/linux/x64/release/bundle dist/ 6 | cd dist 7 | tar -czf bundle.tar.gz bundle 8 | -------------------------------------------------------------------------------- /buildForOther.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf dist/2* 3 | cp buildAsset/other/settings.json assets/ 4 | ./buildDeb.sh 5 | ./buildRpm.sh 6 | -------------------------------------------------------------------------------- /buildRpm.sh: -------------------------------------------------------------------------------- 1 | flutter_distributor release --name=dev --jobs=dupot-easy-flatpak-rpm 2 | -------------------------------------------------------------------------------- /buildZip.sh: -------------------------------------------------------------------------------- 1 | flutter_distributor release --name=dev --jobs=dupot-easy-flatpak-zip 2 | -------------------------------------------------------------------------------- /checkLocalization.php: -------------------------------------------------------------------------------- 1 | applicationList; 5 | 6 | BundleEntity(this.name, this.icon, this.applicationList); 7 | } 8 | -------------------------------------------------------------------------------- /lib/Domain/Entity/db/application_category_entity.dart: -------------------------------------------------------------------------------- 1 | class ApplicationCategoryEntity { 2 | // ignore: non_constant_identifier_names 3 | final String appstream_id; 4 | // ignore: non_constant_identifier_names 5 | final String category_id; 6 | 7 | ApplicationCategoryEntity({ 8 | // ignore: non_constant_identifier_names 9 | required this.appstream_id, 10 | // ignore: non_constant_identifier_names 11 | required this.category_id, 12 | }); 13 | 14 | Map toMap() { 15 | return { 16 | 'appstream_id': appstream_id, 17 | 'category_id': category_id, 18 | }; 19 | } 20 | 21 | // Implement toString to make it easier to see information about 22 | // each dog when using the print statement. 23 | @override 24 | String toString() { 25 | return 'ApplicationCategoryEntity{appstream_id: $appstream_id, category_id: $category_id }'; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Domain/Entity/db/category_entity.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imikado/dupotEasyFlatpak/5435bc1111afb388b0cb4a9e5619552b94640414/lib/Domain/Entity/db/category_entity.dart -------------------------------------------------------------------------------- /lib/Domain/Entity/recipe/permission_entity.dart: -------------------------------------------------------------------------------- 1 | class PermissionEntity { 2 | final String type; 3 | final String label; 4 | final String? value; 5 | final String? subValueYes; 6 | final String? subValueNo; 7 | 8 | static const constTypeFileSystem = 'filesystem'; 9 | static const constTypeFileSystemNoPrompt = 'filesystem_noprompt'; 10 | 11 | static const constTypeInstallYesNo = 'install_flatpak_yesno'; 12 | 13 | static const constTypeEnvYesNo = 'env_yesno'; 14 | 15 | PermissionEntity(this.type, this.label, 16 | [this.value, this.subValueYes, this.subValueNo]); 17 | 18 | bool isFileSystem() { 19 | return (type == constTypeFileSystem); 20 | } 21 | 22 | bool isFileSystemNoPrompt() { 23 | return (type == constTypeFileSystemNoPrompt); 24 | } 25 | 26 | bool isInstallFlatpakYesNo() { 27 | return (type == constTypeInstallYesNo); 28 | } 29 | 30 | bool isEnvYesNo() { 31 | return (type == constTypeEnvYesNo); 32 | } 33 | 34 | String getType() { 35 | return type; 36 | } 37 | 38 | String getLabel() { 39 | return label; 40 | } 41 | 42 | String? getValue() { 43 | return value; 44 | } 45 | 46 | String? getSubValueYes() { 47 | return subValueYes; 48 | } 49 | 50 | String? getSubValueNo() { 51 | return subValueNo; 52 | } 53 | 54 | String getFlatpakOverrideType() { 55 | if (isFileSystemNoPrompt()) { 56 | return getFlatpakParameter(constTypeFileSystem); 57 | } 58 | 59 | return getFlatpakParameter(type); 60 | } 61 | 62 | String getFlatpakParameter(String parameter) { 63 | return '--$parameter='; 64 | } 65 | 66 | Map toJson() => { 67 | 'type': type, 68 | 'value': value, 69 | 'subValueYes': subValueYes, 70 | 'subValueNo': subValueNo 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /lib/Domain/Entity/recipe/permission_overrided_entity.dart: -------------------------------------------------------------------------------- 1 | class PermissionOverridedEntity { 2 | final String type; 3 | final String? value; 4 | final String? subValueYesNo; 5 | 6 | static const constSubValueTrue = 'yes'; 7 | 8 | static const constTypeFileSystem = 'filesystem'; 9 | static const constTypeFileSystemNoPrompt = 'filesystem_noprompt'; 10 | 11 | static const constTypeInstallYesNo = 'install_flatpak_yesno'; 12 | 13 | PermissionOverridedEntity(this.type, [this.value, this.subValueYesNo]); 14 | 15 | PermissionOverridedEntity.fromJson(Map json) 16 | : type = json['type'] as String, 17 | value = json['value'] as String, 18 | subValueYesNo = json['subValueYesNo']; 19 | 20 | bool isFileSystem() { 21 | return (type == constTypeFileSystem); 22 | } 23 | 24 | bool isFileSystemNoPrompt() { 25 | return (type == constTypeFileSystemNoPrompt); 26 | } 27 | 28 | bool isInstallFlatpakYesNo() { 29 | return (type == constTypeInstallYesNo); 30 | } 31 | 32 | String getType() { 33 | return type; 34 | } 35 | 36 | String? getValue() { 37 | return value; 38 | } 39 | 40 | String? getSubValueYesNo() { 41 | return subValueYesNo; 42 | } 43 | 44 | String getFlatpakOverrideType() { 45 | if (isFileSystemNoPrompt()) { 46 | return getFlatpakParameter(constTypeFileSystem); 47 | } 48 | 49 | return getFlatpakParameter(type); 50 | } 51 | 52 | String getFlatpakParameter(String parameter) { 53 | return '--$parameter='; 54 | } 55 | 56 | Map toJson() => { 57 | 'type': type, 58 | 'value': value, 59 | 'subValueYesNo': subValueYesNo, 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /lib/Domain/Entity/recipe/recipe_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/recipe/permission_entity.dart'; 2 | 3 | class RecipeEntity { 4 | String id = ''; 5 | List flatpakPermissionToOverrideList = []; 6 | 7 | RecipeEntity(String applicationId, 8 | List> rawFlatpakPermissionToOverrideList) { 9 | id = applicationId; 10 | for (Map rawPermissionLoop 11 | in rawFlatpakPermissionToOverrideList) { 12 | String value = "none"; 13 | if (rawPermissionLoop.containsKey('value')) { 14 | value = rawPermissionLoop['value']; 15 | } 16 | String subValueYes = ""; 17 | String subValueNo = ""; 18 | 19 | if (rawPermissionLoop.containsKey('subValueYes')) { 20 | subValueYes = rawPermissionLoop['subValueYes']; 21 | } 22 | if (rawPermissionLoop.containsKey('subValueNo')) { 23 | subValueNo = rawPermissionLoop['subValueNo']; 24 | } 25 | 26 | flatpakPermissionToOverrideList.add(PermissionEntity( 27 | rawPermissionLoop['type'].toString(), 28 | rawPermissionLoop['label']!, 29 | value, 30 | subValueYes, 31 | subValueNo)); 32 | } 33 | } 34 | 35 | List getFlatpakPermissionToOverrideList() { 36 | return flatpakPermissionToOverrideList; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Domain/Entity/settings_entity.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | class Settings { 6 | late Map settingsObj; 7 | 8 | Future load() async { 9 | String settingsString = await rootBundle.loadString("assets/settings.json"); 10 | settingsObj = Map.from(json.decode(settingsString)); 11 | } 12 | 13 | bool useFlatpakSpawn() { 14 | if (settingsObj.containsKey('useFlatpakSpawn')) { 15 | return settingsObj['useFlatpakSpawn']; 16 | } 17 | return false; 18 | } 19 | 20 | String getFlatpakSpawnCommand() { 21 | return 'flatpak-spawn'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Infrastructure/Api/bundle_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dupot_easy_flatpak/Domain/Entity/bundle_entity.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | class BundleApi { 7 | static bool isDebug = true; 8 | 9 | Future> getBundleEntityList() async { 10 | String bundlesString = await rootBundle.loadString("assets/bundles.json"); 11 | Map rawBundleList = 12 | Map.from(json.decode(bundlesString)); 13 | 14 | List bundleEntityList = []; 15 | 16 | rawBundleList.forEach((String bundleNameLoop, dynamic objLoop) { 17 | bundleEntityList.add(BundleEntity(bundleNameLoop, objLoop['icon'], 18 | List.from(objLoop['applicationList'] as List))); 19 | }); 20 | 21 | return bundleEntityList; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Infrastructure/Api/localization_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart'; 4 | 5 | class LocalizationApi { 6 | static final LocalizationApi _singleton = LocalizationApi._internal(); 7 | 8 | factory LocalizationApi({String newLanguageCode = ''}) { 9 | if (newLanguageCode.isNotEmpty && 10 | _localizedValues.containsKey(newLanguageCode)) { 11 | _singleton.languageCode = newLanguageCode; 12 | } 13 | return _singleton; 14 | } 15 | 16 | Future load() async { 17 | for (String languageLoop in languages()) { 18 | String recipiesString = await rootBundle 19 | .loadString("assets/localizations/$languageLoop.json"); 20 | _localizedValues[languageLoop] = 21 | Map.from(json.decode(recipiesString)); 22 | } 23 | } 24 | 25 | void setLanguageCode(String newLanguageCode) { 26 | if (newLanguageCode.contains('_')) { 27 | List newLanguageCodeList = newLanguageCode.split('_'); 28 | newLanguageCode = newLanguageCodeList[0]; 29 | } 30 | 31 | if (_localizedValues.containsKey(newLanguageCode)) { 32 | languageCode = newLanguageCode; 33 | } 34 | } 35 | 36 | LocalizationApi._internal(); 37 | 38 | String languageCode = 'en'; 39 | 40 | static final _localizedValues = >{ 41 | 'en': {}, 42 | 'fr': {}, 43 | 'it': {}, 44 | 'es': {}, 45 | 'br': {}, 46 | 'ar': {} 47 | }; 48 | 49 | static List languages() => _localizedValues.keys.toList(); 50 | 51 | String tr(String key) { 52 | if (!_localizedValues.containsKey(languageCode)) { 53 | throw Exception( 54 | 'Missing languageCode "$languageCode" in localization when ask tr("$key") '); 55 | } else if (!_localizedValues[languageCode]!.containsKey(key)) { 56 | return "$key (need translation)"; 57 | } 58 | return _localizedValues[languageCode]![key]!; 59 | } 60 | 61 | String trAndReplace(String key, Map patternList) { 62 | String rawTranslation = tr(key); 63 | String resultTranslation = rawTranslation; 64 | for (String patternLoop in patternList.keys) { 65 | resultTranslation = 66 | rawTranslation.replaceAll(patternLoop, patternList[patternLoop]!); 67 | } 68 | return resultTranslation; 69 | } 70 | 71 | String getLanguageCode() { 72 | return languageCode; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/Infrastructure/Api/logger_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:logger/logger.dart'; 4 | 5 | class LoggerApi { 6 | late File logFile; 7 | late Logger logger; 8 | 9 | static final LoggerApi _singleton = LoggerApi._internal(); 10 | 11 | factory LoggerApi([File? logFile]) { 12 | if (logFile != null) { 13 | _singleton.logFile = logFile; 14 | _singleton.logger = Logger( 15 | filter: 16 | ProductionFilter(), // Use the default LogFilter (-> only log in debug mode) 17 | printer: 18 | PrettyPrinter(), // Use the PrettyPrinter to format and print log 19 | output: FileOutput( 20 | overrideExisting: true, 21 | file: 22 | logFile), // Use the default LogOutput (-> send everything to console) 23 | ); 24 | } 25 | return _singleton; 26 | } 27 | 28 | LoggerApi._internal(); 29 | 30 | void info(String message) { 31 | logger.i(message); 32 | } 33 | 34 | void error(String message) { 35 | logger.e(message); 36 | } 37 | 38 | void warning(String message) { 39 | logger.w(message); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Infrastructure/Api/path_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as p; 4 | 5 | class PathApi { 6 | static String appPath = 'Easyflatpak'; 7 | static String installedJsonFilename = 'installed_apps.json'; 8 | 9 | static String getDataPath() { 10 | return Platform.environment['XDG_DATA_HOME']!; 11 | } 12 | 13 | static String getCachePath() { 14 | return getDataPath(); 15 | } 16 | 17 | static String getConfigPath() { 18 | return Platform.environment['XDG_CONFIG_HOME']!; 19 | } 20 | 21 | static String getLogPath() { 22 | return getDataPath(); 23 | } 24 | 25 | static String getImportConfigPath() { 26 | return p.join(getConfigPath(), 'Import'); 27 | } 28 | 29 | static String getExportConfigPath() { 30 | return p.join(getConfigPath(), 'Export'); 31 | } 32 | 33 | static String getImportJsonConfigPath() { 34 | return p.join(getImportConfigPath(), installedJsonFilename); 35 | } 36 | 37 | static String getExportJsonConfigPath() { 38 | return p.join(getExportConfigPath(), installedJsonFilename); 39 | } 40 | 41 | static String getUserSettingsJsonConfigPath() { 42 | return p.join(getConfigPath(), 'userSettings.json'); 43 | } 44 | 45 | static String getIconsCachePath() { 46 | return p.join(getCachePath(), 'Icons'); 47 | } 48 | 49 | static String getBuildConfigPath() { 50 | return p.join(getConfigPath(), 'build.log'); 51 | } 52 | 53 | static String getDbCachePath() { 54 | return p.join(getCachePath(), 'flathub_database.db'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/Infrastructure/Api/recipe_api.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dupot_easy_flatpak/Domain/Entity/recipe/recipe_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 5 | import 'package:flutter/services.dart'; 6 | 7 | class RecipeApi { 8 | static bool isDebug = true; 9 | 10 | Future> getRecipeApplicationIdList() async { 11 | String recipiesString = await rootBundle.loadString("assets/recipies.json"); 12 | return List.from(json.decode(recipiesString)); 13 | } 14 | 15 | Future> getApplicationList() async { 16 | List recipeList = await getRecipeApplicationIdList(); 17 | 18 | List recipeLowerCaseList = []; 19 | for (String recipeId in recipeList) { 20 | recipeLowerCaseList.add(recipeId.toLowerCase()); 21 | } 22 | 23 | return recipeLowerCaseList; 24 | } 25 | 26 | Future hasApplication(String id) async { 27 | List recipeList = await getRecipeApplicationIdList(); 28 | 29 | return recipeList.contains(id); 30 | } 31 | 32 | Future getApplication(id) async { 33 | final languageCode = LocalizationApi().getLanguageCode(); 34 | 35 | String applicaitonRecipieString = ''; 36 | 37 | applicaitonRecipieString = 38 | await rootBundle.loadString("assets/recipies/$id.json"); 39 | 40 | Map jsonApp = json.decode(applicaitonRecipieString); 41 | 42 | List rawList = jsonApp['flatpakPermissionToOverrideList']; 43 | 44 | if (jsonApp.containsKey('description_$languageCode')) { 45 | jsonApp['description'] = jsonApp['description_$languageCode']; 46 | } 47 | 48 | List> objectList = []; 49 | for (Map rawLoop in rawList) { 50 | objectList.add(rawLoop); 51 | } 52 | 53 | RecipeEntity applicationLoaded = RecipeEntity(id, objectList); 54 | 55 | return applicationLoaded; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Infrastructure/Control/Model/View/application_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/flathub_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/recipe_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Repository/application_repository.dart'; 8 | 9 | class ApplicationViewModel { 10 | Future getApplicationEntity(String appId) async { 11 | ApplicationRepository appStreamFactory = ApplicationRepository(); 12 | 13 | ApplicationEntity applicationEntity = 14 | await appStreamFactory.findApplicationEntityById(appId); 15 | 16 | UserSettingsEntity userSettingsEntity = UserSettingsEntity(); 17 | if (userSettingsEntity.isFlathubApiEnabled() && 18 | applicationEntity.lastUpdateIsOlderThan(7)) { 19 | LoggerApi().info('Updating from API'); 20 | if (!await FlathubApi(applicationRepository: appStreamFactory) 21 | .updateAppStream(appId)) { 22 | applicationEntity.isEmpty = true; 23 | 24 | await ApplicationRepository().deleteApplicationId(appId); 25 | return applicationEntity; 26 | } 27 | 28 | applicationEntity = 29 | await appStreamFactory.findApplicationEntityById(appId); 30 | } 31 | 32 | applicationEntity.isAlreadyInstalled = await checkAlreadyInstalled(appId); 33 | if (applicationEntity.isAlreadyInstalled) { 34 | applicationEntity.isScopeUser = await isInstalledInUserScope(appId); 35 | } 36 | 37 | applicationEntity.isOverrided = await checkIsOverrided(appId); 38 | 39 | applicationEntity.hasRecipe = await checkHasRecipe(appId); 40 | 41 | return applicationEntity; 42 | } 43 | 44 | Future checkHasUpdate(String applicationId) async { 45 | if (CommandApi().hasUpdateAvailableByAppId(applicationId)) { 46 | return true; 47 | } 48 | return false; 49 | } 50 | 51 | Future checkHasRecipe(String applicationId) async { 52 | List recipeList = await RecipeApi().getApplicationList(); 53 | if (recipeList.contains(applicationId.toLowerCase())) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | Future checkAlreadyInstalled(String applicationId) async { 60 | FlatpakApplication result = 61 | await CommandApi().isApplicationAlreadyInstalled(applicationId); 62 | 63 | return result.isInstalled; 64 | } 65 | 66 | Future isInstalledInUserScope(String applicationId) async { 67 | return await CommandApi().isApplicationInstalledInScopeUser(applicationId); 68 | } 69 | 70 | Future checkIsOverrided(String applicationId) async { 71 | if (!await RecipeApi().hasApplication(applicationId)) { 72 | return false; 73 | } 74 | 75 | FlatpakOverrideApplication result = 76 | await CommandApi().isApplicationOverrided(applicationId); 77 | return result.isOverrided; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/Infrastructure/Control/Model/View/home_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Repository/application_repository.dart'; 3 | 4 | class HomeViewModel { 5 | late ApplicationRepository applicationRepository; 6 | late List categoryIdList = []; 7 | 8 | HomeViewModel() { 9 | applicationRepository = ApplicationRepository(); 10 | } 11 | 12 | Future>> getApplicationEntityList() async { 13 | List> applicationEntityListList = []; 14 | 15 | List localCategoryIdList = await getCategoryIdList(); 16 | 17 | for (String categoryIdLoop in localCategoryIdList) { 18 | List appStreamList = await applicationRepository 19 | .findListApplicationEntityByCategoryOrderedAndLimited( 20 | categoryIdLoop, 9); 21 | 22 | applicationEntityListList.add(appStreamList); 23 | } 24 | 25 | return applicationEntityListList; 26 | } 27 | 28 | Future> getCategoryIdList() async { 29 | if (categoryIdList.isEmpty) { 30 | categoryIdList = await applicationRepository.findAllCategoryList(); 31 | } 32 | return categoryIdList; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Infrastructure/Control/Model/View/search_view_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Repository/application_repository.dart'; 3 | 4 | class SearchViewModel { 5 | Future> getApplicationEntityListBySearch( 6 | String search) async { 7 | ApplicationRepository appStreamFactory = ApplicationRepository(); 8 | 9 | List applicationEntityList = []; 10 | if (search.length > 2) { 11 | return await appStreamFactory.findListApplicationEntityBySearch(search); 12 | } 13 | 14 | return applicationEntityList; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/Infrastructure/Control/Process/update_from_flathub_process.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io' as io; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/path_api.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:path_provider/path_provider.dart'; 8 | import 'package:path/path.dart' as p; 9 | import 'package:package_info_plus/package_info_plus.dart'; 10 | import 'package:archive/archive_io.dart'; 11 | 12 | import 'dart:io'; 13 | 14 | class UpdateFromFlathubProcess { 15 | CommandApi commandApi; 16 | 17 | UpdateFromFlathubProcess({required this.commandApi}); 18 | 19 | Future mkdir(String path) async { 20 | await commandApi.runProcessSync('/usr/bin/mkdir', [path]); 21 | } 22 | 23 | Future copyTo(String fromPath, String targetPath) async { 24 | await commandApi.runProcessSync( 25 | '/usr/bin/cp', 26 | [fromPath, targetPath], 27 | ); 28 | } 29 | 30 | Future unarchive(String archivePath, String targetPath) async { 31 | final inputStream = InputFileStream(archivePath); 32 | final archive = ZipDecoder().decodeStream(inputStream); 33 | 34 | await extractArchiveToDisk(archive, targetPath); 35 | } 36 | 37 | Future process() async { 38 | PackageInfo packageInfo = await PackageInfo.fromPlatform(); 39 | 40 | io.File buildInstalled = File(PathApi.getBuildConfigPath()); 41 | 42 | if (buildInstalled.existsSync()) { 43 | String buildInfo = buildInstalled.readAsStringSync(); 44 | if (buildInfo == packageInfo.version) { 45 | return; 46 | } else { 47 | LoggerApi().info( 48 | 'Build installed $buildInfo different from current ${packageInfo.version}'); 49 | } 50 | } 51 | 52 | LoggerApi().info('Installing icons'); 53 | 54 | String targetIconsArchive = p.join(PathApi.getCachePath(), 'Archive.zip'); 55 | 56 | await copyAssetTo('assets/icons/Archive.zip', targetIconsArchive); 57 | 58 | await unarchive(targetIconsArchive, PathApi.getIconsCachePath()); 59 | 60 | buildInstalled.writeAsStringSync(packageInfo.version); 61 | } 62 | 63 | Future copyAssetTo(String assetPath, String targetPath) async { 64 | final bytes = await rootBundle.load(assetPath); 65 | final targetFile = File(targetPath); 66 | await targetFile.writeAsBytes(bytes.buffer.asUint8List()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Infrastructure/Entity/menu_item_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MenuItemEntity { 4 | MenuItemEntity( 5 | {required this.label, 6 | required this.action, 7 | required this.pageSelected, 8 | required this.categoryIdSelected, 9 | required this.badge, 10 | required this.icon}); 11 | 12 | String label; 13 | Function action; 14 | String pageSelected; 15 | String categoryIdSelected; 16 | String badge; 17 | IconData icon; 18 | 19 | bool isCategory() { 20 | return pageSelected == 'category'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Infrastructure/Entity/override_form_control.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class OverrideFormControl { 4 | late String label; 5 | late String type; 6 | late String value; 7 | 8 | late bool boolValue = false; 9 | 10 | String subValueYes = ''; 11 | String subValueNo = ''; 12 | 13 | TextEditingController textEditingController = TextEditingController(); 14 | 15 | static const String constFileSystem = 'filesystem'; 16 | static const String constInstallFlatpak = 'installFlatpak'; 17 | static const String constEnv = 'env'; 18 | 19 | void setLabel(String label) { 20 | this.label = label; 21 | } 22 | 23 | bool isTypeFileSystem() { 24 | return type == constFileSystem; 25 | } 26 | 27 | bool isTypeInstallFlatpak() { 28 | return type == constInstallFlatpak; 29 | } 30 | 31 | bool isTypeEnv() { 32 | return type == constEnv; 33 | } 34 | 35 | void setType(String type) { 36 | if (['filesystem_noprompt', 'filesystem'].contains(type)) { 37 | this.type = constFileSystem; 38 | return; 39 | } else if (type == 'install_flatpak_yesno') { 40 | this.type = constInstallFlatpak; 41 | return; 42 | } else if (type == 'env_yesno') { 43 | this.type = constEnv; 44 | return; 45 | } 46 | throw Exception('OverrideFormControl setType "$type" not implemented'); 47 | } 48 | 49 | void setValue(String value) { 50 | textEditingController.text = value; 51 | this.value = value; 52 | } 53 | 54 | void setSubValueYes(String subValueYes) { 55 | this.subValueYes = subValueYes; 56 | } 57 | 58 | void setSubValueNo(String subValueNo) { 59 | this.subValueNo = subValueNo; 60 | } 61 | 62 | String getValue() { 63 | return textEditingController.text; 64 | } 65 | 66 | String getSubValueYesNo() { 67 | if (boolValue) { 68 | return subValueYes; 69 | } 70 | return subValueNo; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/Infrastructure/Entity/radio_bool_entity.dart: -------------------------------------------------------------------------------- 1 | class RadioBoolEntity { 2 | String label; 3 | bool value; 4 | RadioBoolEntity({required this.label, required this.value}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/Infrastructure/Entity/radio_string_entity.dart: -------------------------------------------------------------------------------- 1 | class RadioStringEntity { 2 | String label; 3 | String value; 4 | RadioStringEntity({required this.label, required this.value}); 5 | } 6 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/Layout/only_content_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class OnlyContentLayout extends StatefulWidget { 4 | final Widget content; 5 | final Function handleGoTo; 6 | 7 | const OnlyContentLayout({ 8 | super.key, 9 | required this.handleGoTo, 10 | required this.content, 11 | }); 12 | 13 | @override 14 | OnlyContentLayoutState createState() => OnlyContentLayoutState(); 15 | } 16 | 17 | class OnlyContentLayoutState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | body: widget.content, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/Layout/side_menu_with_content_and_subcontent.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class SideMenuWithContentAndSubContentLayout extends StatefulWidget { 5 | final Widget menu; 6 | final Widget content; 7 | final Widget subContent; 8 | final bool hasSubContent; 9 | final bool hasPrevious; 10 | final Function handleGoToPrevious; 11 | final String pageSelected; 12 | 13 | const SideMenuWithContentAndSubContentLayout( 14 | {super.key, 15 | required this.menu, 16 | required this.content, 17 | required this.subContent, 18 | required this.hasSubContent, 19 | required this.hasPrevious, 20 | required this.handleGoToPrevious, 21 | required this.pageSelected}); 22 | 23 | @override 24 | SideMenuWithContentAndSubContentLayoutState createState() => 25 | SideMenuWithContentAndSubContentLayoutState(); 26 | } 27 | 28 | class SideMenuWithContentAndSubContentLayoutState 29 | extends State { 30 | @override 31 | Widget build(BuildContext context) { 32 | Widget content = Padding( 33 | padding: const EdgeInsets.fromLTRB(0, 5, 0, 5), 34 | child: Card( 35 | elevation: 4, 36 | color: Theme.of(context).cardColor, 37 | child: widget.content)); 38 | 39 | return Scaffold( 40 | resizeToAvoidBottomInset: true, 41 | body: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ 42 | Padding( 43 | padding: const EdgeInsets.all(5), 44 | child: SizedBox( 45 | width: 270, 46 | child: Card( 47 | elevation: 4, 48 | color: Theme.of(context).primaryColorLight, 49 | child: widget.menu), 50 | )), 51 | widget.hasSubContent 52 | ? SizedBox( 53 | width: 500, 54 | child: content, 55 | ) 56 | : Expanded(child: content), 57 | if (widget.hasSubContent) 58 | Expanded( 59 | child: Padding( 60 | padding: const EdgeInsets.all(5), 61 | child: Card( 62 | elevation: 4, 63 | color: Theme.of(context).secondaryHeaderColor, 64 | child: Padding( 65 | padding: const EdgeInsets.fromLTRB(0, 10, 0, 10), 66 | child: widget.subContent)))) 67 | ]), 68 | floatingActionButton: widget.hasPrevious & 69 | !widget.hasSubContent & 70 | (widget.pageSelected == NavigationEntity.pageApplication) 71 | ? FloatingActionButton( 72 | backgroundColor: Theme.of(context).secondaryHeaderColor, 73 | onPressed: () { 74 | widget.handleGoToPrevious(); 75 | }, 76 | child: const Icon(Icons.arrow_back_rounded)) 77 | : null); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/add_to_cart_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class AddToCartButton extends StatelessWidget { 7 | final ApplicationEntity applicationEntity; 8 | final Function handle; 9 | final bool isActive; 10 | 11 | const AddToCartButton( 12 | {super.key, 13 | required this.applicationEntity, 14 | required this.handle, 15 | required this.isActive}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 20 | 21 | return FilledButton.icon( 22 | style: themeButtonStyle.getButtonStyle(), 23 | onPressed: !isActive 24 | ? null 25 | : () { 26 | handle(); 27 | }, 28 | label: Text(LocalizationApi().tr('add_to_cart'), 29 | style: themeButtonStyle.getButtonTextStyle()), 30 | icon: Icon(Icons.add_shopping_cart, 31 | color: themeButtonStyle.getButtonTextStyle().color), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class CloseSubViewButton extends StatelessWidget { 6 | final Function handle; 7 | 8 | const CloseSubViewButton({super.key, required this.handle}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 13 | 14 | return FilledButton.icon( 15 | style: themeButtonStyle.getButtonStyle(), 16 | onPressed: () { 17 | handle(); 18 | }, 19 | label: Text(LocalizationApi().tr('close'), 20 | style: themeButtonStyle.getButtonTextStyle()), 21 | icon: 22 | Icon(Icons.close, color: themeButtonStyle.getButtonTextStyle().color), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/dialog_cancel_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class DialogCancelButton extends StatelessWidget { 6 | const DialogCancelButton({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 11 | 12 | return FilledButton( 13 | style: themeButtonStyle.getDialogButtonStyle(), 14 | onPressed: () { 15 | Navigator.of(context).pop(); 16 | }, 17 | child: Text(LocalizationApi().tr('cancel'))); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/dialog_confirm_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class DialogConfirmButton extends StatelessWidget { 6 | final Function onPressedFunction; 7 | 8 | const DialogConfirmButton({super.key, required this.onPressedFunction}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 13 | 14 | return FilledButton( 15 | style: themeButtonStyle.getDialogButtonStyle(), 16 | onPressed: () { 17 | onPressedFunction(); 18 | }, 19 | child: Text(LocalizationApi().tr('confirm'))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/install_all_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/dialog_cancel_button.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/dialog_confirm_button.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class InstallAllButton extends StatelessWidget { 8 | final Function handle; 9 | final bool isActive; 10 | 11 | const InstallAllButton( 12 | {super.key, required this.handle, required this.isActive}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 17 | 18 | return FilledButton.icon( 19 | style: themeButtonStyle.getButtonStyle(), 20 | onPressed: !isActive 21 | ? null 22 | : () { 23 | showDialog( 24 | context: context, 25 | builder: (context) => AlertDialog( 26 | backgroundColor: Theme.of(context).primaryColorLight, 27 | buttonPadding: const EdgeInsets.all(10), 28 | actions: [ 29 | const DialogCancelButton(), 30 | DialogConfirmButton(onPressedFunction: () { 31 | Navigator.of(context).pop(); 32 | 33 | handle(); 34 | }) 35 | ], 36 | title: Text(LocalizationApi().tr('confirmation_title')), 37 | contentPadding: const EdgeInsets.all(20.0), 38 | content: Text( 39 | '${LocalizationApi().tr('do_you_confirm_installation_of_all')} ?'), 40 | )); 41 | }, 42 | label: Text(LocalizationApi().tr('install_all'), 43 | style: themeButtonStyle.getButtonTextStyle()), 44 | icon: Icon(Icons.install_desktop, 45 | color: themeButtonStyle.getButtonTextStyle().color), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/override_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class OverrideButton extends StatelessWidget { 7 | final ApplicationEntity applicationEntity; 8 | final Function handle; 9 | final bool isActive; 10 | final bool hasError; 11 | 12 | const OverrideButton( 13 | {super.key, 14 | required this.applicationEntity, 15 | required this.handle, 16 | required this.isActive, 17 | required this.hasError}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 22 | 23 | return FilledButton.icon( 24 | style: themeButtonStyle.getButtonStyle(), 25 | onPressed: !isActive 26 | ? null 27 | : () { 28 | handle(); 29 | }, 30 | label: Text(LocalizationApi().tr('Edit_override'), 31 | style: themeButtonStyle.getButtonTextStyle()), 32 | icon: Icon(Icons.settings, 33 | color: hasError 34 | ? Colors.redAccent 35 | : themeButtonStyle.getButtonTextStyle().color), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/remove_from_cart_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class RemoveFromCartButton extends StatelessWidget { 7 | final ApplicationEntity applicationEntity; 8 | final Function handle; 9 | final bool isActive; 10 | 11 | const RemoveFromCartButton( 12 | {super.key, 13 | required this.applicationEntity, 14 | required this.handle, 15 | required this.isActive}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 20 | 21 | return FilledButton.icon( 22 | style: themeButtonStyle.getButtonStyle(), 23 | onPressed: !isActive 24 | ? null 25 | : () { 26 | handle(); 27 | }, 28 | label: Text(LocalizationApi().tr('remove_from_cart'), 29 | style: themeButtonStyle.getButtonTextStyle()), 30 | icon: Icon(Icons.remove_shopping_cart, 31 | color: themeButtonStyle.getButtonTextStyle().color), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/run_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class RunButton extends StatelessWidget { 8 | final ApplicationEntity applicationEntity; 9 | final bool isActive; 10 | 11 | const RunButton( 12 | {super.key, required this.applicationEntity, required this.isActive}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 17 | 18 | return FilledButton.icon( 19 | style: themeButtonStyle.getButtonStyle(), 20 | onPressed: !isActive 21 | ? null 22 | : () { 23 | CommandApi().run(applicationEntity.id); 24 | }, 25 | label: Text(LocalizationApi().tr('Run'), 26 | style: themeButtonStyle.getButtonTextStyle()), 27 | icon: Icon(Icons.launch, 28 | color: themeButtonStyle.getButtonTextStyle().color), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/uninstall_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class UninstallButton extends StatefulWidget { 7 | final ApplicationEntity applicationEntity; 8 | final Function handle; 9 | final bool isActive; 10 | final bool scopeUser; 11 | 12 | const UninstallButton( 13 | {super.key, 14 | required this.applicationEntity, 15 | required this.handle, 16 | required this.isActive, 17 | required this.scopeUser}); 18 | 19 | @override 20 | State createState() => _UninstallButtonState(); 21 | } 22 | 23 | class _UninstallButtonState extends State { 24 | bool stateWillDeleteAppData = false; 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | bool stateWillDeleteAppData = false; 29 | 30 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 31 | 32 | return FilledButton.icon( 33 | style: themeButtonStyle.getButtonStyle(), 34 | onPressed: !widget.isActive 35 | ? null 36 | : () { 37 | showDialog( 38 | context: context, 39 | builder: (BuildContext context) { 40 | return StatefulBuilder( 41 | builder: (context, StateSetter setState) { 42 | return AlertDialog( 43 | backgroundColor: Theme.of(context).primaryColorLight, 44 | buttonPadding: const EdgeInsets.all(10), 45 | actions: [ 46 | FilledButton( 47 | style: themeButtonStyle.getDialogButtonStyle(), 48 | onPressed: () { 49 | Navigator.of(context).pop(); 50 | }, 51 | child: Text(LocalizationApi().tr('cancel'))), 52 | FilledButton( 53 | style: themeButtonStyle.getDialogButtonStyle(), 54 | onPressed: () { 55 | Navigator.of(context).pop(); 56 | 57 | widget.handle( 58 | stateWillDeleteAppData, widget.scopeUser); 59 | }, 60 | child: Text(LocalizationApi().tr('confirm'))), 61 | ], 62 | title: Text(LocalizationApi().tr('confirmation_title')), 63 | contentPadding: const EdgeInsets.all(20.0), 64 | content: SizedBox( 65 | height: 100, 66 | child: Column( 67 | children: [ 68 | Text( 69 | '${LocalizationApi().tr('do_you_confirm_uninstallation_of')} ${widget.applicationEntity.getName()} ?'), 70 | const SizedBox( 71 | height: 20, 72 | ), 73 | Row( 74 | children: [ 75 | Switch( 76 | value: stateWillDeleteAppData, 77 | onChanged: (bool value) { 78 | setState(() { 79 | stateWillDeleteAppData = value; 80 | }); 81 | }, 82 | ), 83 | Text(LocalizationApi() 84 | .tr('delete_all_app_data')) 85 | ], 86 | ) 87 | ], 88 | )), 89 | ); 90 | }); 91 | }); 92 | 93 | //install(application); 94 | }, 95 | label: Text(LocalizationApi().tr('uninstall'), 96 | style: themeButtonStyle.getButtonTextStyle()), 97 | icon: Icon(Icons.delete_forever, 98 | color: themeButtonStyle.getButtonTextStyle().color), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/update_all_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/dialog_cancel_button.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/dialog_confirm_button.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class UpdateAllButton extends StatelessWidget { 8 | final Function handle; 9 | final bool isActive; 10 | 11 | const UpdateAllButton( 12 | {super.key, required this.handle, required this.isActive}); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 17 | 18 | return FilledButton.icon( 19 | style: themeButtonStyle.getButtonStyle(), 20 | onPressed: !isActive 21 | ? null 22 | : () { 23 | showDialog( 24 | context: context, 25 | builder: (context) => AlertDialog( 26 | backgroundColor: Theme.of(context).primaryColorLight, 27 | buttonPadding: const EdgeInsets.all(10), 28 | actions: [ 29 | const DialogCancelButton(), 30 | DialogConfirmButton(onPressedFunction: () { 31 | Navigator.of(context).pop(); 32 | 33 | handle(); 34 | }) 35 | ], 36 | title: Text(LocalizationApi().tr('confirmation_title')), 37 | contentPadding: const EdgeInsets.all(20.0), 38 | content: Text( 39 | '${LocalizationApi().tr('do_you_confirm_update_all')} ?'), 40 | )); 41 | }, 42 | label: Text(LocalizationApi().tr('update_all'), 43 | style: themeButtonStyle.getButtonTextStyle()), 44 | icon: Icon(Icons.install_desktop, 45 | color: themeButtonStyle.getButtonTextStyle().color), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Button/update_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/dialog_cancel_button.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/dialog_confirm_button.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class UpdateButton extends StatelessWidget { 8 | final Function handle; 9 | final bool isActive; 10 | 11 | const UpdateButton({super.key, required this.handle, required this.isActive}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 16 | 17 | return FilledButton.icon( 18 | style: themeButtonStyle.getButtonStyle(), 19 | onPressed: !isActive 20 | ? null 21 | : () { 22 | showDialog( 23 | context: context, 24 | builder: (context) => AlertDialog( 25 | backgroundColor: Theme.of(context).primaryColorLight, 26 | buttonPadding: const EdgeInsets.all(10), 27 | actions: [ 28 | const DialogCancelButton(), 29 | DialogConfirmButton(onPressedFunction: () { 30 | Navigator.of(context).pop(); 31 | 32 | handle(); 33 | }) 34 | ], 35 | title: Text(LocalizationApi().tr('confirmation_title')), 36 | contentPadding: const EdgeInsets.all(20.0), 37 | content: Text( 38 | '${LocalizationApi().tr('do_you_confirm_update_selected')} ?'), 39 | )); 40 | }, 41 | label: Text(LocalizationApi().tr('Update'), 42 | style: themeButtonStyle.getButtonTextStyle()), 43 | icon: Icon(Icons.launch, 44 | color: themeButtonStyle.getButtonTextStyle().color), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Card/card_application_component.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class CardApplicationComponent extends StatelessWidget { 7 | final String id; 8 | final String title; 9 | final String sumary; 10 | final String icon; 11 | final Function handleGoTo; 12 | 13 | const CardApplicationComponent( 14 | {super.key, 15 | required this.id, 16 | required this.title, 17 | required this.sumary, 18 | required this.icon, 19 | required this.handleGoTo}); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | if (icon.length > 10) { 24 | return InkWell( 25 | borderRadius: BorderRadius.circular(8.0), 26 | onTap: () { 27 | NavigationEntity.gotToApplicationId( 28 | handleGoTo: handleGoTo, applicationId: id); 29 | }, 30 | child: Card( 31 | color: Theme.of(context).primaryColorLight, 32 | clipBehavior: Clip.hardEdge, 33 | child: Column(mainAxisSize: MainAxisSize.min, children: [ 34 | ListTile( 35 | title: Text( 36 | title.length > 12 ? '${title.substring(0, 12)}..' : title, 37 | style: TextStyle( 38 | fontSize: 18, 39 | color: 40 | Theme.of(context).textTheme.headlineLarge!.color), 41 | textAlign: TextAlign.center, 42 | ), 43 | ), 44 | SizedBox( 45 | height: 5, 46 | ), 47 | Image.file( 48 | File(icon), 49 | width: 47, 50 | ) 51 | ]))); 52 | } 53 | 54 | return Card( 55 | color: Theme.of(context).primaryColorLight, 56 | clipBehavior: Clip.hardEdge, 57 | child: Column(mainAxisSize: MainAxisSize.min, children: [ 58 | ListTile( 59 | title: Text( 60 | title.length > 8 ? '${title.substring(0, 8)}...' : title, 61 | style: TextStyle( 62 | fontSize: 20, 63 | color: Theme.of(context).textTheme.headlineLarge!.color), 64 | textAlign: TextAlign.center, 65 | ), 66 | ), 67 | Expanded( 68 | child: Image.asset( 69 | 'assets/logos/512x512.png', 70 | width: 50, 71 | )) 72 | ])); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CardOutputComponent extends StatelessWidget { 4 | final String outputString; 5 | 6 | const CardOutputComponent({super.key, required this.outputString}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | const TextStyle outputTextStyle = 11 | TextStyle(color: Colors.white, fontSize: 14.0); 12 | 13 | return Padding( 14 | padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), 15 | child: Container( 16 | constraints: const BoxConstraints(minHeight: 20), 17 | color: Colors.black54, 18 | child: Padding( 19 | padding: const EdgeInsets.all(20), 20 | child: RichText( 21 | overflow: TextOverflow.clip, 22 | text: TextSpan( 23 | style: outputTextStyle, 24 | children: [ 25 | TextSpan(text: outputString), 26 | ], 27 | ), 28 | ))), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Content/application_list_content.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/datatable_application_list_component.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/grid_application_list_component.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/listview_application_list_component.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class ApplicationListContent extends StatefulWidget { 10 | final Function handleGoTo; 11 | final List applicationEntityList; 12 | 13 | const ApplicationListContent( 14 | {super.key, 15 | required this.applicationEntityList, 16 | required this.handleGoTo}); 17 | 18 | @override 19 | State createState() => _ApplicationListContentState(); 20 | } 21 | 22 | class _ApplicationListContentState extends State { 23 | ScrollController scrollController = ScrollController(); 24 | 25 | Set _segmentedButtonSelection = { 26 | UserSettingsEntity().getDisplayAppsMode() 27 | }; 28 | @override 29 | Widget build(BuildContext context) { 30 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 31 | 32 | return Scrollbar( 33 | interactive: false, 34 | thumbVisibility: true, 35 | controller: scrollController, 36 | child: Column( 37 | children: [ 38 | Padding( 39 | padding: const EdgeInsets.all(10.0), 40 | child: Row(children: [ 41 | const Expanded(child: SizedBox()), 42 | SegmentedButton( 43 | style: themeButtonStyle.getSegmentedButtonStyle(), 44 | // ToggleButtons above allows multiple or no selection. 45 | // Set `multiSelectionEnabled` and `emptySelectionAllowed` to true 46 | // to match the behavior of ToggleButtons. 47 | multiSelectionEnabled: false, 48 | emptySelectionAllowed: false, 49 | 50 | // Hide the selected icon to match the behavior of ToggleButtons. 51 | showSelectedIcon: true, 52 | // SegmentedButton uses a Set to track its selection state. 53 | selected: _segmentedButtonSelection, 54 | // This callback updates the set of selected segment values. 55 | onSelectionChanged: (Set newSelection) { 56 | UserSettingsEntity() 57 | .setDisplayAppsMode(newSelection.first); 58 | 59 | setState(() { 60 | _segmentedButtonSelection = newSelection; 61 | }); 62 | }, 63 | // SegmentedButton uses a List> to build its children 64 | // instead of a List like ToggleButtons. 65 | segments: appDisplayOptions.map>( 66 | ((AppDisplay, IconData) shirt) { 67 | return ButtonSegment( 68 | value: shirt.$1, 69 | label: Icon(shirt.$2, 70 | color: 71 | themeButtonStyle.getButtonTextStyle().color)); 72 | }).toList(), 73 | ), 74 | const SizedBox( 75 | width: 10, 76 | ) 77 | ])), 78 | Expanded(child: getContent()) 79 | ], 80 | )); 81 | } 82 | 83 | Widget getContent() { 84 | if (_segmentedButtonSelection.first == AppDisplay.grid) { 85 | return GridApplicationListComponent( 86 | applicationEntityList: widget.applicationEntityList, 87 | handleGoTo: widget.handleGoTo, 88 | handleScrollController: scrollController); 89 | } 90 | if (_segmentedButtonSelection.first == AppDisplay.list) { 91 | return ListviewApplicationListComponent( 92 | applicationEntityList: widget.applicationEntityList, 93 | handleGoTo: widget.handleGoTo, 94 | handleScrollController: scrollController); 95 | } 96 | 97 | return DatatableApplicationListComponent( 98 | applicationEntityList: widget.applicationEntityList, 99 | handleGoTo: widget.handleGoTo, 100 | handleScrollController: scrollController); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/Group/block_app_list_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_application_component.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class BlockAppListComponent extends StatelessWidget { 9 | final String categoryId; 10 | final List appStreamList; 11 | final String appPath; 12 | final Function handleGoTo; 13 | 14 | const BlockAppListComponent({ 15 | super.key, 16 | required this.categoryId, 17 | required this.appStreamList, 18 | required this.appPath, 19 | required this.handleGoTo, 20 | }); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | List widgetList = appStreamList.map((appStreamLoop) { 25 | String icon = ''; 26 | if (appStreamLoop.hasAppIcon()) { 27 | icon = 28 | '${UserSettingsEntity().getApplicationIconsPath()}/${appStreamLoop.getAppIcon()}'; 29 | } 30 | 31 | return SizedBox( 32 | width: 198, 33 | height: 122, 34 | child: CardApplicationComponent( 35 | id: appStreamLoop.id, 36 | title: appStreamLoop.getName(), 37 | sumary: appStreamLoop.getSummary(), 38 | icon: icon, 39 | handleGoTo: handleGoTo)); 40 | }).toList(); 41 | 42 | widgetList.add(SizedBox( 43 | width: 170, 44 | height: 140, 45 | child: IconButton( 46 | icon: const Icon(Icons.more_horiz_outlined), 47 | 48 | // icon: Icon(Icons.more), 49 | onPressed: () { 50 | NavigationEntity.gotToCategoryId( 51 | handleGoTo: handleGoTo, categoryId: categoryId); 52 | }, 53 | ))); 54 | 55 | return Column( 56 | children: [ 57 | const SizedBox(height: 20), 58 | Text( 59 | LocalizationApi().tr(categoryId), 60 | style: const TextStyle( 61 | fontSize: 30, 62 | fontWeight: FontWeight.bold, 63 | color: Colors.blueGrey), 64 | ), 65 | const SizedBox(height: 10), 66 | Wrap(children: widgetList), 67 | ], 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/List/datatable_application_list_component.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 7 | import 'package:flutter/material.dart'; 8 | 9 | class DatatableApplicationListComponent extends StatefulWidget { 10 | final List applicationEntityList; 11 | final Function handleGoTo; 12 | final ScrollController handleScrollController; 13 | 14 | const DatatableApplicationListComponent( 15 | {super.key, 16 | required this.applicationEntityList, 17 | required this.handleGoTo, 18 | required this.handleScrollController}); 19 | 20 | @override 21 | State createState() => 22 | _DatatableApplicationListComponentState(); 23 | } 24 | 25 | class _DatatableApplicationListComponentState 26 | extends State { 27 | List stateApplicationEntityList = []; 28 | bool stateSort = false; 29 | int stateColumnIndex = 1; 30 | List stateDataRowList = []; 31 | 32 | @override 33 | void initState() { 34 | super.initState(); 35 | setState(() { 36 | stateApplicationEntityList = widget.applicationEntityList; 37 | }); 38 | loadList(); 39 | } 40 | 41 | @override 42 | void didUpdateWidget(covariant DatatableApplicationListComponent oldWidget) { 43 | super.didUpdateWidget(oldWidget); 44 | 45 | if (widget.applicationEntityList != oldWidget.applicationEntityList) { 46 | setState(() { 47 | stateApplicationEntityList = widget.applicationEntityList; 48 | }); 49 | loadList(); 50 | } 51 | } 52 | 53 | void loadList() { 54 | List dataRowList = []; 55 | for (ApplicationEntity applicationEntityLoop 56 | in stateApplicationEntityList) { 57 | dataRowList.add(DataRow(cells: [ 58 | DataCell( 59 | !applicationEntityLoop.hasAppIcon() 60 | ? Image.asset('assets/images/no-image.png', height: 50) 61 | : Image.file( 62 | height: 50, 63 | File( 64 | '${UserSettingsEntity().getApplicationIconsPath()}/${applicationEntityLoop.getAppIcon()}')), 65 | onTap: () => NavigationEntity.gotToApplicationId( 66 | handleGoTo: widget.handleGoTo, 67 | applicationId: applicationEntityLoop.id)), 68 | DataCell(Text(applicationEntityLoop.name), 69 | onTap: () => NavigationEntity.gotToApplicationId( 70 | handleGoTo: widget.handleGoTo, 71 | applicationId: applicationEntityLoop.id)), 72 | DataCell(Text(applicationEntityLoop.summary), 73 | onTap: () => NavigationEntity.gotToApplicationId( 74 | handleGoTo: widget.handleGoTo, 75 | applicationId: applicationEntityLoop.id)), 76 | ])); 77 | } 78 | setState( 79 | () => stateDataRowList = dataRowList, 80 | ); 81 | } 82 | 83 | void sortBy(columnIndex, ascending) { 84 | setState(() { 85 | stateColumnIndex = columnIndex; 86 | if (columnIndex == 1) { 87 | stateApplicationEntityList.sort((a, b) => a.name.compareTo(b.name)); 88 | } 89 | 90 | if (ascending) { 91 | stateApplicationEntityList = 92 | stateApplicationEntityList.reversed.toList(); 93 | } 94 | }); 95 | loadList(); 96 | } 97 | 98 | @override 99 | Widget build(BuildContext context) { 100 | return SingleChildScrollView( 101 | controller: widget.handleScrollController, 102 | child: DataTable( 103 | sortAscending: stateSort, 104 | sortColumnIndex: stateColumnIndex, 105 | columns: [ 106 | DataColumn( 107 | label: Expanded( 108 | child: Text( 109 | '', 110 | style: TextStyle(fontStyle: FontStyle.italic), 111 | ), 112 | ), 113 | ), 114 | DataColumn( 115 | onSort: (columnIndex, ascending) { 116 | setState(() { 117 | stateSort = !stateSort; 118 | }); 119 | sortBy(columnIndex, ascending); 120 | }, 121 | label: Expanded( 122 | child: Text( 123 | LocalizationApi().tr('Name'), 124 | style: TextStyle(fontStyle: FontStyle.italic), 125 | ), 126 | ), 127 | ), 128 | DataColumn( 129 | label: Expanded( 130 | child: Text( 131 | LocalizationApi().tr('Description'), 132 | style: TextStyle(fontStyle: FontStyle.italic), 133 | ), 134 | ), 135 | ), 136 | ], 137 | rows: stateDataRowList)); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/List/grid_application_list_component.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_application_component.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class GridApplicationListComponent extends StatelessWidget { 7 | final List applicationEntityList; 8 | final Function handleGoTo; 9 | final ScrollController handleScrollController; 10 | 11 | const GridApplicationListComponent( 12 | {super.key, 13 | required this.applicationEntityList, 14 | required this.handleGoTo, 15 | required this.handleScrollController}); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return GridView.builder( 20 | itemCount: applicationEntityList.length, 21 | controller: handleScrollController, 22 | itemBuilder: (context, index) { 23 | ApplicationEntity appStreamLoop = applicationEntityList[index]; 24 | 25 | String icon = ''; 26 | if (appStreamLoop.hasAppIcon()) { 27 | icon = 28 | '${UserSettingsEntity().getApplicationIconsPath()}/${appStreamLoop.getAppIcon()}'; 29 | } 30 | 31 | return SizedBox( 32 | width: 150, 33 | height: 120, 34 | child: CardApplicationComponent( 35 | id: appStreamLoop.id, 36 | title: appStreamLoop.getName(), 37 | sumary: appStreamLoop.getSummary(), 38 | icon: icon, 39 | handleGoTo: handleGoTo)); 40 | }, 41 | gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( 42 | maxCrossAxisExtent: 180), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/List/listview_application_list_component.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class ListviewApplicationListComponent extends StatelessWidget { 9 | final List applicationEntityList; 10 | final Function handleGoTo; 11 | final ScrollController handleScrollController; 12 | 13 | const ListviewApplicationListComponent( 14 | {super.key, 15 | required this.applicationEntityList, 16 | required this.handleGoTo, 17 | required this.handleScrollController}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return ListView.builder( 22 | itemCount: applicationEntityList.length, 23 | controller: handleScrollController, 24 | itemBuilder: (context, index) { 25 | ApplicationEntity appStreamLoop = applicationEntityList[index]; 26 | 27 | return InkWell( 28 | borderRadius: BorderRadius.circular(8.0), 29 | onTap: () { 30 | NavigationEntity.gotToApplicationId( 31 | handleGoTo: handleGoTo, applicationId: appStreamLoop.id); 32 | }, 33 | child: Card( 34 | color: Theme.of(context).primaryColorLight, 35 | child: Column( 36 | children: [ 37 | Row( 38 | children: [ 39 | const SizedBox(width: 10), 40 | !appStreamLoop.hasAppIcon() 41 | ? Image.asset('assets/images/no-image.png', 42 | height: 50) 43 | : Image.file( 44 | height: 50, 45 | File( 46 | '${UserSettingsEntity().getApplicationIconsPath()}/${appStreamLoop.getAppIcon()}')), 47 | const SizedBox(width: 20), 48 | Expanded( 49 | child: Column( 50 | crossAxisAlignment: CrossAxisAlignment.start, 51 | children: [ 52 | Text( 53 | appStreamLoop.getName(), 54 | style: TextStyle( 55 | fontSize: 24, 56 | color: Theme.of(context) 57 | .textTheme 58 | .headlineLarge! 59 | .color), 60 | ), 61 | Text( 62 | appStreamLoop.getSummary(), 63 | ) 64 | ], 65 | ), 66 | ) 67 | ], 68 | ), 69 | ], 70 | ), 71 | )); 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/SubForm/radio_bool_list_subform.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_bool_entity.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class RadioBoolListSubform extends StatelessWidget { 6 | final List radioBoolEntityList; 7 | final bool radioGroupValue; 8 | final Function handleUpdateValue; 9 | 10 | const RadioBoolListSubform( 11 | {super.key, 12 | required this.radioBoolEntityList, 13 | required this.radioGroupValue, 14 | required this.handleUpdateValue}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Column( 19 | children: radioBoolEntityList 20 | .map( 21 | (RadioBoolEntity radioBoolEntityLoop) => ListTile( 22 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 23 | titleTextStyle: TextStyle( 24 | fontSize: 14, 25 | color: Theme.of(context).textTheme.headlineLarge!.color), 26 | title: Text(LocalizationApi().tr(radioBoolEntityLoop.label)), 27 | leading: Radio( 28 | value: radioBoolEntityLoop.value, 29 | groupValue: radioGroupValue, 30 | onChanged: (bool? value) { 31 | handleUpdateValue(value); 32 | }, 33 | ), 34 | ), 35 | ) 36 | .toList()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SharedComponents/SubForm/radio_string_list_subform.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_string_entity.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class RadioStringListSubform extends StatelessWidget { 6 | final List radioStringEntityList; 7 | final String value; 8 | final Function handleUpdateValue; 9 | 10 | const RadioStringListSubform( 11 | {super.key, 12 | required this.radioStringEntityList, 13 | required this.value, 14 | required this.handleUpdateValue}); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Column( 19 | spacing: 0, 20 | children: radioStringEntityList 21 | .map( 22 | (RadioStringEntity radioStringEntityLoop) => ListTile( 23 | visualDensity: VisualDensity(horizontal: 0, vertical: -4), 24 | titleTextStyle: TextStyle( 25 | fontSize: 14, 26 | color: Theme.of(context).textTheme.headlineLarge!.color), 27 | title: Text(LocalizationApi().tr(radioStringEntityLoop.label)), 28 | leading: Radio( 29 | value: radioStringEntityLoop.value, 30 | groupValue: value, 31 | onChanged: (String? value) { 32 | handleUpdateValue(radioStringEntityLoop.value); 33 | }, 34 | ), 35 | ), 36 | ) 37 | .toList()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SubView/cart_install_all_subview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Control/Model/SubView/override_control.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/override_form_control.dart'; 9 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart'; 10 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart'; 11 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 12 | import 'package:flutter/material.dart'; 13 | 14 | class CartInstallAllSubview extends StatefulWidget { 15 | final Function handleGoToApplicationInstalled; 16 | final List applicationIdListInCart; 17 | final Function handleRemoveFromCart; 18 | final Map> overrideSetupListByApplicationId; 19 | final Function handleSetApplicationLighted; 20 | 21 | const CartInstallAllSubview( 22 | {super.key, 23 | required this.handleGoToApplicationInstalled, 24 | required this.applicationIdListInCart, 25 | required this.handleRemoveFromCart, 26 | required this.overrideSetupListByApplicationId, 27 | required this.handleSetApplicationLighted}); 28 | 29 | @override 30 | State createState() => 31 | _InstallationWithRecipeViewState(); 32 | } 33 | 34 | class _InstallationWithRecipeViewState extends State { 35 | bool stateIsInstalling = false; 36 | bool stateDisplayInstallButton = true; 37 | String stateInstallationOutput = ''; 38 | 39 | String appPath = ''; 40 | 41 | final ScrollController scrollController = ScrollController(); 42 | 43 | bool stateIsLoaded = true; 44 | 45 | @override 46 | void initState() { 47 | install(); 48 | super.initState(); 49 | } 50 | 51 | Future install() async { 52 | setState(() { 53 | stateIsInstalling = true; 54 | }); 55 | 56 | OverrideControl overrideControl = OverrideControl(); 57 | 58 | CommandApi command = CommandApi(); 59 | String commandBin = 'flatpak'; 60 | 61 | List cartApplicationIdList = 62 | List.from(widget.applicationIdListInCart); 63 | 64 | for (String applicationIdLoop in cartApplicationIdList) { 65 | await widget.handleSetApplicationLighted(applicationIdLoop); 66 | List commandArgList = [ 67 | 'install', 68 | '-y', 69 | 'flathub', 70 | UserSettingsEntity().getInstallationScope(), 71 | applicationIdLoop 72 | ]; 73 | 74 | Process process = await Process.start(command.getCommand(commandBin), 75 | command.getFlatpakSpawnArgumentList(commandBin, commandArgList)); 76 | 77 | process.stdout.transform(utf8.decoder).listen((data) { 78 | if (mounted) { 79 | setState(() { 80 | stateInstallationOutput = data; 81 | }); 82 | } 83 | }); 84 | 85 | await process.exitCode; 86 | 87 | if (widget.overrideSetupListByApplicationId 88 | .containsKey(applicationIdLoop)) { 89 | await overrideControl.save(applicationIdLoop, 90 | widget.overrideSetupListByApplicationId[applicationIdLoop]!); 91 | } 92 | 93 | await CommandApi().loadApplicationInstalledList(); 94 | 95 | await widget.handleRemoveFromCart(applicationIdLoop); 96 | 97 | await widget.handleSetApplicationLighted(''); 98 | } 99 | 100 | setState(() { 101 | stateIsInstalling = false; 102 | }); 103 | } 104 | 105 | @override 106 | Widget build(BuildContext context) { 107 | return Scrollbar( 108 | interactive: false, 109 | thumbVisibility: true, 110 | controller: scrollController, 111 | child: ListView( 112 | controller: scrollController, 113 | children: [ 114 | Wrap( 115 | alignment: WrapAlignment.end, 116 | children: [ 117 | stateIsInstalling 118 | ? const LinearProgressIndicator() 119 | : CloseSubViewButton( 120 | handle: widget.handleGoToApplicationInstalled), 121 | const SizedBox(width: 20) 122 | ], 123 | ), 124 | CardOutputComponent(outputString: stateInstallationOutput), 125 | const SizedBox( 126 | height: 10, 127 | ), 128 | if (!stateIsInstalling) 129 | Center(child: Text(LocalizationApi().tr('installation_finished'))) 130 | ], 131 | ), 132 | ); 133 | } 134 | 135 | Widget getInstallButton() { 136 | if (stateIsInstalling | !stateDisplayInstallButton) { 137 | return const SizedBox(); 138 | } 139 | 140 | return FilledButton.icon( 141 | style: ThemeButtonStyle(context: context).getButtonStyle(), 142 | onPressed: () { 143 | install(); 144 | setState(() { 145 | stateDisplayInstallButton = false; 146 | }); 147 | }, 148 | label: Text(LocalizationApi().tr('install')), 149 | icon: const Icon(Icons.install_desktop), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SubView/import_subview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dupot_easy_flatpak/Domain/Entity/recipe/permission_overrided_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Control/Model/SubView/override_control.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart'; 9 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:ini/ini.dart'; 12 | 13 | class ImportSubview extends StatefulWidget { 14 | final Function handleGoToMore; 15 | final Function handleAddToCart; 16 | final Function handleSaveImportedPermissionOverridedEntity; 17 | 18 | const ImportSubview( 19 | {super.key, 20 | required this.handleGoToMore, 21 | required this.handleAddToCart, 22 | required this.handleSaveImportedPermissionOverridedEntity}); 23 | 24 | @override 25 | State createState() => _ImportSubviewState(); 26 | } 27 | 28 | class _ImportSubviewState extends State { 29 | bool stateIsInstalling = true; 30 | String stateInstallationOutput = ''; 31 | 32 | String applicationIdSelected = ''; 33 | 34 | String appPath = ''; 35 | 36 | final ScrollController scrollController = ScrollController(); 37 | 38 | OverrideControl overrideControl = OverrideControl(); 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | 44 | import(); 45 | } 46 | 47 | Future getOverrideConfig(applicationId) async { 48 | FlatpakOverrideApplication flatpakOverrideApplication = 49 | await CommandApi().isApplicationOverrided(applicationId); 50 | 51 | Config overrideConfig = Config.fromStrings( 52 | flatpakOverrideApplication.flatpakOutput.toString().split("\n")); 53 | 54 | return overrideConfig; 55 | } 56 | 57 | Future import() async { 58 | CommandApi commands = CommandApi(); 59 | 60 | if (!await commands.doesImportJsonFileExist()) { 61 | String importJsonPath = await commands.getImportJsonPath(); 62 | 63 | setState(() { 64 | stateInstallationOutput = LocalizationApi().trAndReplace( 65 | 'Missing_import_pattern_filePath', {'_filePath_': importJsonPath}); 66 | }); 67 | 68 | setState(() { 69 | stateIsInstalling = false; 70 | }); 71 | 72 | return; 73 | } 74 | 75 | List installedApplicationIdList = 76 | await commands.getInstalledApplicationList(); 77 | 78 | String jsonData = await commands.importFromJson(); 79 | 80 | Map installedJsonObj = jsonDecode(jsonData); 81 | 82 | Map> 83 | importedPermissionOverridedEntityList = {}; 84 | 85 | for (String applicationIdLoop in installedJsonObj.keys) { 86 | if (!installedApplicationIdList 87 | .contains(applicationIdLoop.toLowerCase()) || 88 | true) { 89 | widget.handleAddToCart(applicationIdLoop); 90 | 91 | List permissionOverrideEntityList = []; 92 | 93 | for (dynamic rawPermissionOverridedEntityLoop 94 | in installedJsonObj[applicationIdLoop]!) { 95 | permissionOverrideEntityList.add(PermissionOverridedEntity( 96 | rawPermissionOverridedEntityLoop['type']!, 97 | rawPermissionOverridedEntityLoop['value']!, 98 | rawPermissionOverridedEntityLoop['subValueYesNo'], 99 | )); 100 | } 101 | 102 | importedPermissionOverridedEntityList[applicationIdLoop] = 103 | permissionOverrideEntityList; 104 | } 105 | } 106 | 107 | widget.handleSaveImportedPermissionOverridedEntity( 108 | importedPermissionOverridedEntityList); 109 | 110 | LoggerApi().info('STDOUT: imported in cart'); 111 | setState(() { 112 | stateInstallationOutput = importedPermissionOverridedEntityList.isNotEmpty 113 | ? LocalizationApi().tr('Successfully_imported_in_cart') 114 | : LocalizationApi().tr('Already_synced_nothing_to_import'); 115 | }); 116 | 117 | setState(() { 118 | stateIsInstalling = false; 119 | }); 120 | } 121 | 122 | @override 123 | Widget build(BuildContext context) { 124 | return Scrollbar( 125 | interactive: false, 126 | thumbVisibility: true, 127 | controller: scrollController, 128 | child: ListView( 129 | controller: scrollController, 130 | children: [ 131 | Wrap( 132 | alignment: WrapAlignment.end, 133 | children: [ 134 | const SizedBox(width: 20), 135 | stateIsInstalling 136 | ? const LinearProgressIndicator() 137 | : CloseSubViewButton(handle: widget.handleGoToMore), 138 | const SizedBox(width: 20) 139 | ], 140 | ), 141 | CardOutputComponent(outputString: stateInstallationOutput), 142 | const SizedBox( 143 | height: 10, 144 | ), 145 | if (!stateIsInstalling) 146 | Center(child: Text(LocalizationApi().tr('export_finished'))) 147 | ], 148 | ), 149 | ); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SubView/install_subview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart'; 9 | import 'package:flutter/material.dart'; 10 | 11 | class InstallSubview extends StatefulWidget { 12 | final String applicationId; 13 | final Function handleGoToApplication; 14 | final String installScope; 15 | 16 | const InstallSubview( 17 | {super.key, 18 | required this.applicationId, 19 | required this.handleGoToApplication, 20 | required this.installScope}); 21 | 22 | @override 23 | State createState() => _InstallSubviewState(); 24 | } 25 | 26 | class _InstallSubviewState extends State { 27 | bool stateIsInstalling = true; 28 | String stateInstallationOutput = ''; 29 | 30 | String applicationIdSelected = ''; 31 | 32 | String appPath = ''; 33 | 34 | final ScrollController scrollController = ScrollController(); 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | 40 | install(); 41 | } 42 | 43 | Future install() async { 44 | applicationIdSelected = widget.applicationId; 45 | 46 | CommandApi command = CommandApi(); 47 | 48 | String commandBin = 'flatpak'; 49 | List commandArgList = [ 50 | 'install', 51 | '-y', 52 | 'flathub', 53 | widget.installScope, 54 | applicationIdSelected 55 | ]; 56 | 57 | Process.start(command.getCommand(commandBin), 58 | command.getFlatpakSpawnArgumentList(commandBin, commandArgList)) 59 | .then((Process process) { 60 | process.stdout.transform(utf8.decoder).listen((data) { 61 | LoggerApi().info('STDOUT: $data'); 62 | setState(() { 63 | stateInstallationOutput = data; 64 | }); 65 | }); 66 | 67 | process.stderr.transform(utf8.decoder).listen((data) { 68 | LoggerApi().warning('STDERR: $data'); 69 | setState(() { 70 | stateInstallationOutput = data; 71 | }); 72 | }); 73 | 74 | process.exitCode.then((exitCode) { 75 | LoggerApi().info('Exit code: $exitCode'); 76 | CommandApi().loadApplicationInstalledList(); 77 | 78 | setState(() { 79 | stateIsInstalling = false; 80 | }); 81 | }); 82 | }).catchError((e) { 83 | LoggerApi().error('Error starting process: $e'); 84 | }); 85 | } 86 | 87 | @override 88 | Widget build(BuildContext context) { 89 | return Scrollbar( 90 | interactive: false, 91 | thumbVisibility: true, 92 | controller: scrollController, 93 | child: ListView( 94 | controller: scrollController, 95 | children: [ 96 | Wrap( 97 | alignment: WrapAlignment.end, 98 | children: [ 99 | const SizedBox(width: 20), 100 | stateIsInstalling 101 | ? const LinearProgressIndicator() 102 | : CloseSubViewButton(handle: widget.handleGoToApplication), 103 | const SizedBox(width: 20) 104 | ], 105 | ), 106 | CardOutputComponent(outputString: stateInstallationOutput), 107 | const SizedBox( 108 | height: 10, 109 | ), 110 | if (!stateIsInstalling) 111 | Center(child: Text(LocalizationApi().tr('installation_finished'))) 112 | ], 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SubView/uninstall_subview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart'; 9 | import 'package:flutter/material.dart'; 10 | 11 | class UninstallSubview extends StatefulWidget { 12 | final String applicationId; 13 | final Function handleGoToApplication; 14 | final bool willDeleteAppData; 15 | final String installScope; 16 | 17 | const UninstallSubview( 18 | {super.key, 19 | required this.applicationId, 20 | required this.handleGoToApplication, 21 | required this.willDeleteAppData, 22 | required this.installScope}); 23 | 24 | @override 25 | State createState() => _InstallSubviewState(); 26 | } 27 | 28 | class _InstallSubviewState extends State { 29 | bool stateIsInstalling = true; 30 | String stateInstallationOutput = ''; 31 | 32 | String applicationIdSelected = ''; 33 | 34 | String appPath = ''; 35 | 36 | final ScrollController scrollController = ScrollController(); 37 | 38 | @override 39 | void initState() { 40 | super.initState(); 41 | 42 | install(); 43 | } 44 | 45 | Future install() async { 46 | applicationIdSelected = widget.applicationId; 47 | 48 | CommandApi command = CommandApi(); 49 | 50 | String commandBin = 'flatpak'; 51 | List commandArgList = [ 52 | 'uninstall', 53 | '-y', 54 | widget.installScope, 55 | ]; 56 | if (widget.willDeleteAppData) { 57 | commandArgList.add('--delete-data'); 58 | } 59 | commandArgList.add(applicationIdSelected); 60 | 61 | Process.start(command.getCommand(commandBin), 62 | command.getFlatpakSpawnArgumentList(commandBin, commandArgList)) 63 | .then((Process process) { 64 | process.stdout.transform(utf8.decoder).listen((data) { 65 | LoggerApi().info('STDOUT: $data'); 66 | setState(() { 67 | stateInstallationOutput = data; 68 | }); 69 | }); 70 | 71 | process.stderr.transform(utf8.decoder).listen((data) { 72 | LoggerApi().warning('STDERR: $data'); 73 | setState(() { 74 | stateInstallationOutput = data; 75 | }); 76 | }); 77 | 78 | process.exitCode.then((exitCode) { 79 | LoggerApi().info('Exit code: $exitCode'); 80 | CommandApi().loadApplicationInstalledList(); 81 | 82 | setState(() { 83 | stateIsInstalling = false; 84 | }); 85 | }).catchError((e) { 86 | LoggerApi().error('Error starting process: $e'); 87 | }); 88 | }).catchError((e) { 89 | LoggerApi().error('Error starting process: $e'); 90 | }); 91 | } 92 | 93 | @override 94 | Widget build(BuildContext context) { 95 | return Scrollbar( 96 | interactive: false, 97 | thumbVisibility: true, 98 | controller: scrollController, 99 | child: ListView( 100 | controller: scrollController, 101 | children: [ 102 | Wrap( 103 | alignment: WrapAlignment.end, 104 | children: [ 105 | const SizedBox(width: 20), 106 | stateIsInstalling 107 | ? const LinearProgressIndicator() 108 | : CloseSubViewButton(handle: widget.handleGoToApplication), 109 | const SizedBox(width: 20) 110 | ], 111 | ), 112 | CardOutputComponent(outputString: stateInstallationOutput), 113 | const SizedBox( 114 | height: 10, 115 | ), 116 | if (!stateIsInstalling) 117 | Center(child: Text(LocalizationApi().tr('uninstallation_finished'))) 118 | ], 119 | ), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SubView/update_available_processing_all_subview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart'; 9 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart'; 10 | import 'package:flutter/material.dart'; 11 | 12 | class UpdateAvailableProcessingAllSubview extends StatefulWidget { 13 | final Function handleGoTo; 14 | 15 | const UpdateAvailableProcessingAllSubview({ 16 | super.key, 17 | required this.handleGoTo, 18 | }); 19 | 20 | @override 21 | State createState() => 22 | _UpdateAvailableProcessingSubviewState(); 23 | } 24 | 25 | class _UpdateAvailableProcessingSubviewState 26 | extends State { 27 | bool stateIsInstalling = true; 28 | String stateInstallationOutput = ''; 29 | 30 | final ScrollController scrollController = ScrollController(); 31 | 32 | @override 33 | void initState() { 34 | super.initState(); 35 | 36 | updateAll(); 37 | } 38 | 39 | Future updateAll() async { 40 | CommandApi command = CommandApi(); 41 | 42 | String commandBin = 'flatpak'; 43 | List commandArgList = [ 44 | 'update', 45 | '-y', 46 | //UserSettingsEntity().getInstallationScope(), 47 | ]; 48 | 49 | Process process = await Process.start(command.getCommand(commandBin), 50 | command.getFlatpakSpawnArgumentList(commandBin, commandArgList)); 51 | 52 | process.stdout.transform(utf8.decoder).listen((data) { 53 | LoggerApi().info('STDOUT: $data'); 54 | setState(() { 55 | stateInstallationOutput = data; 56 | }); 57 | }); 58 | 59 | process.stderr.transform(utf8.decoder).listen((data) { 60 | LoggerApi().warning('STDERR: $data'); 61 | setState(() { 62 | stateInstallationOutput = data; 63 | }); 64 | }); 65 | 66 | process.exitCode.then((exitCode) { 67 | LoggerApi().info('Exit code: $exitCode'); 68 | 69 | CommandApi().checkUpdates().then((value) { 70 | setState(() { 71 | stateIsInstalling = false; 72 | }); 73 | }); 74 | }).catchError((e) { 75 | LoggerApi().error('Error starting process: $e'); 76 | }); 77 | } 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | return Scrollbar( 82 | interactive: false, 83 | thumbVisibility: true, 84 | controller: scrollController, 85 | child: ListView( 86 | controller: scrollController, 87 | children: [ 88 | Wrap( 89 | alignment: WrapAlignment.end, 90 | children: [ 91 | const SizedBox(width: 20), 92 | stateIsInstalling 93 | ? const LinearProgressIndicator() 94 | : CloseSubViewButton(handle: () { 95 | NavigationEntity.goToUpdatesAvailables( 96 | handleGoTo: widget.handleGoTo); 97 | }), 98 | const SizedBox(width: 20) 99 | ], 100 | ), 101 | CardOutputComponent(outputString: stateInstallationOutput), 102 | const SizedBox( 103 | height: 10, 104 | ), 105 | if (!stateIsInstalling) 106 | Center(child: Text(LocalizationApi().tr('update_finished'))) 107 | ], 108 | ), 109 | ); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/SubView/update_available_processing_subview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Button/close_subview_button.dart'; 9 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Card/card_output_component.dart'; 10 | import 'package:flutter/material.dart'; 11 | 12 | class UpdateAvailableProcessingSubview extends StatefulWidget { 13 | final Function handleGoTo; 14 | final List applicationIdSelectedList; 15 | 16 | const UpdateAvailableProcessingSubview( 17 | {super.key, 18 | required this.handleGoTo, 19 | required this.applicationIdSelectedList}); 20 | 21 | @override 22 | State createState() => 23 | _UpdateAvailableProcessingSubviewState(); 24 | } 25 | 26 | class _UpdateAvailableProcessingSubviewState 27 | extends State { 28 | bool stateIsInstalling = true; 29 | String stateInstallationOutput = ''; 30 | 31 | final ScrollController scrollController = ScrollController(); 32 | 33 | CommandApi command = CommandApi(); 34 | String commandBin = 'flatpak'; 35 | 36 | @override 37 | void initState() { 38 | super.initState(); 39 | 40 | updateList(widget.applicationIdSelectedList); 41 | } 42 | 43 | Future updateList(List applicationIdSelectedList) async { 44 | await updateSystem(applicationIdSelectedList); 45 | await updateUser(applicationIdSelectedList); 46 | } 47 | 48 | Future updateSystem(List applicationIdSelectedList) async { 49 | for (String applicationIdSelectedLoop in applicationIdSelectedList) { 50 | executeCommandWithArgList(['update', '-y', applicationIdSelectedLoop]); 51 | } 52 | } 53 | 54 | Future updateUser(List applicationIdSelectedList) async { 55 | for (String applicationIdSelectedLoop in applicationIdSelectedList) { 56 | executeCommandWithArgList( 57 | ['update', '-u', '-y', applicationIdSelectedLoop]); 58 | } 59 | } 60 | 61 | void executeCommandWithArgList(List argumentList) async { 62 | Process process = await Process.start(command.getCommand(commandBin), 63 | command.getFlatpakSpawnArgumentList(commandBin, argumentList)); 64 | 65 | process.stdout.transform(utf8.decoder).listen((data) { 66 | LoggerApi().info('STDOUT: $data'); 67 | setState(() { 68 | stateInstallationOutput = data; 69 | }); 70 | }); 71 | 72 | process.stderr.transform(utf8.decoder).listen((data) { 73 | LoggerApi().warning('STDERR: $data'); 74 | setState(() { 75 | stateInstallationOutput = data; 76 | }); 77 | }); 78 | 79 | process.exitCode.then((exitCode) { 80 | LoggerApi().info('Exit code: $exitCode'); 81 | setState(() { 82 | stateIsInstalling = false; 83 | }); 84 | }).catchError((e) { 85 | LoggerApi().error('Error starting process: $e'); 86 | }); 87 | } 88 | 89 | @override 90 | Widget build(BuildContext context) { 91 | return Scrollbar( 92 | interactive: false, 93 | thumbVisibility: true, 94 | controller: scrollController, 95 | child: ListView( 96 | controller: scrollController, 97 | children: [ 98 | Wrap( 99 | alignment: WrapAlignment.end, 100 | children: [ 101 | const SizedBox(width: 20), 102 | stateIsInstalling 103 | ? const LinearProgressIndicator() 104 | : CloseSubViewButton(handle: () { 105 | NavigationEntity.goToUpdatesAvailables( 106 | handleGoTo: widget.handleGoTo); 107 | }), 108 | const SizedBox(width: 20) 109 | ], 110 | ), 111 | CardOutputComponent(outputString: stateInstallationOutput), 112 | const SizedBox( 113 | height: 10, 114 | ), 115 | if (!stateIsInstalling) 116 | Center(child: Text(LocalizationApi().tr('update_finished'))) 117 | ], 118 | ), 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/Theme/theme_button_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ThemeButtonStyle { 4 | BuildContext context; 5 | 6 | ThemeButtonStyle({required this.context}); 7 | 8 | TextStyle getButtonTextStyle() { 9 | if (Theme.of(context).brightness == Brightness.dark) { 10 | return const TextStyle(fontSize: 14, color: Colors.white); 11 | } 12 | 13 | return const TextStyle(fontSize: 14, color: Colors.white); 14 | } 15 | 16 | ButtonStyle getSegmentedButtonStyle() { 17 | if (Theme.of(context).brightness == Brightness.dark) { 18 | return SegmentedButton.styleFrom( 19 | padding: const EdgeInsets.all(14), 20 | elevation: 0, 21 | side: const BorderSide(color: Colors.transparent, width: 0), 22 | shape: RoundedRectangleBorder( 23 | borderRadius: BorderRadius.circular(8), 24 | ), 25 | backgroundColor: Theme.of(context).primaryColorLight, 26 | foregroundColor: Colors.white54, 27 | selectedForegroundColor: Colors.white, 28 | selectedBackgroundColor: const Color.fromARGB(255, 27, 78, 112), 29 | ); 30 | } 31 | 32 | return SegmentedButton.styleFrom( 33 | padding: const EdgeInsets.all(14), 34 | elevation: 0, 35 | side: const BorderSide(color: Colors.transparent, width: 0), 36 | shape: RoundedRectangleBorder( 37 | borderRadius: BorderRadius.circular(8), 38 | ), 39 | backgroundColor: Theme.of(context).primaryColorLight, 40 | foregroundColor: Theme.of(context).primaryColor, 41 | selectedForegroundColor: Colors.white, 42 | selectedBackgroundColor: Theme.of(context).primaryColorDark, 43 | ); 44 | } 45 | 46 | ButtonStyle getButtonStyle() { 47 | if (Theme.of(context).brightness == Brightness.dark) { 48 | return ElevatedButton.styleFrom( 49 | backgroundColor: Theme.of(context).primaryColorLight, 50 | padding: const EdgeInsets.all(16), 51 | shape: 52 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)), 53 | textStyle: getButtonTextStyle()); 54 | } 55 | 56 | return ElevatedButton.styleFrom( 57 | backgroundColor: Theme.of(context).primaryColorDark, 58 | padding: const EdgeInsets.all(16), 59 | shape: 60 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)), 61 | textStyle: const TextStyle(fontSize: 14, color: Colors.white)); 62 | } 63 | 64 | ButtonStyle getDialogButtonStyle() { 65 | if (Theme.of(context).brightness == Brightness.dark) { 66 | return FilledButton.styleFrom( 67 | backgroundColor: Theme.of(context).colorScheme.onSecondaryContainer, 68 | padding: const EdgeInsets.all(20), 69 | textStyle: const TextStyle(fontSize: 14, color: Colors.black)); 70 | } 71 | return FilledButton.styleFrom( 72 | backgroundColor: Theme.of(context).colorScheme.onPrimaryContainer, 73 | padding: const EdgeInsets.all(20), 74 | textStyle: const TextStyle(fontSize: 14)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/Theme/theme_text_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ThemeTextStyle { 4 | BuildContext context; 5 | 6 | ThemeTextStyle({required this.context}); 7 | 8 | Color getBadgetTextColor(bool isSelected) { 9 | return Colors.white; 10 | } 11 | 12 | Color getHeadlineTextColor(bool isSelected) { 13 | if (Theme.of(context).brightness == Brightness.dark) { 14 | return isSelected 15 | ? const Color.fromARGB(255, 167, 200, 223) 16 | : Colors.white; 17 | } 18 | return isSelected 19 | ? Colors.white 20 | : Theme.of(context).textTheme.bodyMedium!.color!; 21 | } 22 | 23 | Color getHeadlineBackgroundColor(bool isSelected) { 24 | return isSelected ? Theme.of(context).primaryColorDark : Colors.transparent; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/UserSettings/Form/darkmode_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_bool_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/SubForm/radio_bool_list_subform.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class DarkmodeForm extends StatefulWidget { 8 | final UserSettingsEntity userSettings; 9 | final Function handleUpdateUserSettings; 10 | 11 | const DarkmodeForm( 12 | {super.key, 13 | required this.userSettings, 14 | required this.handleUpdateUserSettings}); 15 | 16 | @override 17 | State createState() => _DarkmodeFormState(); 18 | } 19 | 20 | class _DarkmodeFormState extends State { 21 | updateDarkMode(bool darkmodeEnabled) { 22 | widget.userSettings.setDarkModeEnabled(darkmodeEnabled); 23 | 24 | widget.handleUpdateUserSettings(widget.userSettings); 25 | } 26 | 27 | updateOverrideDarkMode(bool override) { 28 | widget.userSettings.setUserOverrideDarkMode(override); 29 | widget.handleUpdateUserSettings(widget.userSettings); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return Column( 35 | children: [ 36 | ListTile( 37 | title: Text( 38 | LocalizationApi().tr('DarkMode'), 39 | style: TextStyle( 40 | color: Theme.of(context).textTheme.headlineLarge!.color), 41 | ), 42 | ), 43 | Padding( 44 | padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), 45 | child: Column( 46 | children: [ 47 | RadioBoolListSubform( 48 | radioBoolEntityList: [ 49 | // RadioBoolEntity( 50 | // label: 'Use_system_darkmode', value: false), 51 | RadioBoolEntity(label: 'Override_darkmode', value: true) 52 | ], 53 | radioGroupValue: 54 | widget.userSettings.userOverrideDarkModeEnabled, 55 | handleUpdateValue: updateOverrideDarkMode), 56 | if (widget.userSettings.userOverrideDarkModeEnabled) 57 | Padding( 58 | padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), 59 | child: RadioBoolListSubform( 60 | radioBoolEntityList: [ 61 | RadioBoolEntity(label: 'Yes', value: true), 62 | RadioBoolEntity(label: 'No', value: false) 63 | ], 64 | radioGroupValue: 65 | widget.userSettings.getUserDarkModeEnabled(), 66 | handleUpdateValue: updateDarkMode)), 67 | ], 68 | )), 69 | ], 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/UserSettings/Form/flathubapi_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_bool_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/SubForm/radio_bool_list_subform.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class FlathubapiForm extends StatefulWidget { 8 | final UserSettingsEntity userSettings; 9 | final Function handleUpdateUserSettings; 10 | 11 | const FlathubapiForm( 12 | {super.key, 13 | required this.userSettings, 14 | required this.handleUpdateUserSettings}); 15 | 16 | @override 17 | State createState() => _FlathubapiFormState(); 18 | } 19 | 20 | class _FlathubapiFormState extends State { 21 | updateFlathubApi(bool flathubApiEnabled) { 22 | widget.userSettings.setFlathubApiEnabled(flathubApiEnabled); 23 | 24 | widget.handleUpdateUserSettings(widget.userSettings); 25 | } 26 | 27 | updateFlathubApiEnabled(bool flathubApiEnabled) { 28 | widget.userSettings.setFlathubApiEnabled(flathubApiEnabled); 29 | widget.handleUpdateUserSettings(widget.userSettings); 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return Column(children: [ 35 | ListTile( 36 | title: Text( 37 | LocalizationApi().tr('FlathubApiEnabled'), 38 | style: TextStyle( 39 | color: Theme.of(context).textTheme.headlineLarge!.color), 40 | ), 41 | ), 42 | Padding( 43 | padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), 44 | child: RadioBoolListSubform( 45 | radioBoolEntityList: [ 46 | RadioBoolEntity(label: 'Yes', value: true), 47 | RadioBoolEntity(label: 'No', value: false) 48 | ], 49 | radioGroupValue: widget.userSettings.getFlathubApiEnabled(), 50 | handleUpdateValue: updateFlathubApiEnabled)) 51 | ]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/UserSettings/Form/language_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_string_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/SubForm/radio_string_list_subform.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class LanguageForm extends StatefulWidget { 8 | final UserSettingsEntity userSettings; 9 | final Function handleUpdateUserSettings; 10 | final Function handleReloadLanguage; 11 | 12 | const LanguageForm( 13 | {super.key, 14 | required this.userSettings, 15 | required this.handleUpdateUserSettings, 16 | required this.handleReloadLanguage}); 17 | 18 | @override 19 | State createState() => _LanguageFormState(); 20 | } 21 | 22 | class _LanguageFormState extends State { 23 | updateLanguage(String language) { 24 | widget.userSettings.setLanguageCode(language); 25 | 26 | widget.handleUpdateUserSettings(widget.userSettings); 27 | 28 | if (widget.userSettings.userOverrideLanguageCode) { 29 | LocalizationApi().setLanguageCode(language); 30 | } 31 | } 32 | 33 | updateOverrideLanguage(bool override) { 34 | widget.userSettings.setUserOverrideLanguageCode(override); 35 | widget.handleUpdateUserSettings(widget.userSettings); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Column( 41 | children: [ 42 | ListTile( 43 | title: Text( 44 | LocalizationApi().tr('Language'), 45 | style: TextStyle( 46 | color: Theme.of(context).textTheme.headlineLarge!.color), 47 | ), 48 | ), 49 | Padding( 50 | padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), 51 | child: Column( 52 | children: [ 53 | ListTile( 54 | titleTextStyle: TextStyle( 55 | fontSize: 14, 56 | color: Theme.of(context).textTheme.headlineLarge!.color), 57 | title: Text(LocalizationApi().tr('Use_system_language')), 58 | leading: Radio( 59 | value: false, 60 | groupValue: widget.userSettings.userOverrideLanguageCode, 61 | onChanged: (bool? value) { 62 | updateOverrideLanguage(false); 63 | }, 64 | ), 65 | ), 66 | ListTile( 67 | titleTextStyle: TextStyle( 68 | fontSize: 14, 69 | color: Theme.of(context).textTheme.headlineLarge!.color), 70 | title: Text(LocalizationApi().tr('Override_language')), 71 | leading: Radio( 72 | value: true, 73 | groupValue: widget.userSettings.userOverrideLanguageCode, 74 | onChanged: (bool? value) { 75 | updateOverrideLanguage(true); 76 | }, 77 | ), 78 | ), 79 | if (widget.userSettings.userOverrideLanguageCode) 80 | Padding( 81 | padding: const EdgeInsets.fromLTRB(20, 0, 0, 0), 82 | child: RadioStringListSubform( 83 | radioStringEntityList: [ 84 | RadioStringEntity(label: 'English', value: 'en'), 85 | RadioStringEntity(label: 'French', value: 'fr'), 86 | RadioStringEntity(label: 'Italian', value: 'it'), 87 | RadioStringEntity(label: 'Spanish', value: 'es'), 88 | RadioStringEntity(label: 'Brazilian', value: 'br'), 89 | RadioStringEntity(label: 'Arabic', value: 'ar'), 90 | ], 91 | value: widget.userSettings.getUserLanguageCode(), 92 | handleUpdateValue: updateLanguage), 93 | ) 94 | ], 95 | ), 96 | ) 97 | ], 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/UserSettings/Form/parameter_page_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_bool_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/SubForm/radio_bool_list_subform.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class ParameterPageForm extends StatefulWidget { 8 | final UserSettingsEntity userSettings; 9 | final Function handleUpdateUserSettings; 10 | 11 | const ParameterPageForm( 12 | {super.key, 13 | required this.userSettings, 14 | required this.handleUpdateUserSettings}); 15 | @override 16 | State createState() => _ParameterPageFormState(); 17 | } 18 | 19 | class _ParameterPageFormState extends State { 20 | setDisplayApplicationInstalledNumberInSideMenu( 21 | bool displayNumberOfInstalledAppsInSideMenu) { 22 | widget.userSettings.setDisplayApplicationInstalledNumberInSideMenu( 23 | displayNumberOfInstalledAppsInSideMenu); 24 | 25 | widget.handleUpdateUserSettings(widget.userSettings); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Column(children: [ 31 | ListTile( 32 | title: Text( 33 | LocalizationApi().tr('Display_number_of_installedapps_in_sidemenu'), 34 | style: TextStyle( 35 | color: Theme.of(context).textTheme.headlineLarge!.color), 36 | ), 37 | ), 38 | Padding( 39 | padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), 40 | child: RadioBoolListSubform( 41 | radioBoolEntityList: [ 42 | RadioBoolEntity(label: 'Yes', value: true), 43 | RadioBoolEntity(label: 'No', value: false) 44 | ], 45 | radioGroupValue: widget.userSettings 46 | .getDisplayApplicationInstalledNumberInSideMenu(), 47 | handleUpdateValue: setDisplayApplicationInstalledNumberInSideMenu), 48 | ) 49 | ]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/UserSettings/Form/scope_form.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/radio_bool_entity.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/SubForm/radio_bool_list_subform.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class ScopeForm extends StatefulWidget { 8 | final UserSettingsEntity userSettings; 9 | final Function handleUpdateUserSettings; 10 | 11 | const ScopeForm( 12 | {super.key, 13 | required this.userSettings, 14 | required this.handleUpdateUserSettings}); 15 | @override 16 | State createState() => _ScopeFormState(); 17 | } 18 | 19 | class _ScopeFormState extends State { 20 | updateUserInstallationScope(bool userInstallationScope) { 21 | widget.userSettings.setUserInstallationScopeEnabled(userInstallationScope); 22 | 23 | widget.handleUpdateUserSettings(widget.userSettings); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Column(children: [ 29 | ListTile( 30 | title: Text( 31 | LocalizationApi().tr('Installation_scope'), 32 | style: TextStyle( 33 | color: Theme.of(context).textTheme.headlineLarge!.color), 34 | ), 35 | ), 36 | Padding( 37 | padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), 38 | child: RadioBoolListSubform( 39 | radioBoolEntityList: [ 40 | RadioBoolEntity(label: 'scopeSystem', value: false), 41 | RadioBoolEntity(label: 'scopeUser', value: true) 42 | ], 43 | radioGroupValue: 44 | widget.userSettings.getUserInstallationScopeEnabled(), 45 | handleUpdateValue: updateUserInstallationScope)) 46 | ]); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/about_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | 5 | class AboutView extends StatelessWidget { 6 | final String version; 7 | 8 | const AboutView({super.key, required this.version}); 9 | 10 | Widget getLine( 11 | {required BuildContext context, 12 | required String title, 13 | required Widget content}) { 14 | return Column( 15 | crossAxisAlignment: CrossAxisAlignment.start, 16 | children: [ 17 | ListTile( 18 | title: Text( 19 | LocalizationApi().tr(title), 20 | style: TextStyle( 21 | color: Theme.of(context).textTheme.headlineLarge!.color), 22 | ), 23 | ), 24 | Padding( 25 | padding: const EdgeInsets.fromLTRB(25, 0, 0, 0), 26 | child: content, 27 | ), 28 | ], 29 | ); 30 | } 31 | 32 | Widget getSpace() { 33 | return const SizedBox( 34 | height: 10, 35 | ); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Column( 41 | crossAxisAlignment: CrossAxisAlignment.start, 42 | children: [ 43 | getLine( 44 | context: context, 45 | title: 'Author', 46 | content: const Text(' Michael Bertocchi')), 47 | getSpace(), 48 | getLine( 49 | context: context, 50 | title: 'Website', 51 | content: TextButton( 52 | child: const Text('www.dupot.org'), 53 | onPressed: () { 54 | launchUrl(Uri.parse('https://www.dupot.org')); 55 | }, 56 | ), 57 | ), 58 | getLine( 59 | context: context, 60 | title: 'License', 61 | content: const Text('LGPL-2.1')), 62 | getSpace(), 63 | getLine(context: context, title: 'Version', content: Text(version)), 64 | getSpace(), 65 | ], 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/bundles_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/bundle_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/bundle_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class BundlesView extends StatefulWidget { 8 | final Function handleGoTo; 9 | final bool isMain; 10 | final String bundleId; 11 | 12 | const BundlesView( 13 | {super.key, 14 | required this.handleGoTo, 15 | required this.isMain, 16 | required this.bundleId}); 17 | 18 | @override 19 | State createState() => _BundlesViewState(); 20 | } 21 | 22 | class _BundlesViewState extends State { 23 | List stateBundleEntityList = []; 24 | 25 | Map stateCheckboxList = {}; 26 | 27 | ScrollController scrollController = ScrollController(); 28 | 29 | @override 30 | void initState() { 31 | loadData(); 32 | 33 | super.initState(); 34 | } 35 | 36 | Future loadData() async { 37 | List bundleEntityList = 38 | await BundleApi().getBundleEntityList(); 39 | 40 | setState(() { 41 | stateBundleEntityList = bundleEntityList; 42 | }); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return Scrollbar( 48 | interactive: false, 49 | thumbVisibility: true, 50 | controller: scrollController, 51 | child: Column(children: [ 52 | Expanded( 53 | child: ListView( 54 | controller: scrollController, 55 | children: stateBundleEntityList 56 | .map((BundleEntity bundleEntity) => getLine(bundleEntity)) 57 | .toList())) 58 | ])); 59 | } 60 | 61 | Widget getLine(BundleEntity bundleEntity) { 62 | return Card( 63 | color: widget.bundleId == bundleEntity.name 64 | ? Theme.of(context).primaryColorLight 65 | : Theme.of(context).secondaryHeaderColor, 66 | child: ListTile( 67 | enabled: widget.isMain, 68 | onTap: () => widget.isMain 69 | ? NavigationEntity.goToBundleDetail( 70 | handleGoTo: widget.handleGoTo, bundleId: bundleEntity.name) 71 | : null, 72 | title: Column( 73 | children: [ 74 | Row( 75 | children: [ 76 | const SizedBox(width: 10), 77 | Image.asset(height: 60, bundleEntity.icon), 78 | const SizedBox(width: 20), 79 | Expanded( 80 | child: Column( 81 | crossAxisAlignment: CrossAxisAlignment.start, 82 | children: [ 83 | Text( 84 | LocalizationApi().tr("bundle_${bundleEntity.name}"), 85 | style: TextStyle( 86 | fontSize: 26, 87 | color: Theme.of(context) 88 | .textTheme 89 | .headlineLarge! 90 | .color), 91 | ), 92 | ], 93 | ), 94 | ), 95 | ], 96 | ), 97 | ], 98 | ), 99 | )); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/category_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Repository/application_repository.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/datatable_application_list_component.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/grid_application_list_component.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/listview_application_list_component.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | class CategoryView extends StatefulWidget { 11 | final Function handleGoTo; 12 | final String categoryIdSelected; 13 | 14 | const CategoryView( 15 | {super.key, required this.handleGoTo, required this.categoryIdSelected}); 16 | 17 | @override 18 | State createState() => _CategoryViewState(); 19 | } 20 | 21 | class _CategoryViewState extends State { 22 | List stateAppStreamList = []; 23 | List stateCategoryIdList = []; 24 | String appPath = ''; 25 | 26 | String lastCategoryIdSelected = ''; 27 | 28 | ScrollController scrollController = ScrollController(); 29 | 30 | Set _segmentedButtonSelection = { 31 | UserSettingsEntity().getDisplayAppsMode() 32 | }; 33 | 34 | @override 35 | void initState() { 36 | loadData(); 37 | 38 | super.initState(); 39 | } 40 | 41 | @override 42 | void didUpdateWidget(covariant CategoryView oldWidget) { 43 | if (oldWidget.categoryIdSelected != widget.categoryIdSelected) { 44 | loadData(); 45 | } 46 | 47 | super.didUpdateWidget(oldWidget); 48 | } 49 | 50 | Future loadData() async { 51 | lastCategoryIdSelected = widget.categoryIdSelected; 52 | ApplicationRepository appStreamFactory = ApplicationRepository(); 53 | 54 | List appStreamList = await appStreamFactory 55 | .findListApplicationEntityByCategory(widget.categoryIdSelected); 56 | 57 | setState(() { 58 | stateAppStreamList = appStreamList; 59 | }); 60 | if (scrollController.hasClients) { 61 | scrollController.jumpTo(0); 62 | } 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 68 | 69 | return stateAppStreamList.isEmpty 70 | ? const LinearProgressIndicator() 71 | : Column( 72 | children: [ 73 | Padding( 74 | padding: const EdgeInsets.all(10.0), 75 | child: Row(children: [ 76 | const Expanded(child: SizedBox()), 77 | SegmentedButton( 78 | style: themeButtonStyle.getSegmentedButtonStyle(), 79 | 80 | // ToggleButtons above allows multiple or no selection. 81 | // Set `multiSelectionEnabled` and `emptySelectionAllowed` to true 82 | // to match the behavior of ToggleButtons. 83 | multiSelectionEnabled: false, 84 | emptySelectionAllowed: false, 85 | 86 | // Hide the selected icon to match the behavior of ToggleButtons. 87 | showSelectedIcon: true, 88 | // SegmentedButton uses a Set to track its selection state. 89 | selected: _segmentedButtonSelection, 90 | // This callback updates the set of selected segment values. 91 | onSelectionChanged: (Set newSelection) { 92 | UserSettingsEntity() 93 | .setDisplayAppsMode(newSelection.first); 94 | setState(() { 95 | _segmentedButtonSelection = newSelection; 96 | }); 97 | }, 98 | // SegmentedButton uses a List> to build its children 99 | // instead of a List like ToggleButtons. 100 | segments: appDisplayOptions 101 | .map>( 102 | ((AppDisplay, IconData) shirt) { 103 | return ButtonSegment( 104 | value: shirt.$1, label: Icon(shirt.$2)); 105 | }).toList(), 106 | ), 107 | const SizedBox( 108 | width: 10, 109 | ) 110 | ])), 111 | Expanded(child: getContent()) 112 | ], 113 | ); 114 | } 115 | 116 | Widget getContent() { 117 | if (_segmentedButtonSelection.first == AppDisplay.grid) { 118 | return GridApplicationListComponent( 119 | applicationEntityList: stateAppStreamList, 120 | handleGoTo: widget.handleGoTo, 121 | handleScrollController: scrollController); 122 | } 123 | if (_segmentedButtonSelection.first == AppDisplay.list) { 124 | return ListviewApplicationListComponent( 125 | applicationEntityList: stateAppStreamList, 126 | handleGoTo: widget.handleGoTo, 127 | handleScrollController: scrollController); 128 | } 129 | 130 | return DatatableApplicationListComponent( 131 | applicationEntityList: stateAppStreamList, 132 | handleGoTo: widget.handleGoTo, 133 | handleScrollController: scrollController); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/home_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Control/Model/View/home_view_model.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Group/block_app_list_component.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class HomeView extends StatefulWidget { 7 | final Function handleGoTo; 8 | 9 | const HomeView({super.key, required this.handleGoTo}); 10 | 11 | @override 12 | State createState() => _HomeViewState(); 13 | } 14 | 15 | class _HomeViewState extends State { 16 | List> stateAppStreamListList = []; 17 | List stateCategoryIdList = []; 18 | String appPath = ''; 19 | 20 | ScrollController scrollController = ScrollController(); 21 | 22 | @override 23 | void initState() { 24 | loadData(); 25 | 26 | super.initState(); 27 | } 28 | 29 | Future loadData() async { 30 | HomeViewModel homeViewModel = HomeViewModel(); 31 | 32 | List> appStreamListList = 33 | await homeViewModel.getApplicationEntityList(); 34 | 35 | List categoryIdList = await homeViewModel.getCategoryIdList(); 36 | 37 | setState(() { 38 | stateAppStreamListList = appStreamListList; 39 | stateCategoryIdList = categoryIdList; 40 | }); 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return stateAppStreamListList.isEmpty 46 | ? const LinearProgressIndicator() 47 | : Scrollbar( 48 | interactive: false, 49 | thumbVisibility: true, 50 | controller: scrollController, 51 | child: ListView.builder( 52 | itemCount: stateCategoryIdList.length, 53 | controller: scrollController, 54 | itemBuilder: (context, index) { 55 | return BlockAppListComponent( 56 | categoryId: stateCategoryIdList[index], 57 | appStreamList: stateAppStreamListList[index], 58 | appPath: appPath, 59 | handleGoTo: widget.handleGoTo); 60 | })); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/installed_applications_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Repository/application_repository.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/datatable_application_list_component.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/grid_application_list_component.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/List/listview_application_list_component.dart'; 9 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/Theme/theme_button_style.dart'; 10 | import 'package:flutter/material.dart'; 11 | 12 | class InstalledApplicationsView extends StatefulWidget { 13 | final Function handleGoTo; 14 | const InstalledApplicationsView({ 15 | super.key, 16 | required this.handleGoTo, 17 | }); 18 | 19 | @override 20 | State createState() => 21 | _InstalledApplicationsViewState(); 22 | } 23 | 24 | class _InstalledApplicationsViewState extends State { 25 | List stateAppStreamList = []; 26 | 27 | String stateSearch = ''; 28 | 29 | String lastCategoryIdSelected = ''; 30 | 31 | final ScrollController scrollController = ScrollController(); 32 | 33 | Set _segmentedButtonSelection = { 34 | UserSettingsEntity().getDisplayAppsMode() 35 | }; 36 | @override 37 | void initState() { 38 | super.initState(); 39 | 40 | loadData(); 41 | } 42 | 43 | Future loadData() async { 44 | CommandApi commands = CommandApi(); 45 | 46 | List installedApplicationIdList = 47 | await commands.getInstalledApplicationList(); 48 | 49 | ApplicationRepository applicationRepository = ApplicationRepository(); 50 | 51 | List applicationEntityList = await applicationRepository 52 | .findListApplicationEntityByIdList(installedApplicationIdList); 53 | 54 | setState(() { 55 | stateAppStreamList = applicationEntityList; 56 | }); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | ThemeButtonStyle themeButtonStyle = ThemeButtonStyle(context: context); 62 | 63 | return Column( 64 | children: [ 65 | Padding( 66 | padding: const EdgeInsets.all(10.0), 67 | child: Row(children: [ 68 | Text( 69 | "${LocalizationApi().tr('Total')} : ${stateAppStreamList.length.toString()}", 70 | style: const TextStyle(fontSize: 18), 71 | ), 72 | const Expanded(child: SizedBox()), 73 | SegmentedButton( 74 | style: themeButtonStyle.getSegmentedButtonStyle(), 75 | 76 | // ToggleButtons above allows multiple or no selection. 77 | // Set `multiSelectionEnabled` and `emptySelectionAllowed` to true 78 | // to match the behavior of ToggleButtons. 79 | multiSelectionEnabled: false, 80 | emptySelectionAllowed: false, 81 | 82 | // Hide the selected icon to match the behavior of ToggleButtons. 83 | showSelectedIcon: true, 84 | // SegmentedButton uses a Set to track its selection state. 85 | selected: _segmentedButtonSelection, 86 | // This callback updates the set of selected segment values. 87 | onSelectionChanged: (Set newSelection) { 88 | UserSettingsEntity().setDisplayAppsMode(newSelection.first); 89 | setState(() { 90 | _segmentedButtonSelection = newSelection; 91 | }); 92 | }, 93 | // SegmentedButton uses a List> to build its children 94 | // instead of a List like ToggleButtons. 95 | segments: appDisplayOptions.map>( 96 | ((AppDisplay, IconData) shirt) { 97 | return ButtonSegment( 98 | value: shirt.$1, label: Icon(shirt.$2)); 99 | }).toList(), 100 | ), 101 | const SizedBox( 102 | width: 10, 103 | ) 104 | ])), 105 | Expanded(child: getContent()) 106 | ], 107 | ); 108 | } 109 | 110 | Widget getContent() { 111 | if (_segmentedButtonSelection.first == AppDisplay.grid) { 112 | return GridApplicationListComponent( 113 | applicationEntityList: stateAppStreamList, 114 | handleGoTo: widget.handleGoTo, 115 | handleScrollController: scrollController); 116 | } 117 | if (_segmentedButtonSelection.first == AppDisplay.list) { 118 | return ListviewApplicationListComponent( 119 | applicationEntityList: stateAppStreamList, 120 | handleGoTo: widget.handleGoTo, 121 | handleScrollController: scrollController); 122 | } 123 | 124 | return DatatableApplicationListComponent( 125 | applicationEntityList: stateAppStreamList, 126 | handleGoTo: widget.handleGoTo, 127 | handleScrollController: scrollController); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/loading_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Api/flathub_api.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Control/Process/update_from_flathub_process.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Repository/application_repository.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | class LoadingView extends StatefulWidget { 11 | final Function handle; 12 | 13 | const LoadingView({super.key, required this.handle}); 14 | 15 | @override 16 | State createState() => _LoadingView(); 17 | } 18 | 19 | class _LoadingView extends State with TickerProviderStateMixin { 20 | bool isLoaded = false; 21 | 22 | double progressValue = 0.0; 23 | 24 | String stateLoadingInfo = ''; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | 30 | processInit(); 31 | } 32 | 33 | Future processInit() async { 34 | setState(() { 35 | progressValue = 0.1; 36 | }); 37 | 38 | await LocalizationApi().load(); 39 | setState(() { 40 | progressValue = 0.8; 41 | }); 42 | 43 | setState(() { 44 | stateLoadingInfo = LocalizationApi().tr('loading_Check_installation'); 45 | }); 46 | 47 | LoggerApi().info('Starting installation'); 48 | UpdateFromFlathubProcess updateFromFlathubProcess = 49 | UpdateFromFlathubProcess(commandApi: CommandApi()); 50 | await updateFromFlathubProcess.process(); 51 | LoggerApi().info('Installation complete'); 52 | 53 | setState(() { 54 | stateLoadingInfo = LocalizationApi().tr('loading_Installation_ok'); 55 | }); 56 | 57 | setState(() { 58 | progressValue = 0.20; 59 | }); 60 | 61 | final applicatoinRepository = ApplicationRepository(); 62 | //await appStreamFactory.create(); 63 | 64 | LoggerApi().info('Starting flathub load'); 65 | setState(() { 66 | stateLoadingInfo = LocalizationApi() 67 | .tr('loading_Should_update_application_list_from_Flathub_api'); 68 | }); 69 | UserSettingsEntity userSettingsEntity = UserSettingsEntity(); 70 | 71 | if (userSettingsEntity.isFlathubApiEnabled() && 72 | userSettingsEntity.shouldUpdateApplicationsFromApi()) { 73 | setState(() { 74 | stateLoadingInfo = LocalizationApi() 75 | .tr('loading_Starting_update_application_list_from_Flathub_api'); 76 | }); 77 | 78 | FlathubApi flathubApi = 79 | FlathubApi(applicationRepository: applicatoinRepository); 80 | await flathubApi.load(); 81 | 82 | userSettingsEntity.updateLasttimeStampUpdateApplicationsFromApi(); 83 | 84 | UserSettingsEntity().save(); 85 | 86 | setState(() { 87 | stateLoadingInfo = LocalizationApi() 88 | .tr('loading_Update_application_list_from_Flathub_api_finished'); 89 | }); 90 | } else { 91 | setState(() { 92 | stateLoadingInfo = LocalizationApi().tr( 93 | 'loading_Last_applications_updates_from_API_is_newer_than_7_days_not_need'); 94 | }); 95 | } 96 | 97 | LoggerApi().info('Flathub load complete'); 98 | 99 | setState(() { 100 | progressValue = 0.50; 101 | }); 102 | 103 | if (await CommandApi().missFlathubInFlatpak()) { 104 | LoggerApi().info('Need flathub setup'); 105 | setState(() { 106 | progressValue = 0.6; 107 | }); 108 | await CommandApi().setupFlathub(); 109 | } else { 110 | LoggerApi().info('Flathub already setup'); 111 | } 112 | 113 | List dbApplicationIdList = 114 | await applicatoinRepository.findAllApplicationIdList(); 115 | 116 | setState(() { 117 | stateLoadingInfo = 118 | LocalizationApi().tr('loading_Looking_for_applications_updates'); 119 | }); 120 | CommandApi().setDbApplicationIdList(dbApplicationIdList); 121 | await CommandApi().loadApplicationInstalledList(); 122 | setState(() { 123 | progressValue = 0.8; 124 | }); 125 | await CommandApi().checkUpdates(); 126 | 127 | setState(() { 128 | progressValue = 1; 129 | }); 130 | 131 | widget.handle(); 132 | } 133 | 134 | @override 135 | Widget build(BuildContext context) { 136 | return Scaffold( 137 | backgroundColor: Theme.of(context).primaryColorLight, 138 | body: Center( 139 | child: Column( 140 | children: [ 141 | const SizedBox( 142 | height: 200, 143 | ), 144 | Image.asset( 145 | 'assets/logos/splash.png', 146 | width: 200, 147 | ), 148 | LinearProgressIndicator( 149 | value: progressValue, 150 | color: Theme.of(context).primaryColorDark, 151 | ), 152 | SizedBox( 153 | height: 10, 154 | ), 155 | Text(stateLoadingInfo) 156 | ], 157 | ), 158 | )); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/moreactions_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Entity/navigation_entity.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class MoreActionsView extends StatefulWidget { 6 | final Function handleGoTo; 7 | 8 | const MoreActionsView({super.key, required this.handleGoTo}); 9 | 10 | @override 11 | State createState() => _MoreActionsViewState(); 12 | } 13 | 14 | class _MoreActionsViewState extends State { 15 | ScrollController scrollController = ScrollController(); 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | } 21 | 22 | Widget getLine(Function functionToCall, IconData actionIcon, String label, 23 | String summary) { 24 | return InkWell( 25 | borderRadius: BorderRadius.circular(8.0), 26 | onTap: () => functionToCall(), 27 | child: Card( 28 | color: Theme.of(context).primaryColorLight, 29 | child: Column( 30 | children: [ 31 | Row( 32 | children: [ 33 | const SizedBox(width: 10), 34 | Icon(actionIcon), 35 | const SizedBox(width: 20), 36 | Expanded( 37 | child: Column( 38 | crossAxisAlignment: CrossAxisAlignment.start, 39 | children: [ 40 | Text( 41 | label, 42 | style: TextStyle( 43 | fontSize: 24, 44 | color: Theme.of(context) 45 | .textTheme 46 | .headlineLarge! 47 | .color), 48 | ), 49 | Text( 50 | summary, 51 | ) 52 | ], 53 | ), 54 | ) 55 | ], 56 | ), 57 | ], 58 | ), 59 | )); 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | return Scrollbar( 65 | interactive: false, 66 | thumbVisibility: true, 67 | controller: scrollController, 68 | child: ListView(controller: scrollController, children: [ 69 | getLine(() { 70 | return NavigationEntity.goToMoreExport( 71 | handleGoTo: widget.handleGoTo); 72 | }, Icons.upload, LocalizationApi().tr('Export'), 73 | LocalizationApi().tr('Export_installed_apps')), 74 | getLine(() { 75 | return NavigationEntity.goToMoreImport( 76 | handleGoTo: widget.handleGoTo); 77 | }, Icons.download, LocalizationApi().tr('Import'), 78 | LocalizationApi().tr('Import_installed_apps_from_json')) 79 | ])); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/reload_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ReloadView extends StatefulWidget { 4 | final Function handle; 5 | 6 | const ReloadView({super.key, required this.handle}); 7 | 8 | @override 9 | State createState() => _ReloadViewState(); 10 | } 11 | 12 | class _ReloadViewState extends State { 13 | @override 14 | void initState() { 15 | super.initState(); 16 | } 17 | 18 | @override 19 | void dispose() { 20 | processInit(); 21 | super.dispose(); 22 | } 23 | 24 | Future processInit() async { 25 | widget.handle(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return const SizedBox(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/search_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/db/application_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Control/Model/View/search_view_model.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/SharedComponents/Content/application_list_content.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class SearchView extends StatefulWidget { 8 | final Function handleGoTo; 9 | final String searched; 10 | 11 | const SearchView( 12 | {super.key, required this.handleGoTo, required this.searched}); 13 | 14 | @override 15 | State createState() => _SearchViewState(); 16 | } 17 | 18 | class _SearchViewState extends State { 19 | List stateAppStreamList = []; 20 | 21 | String stateSearch = ''; 22 | 23 | String lastCategoryIdSelected = ''; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | 29 | loadData(); 30 | } 31 | 32 | @override 33 | void didUpdateWidget(SearchView oldWidget) { 34 | if (oldWidget.searched != widget.searched) { 35 | loadData(); 36 | } 37 | super.didUpdateWidget(oldWidget); 38 | } 39 | 40 | Future loadData() async { 41 | List applicationEntityList = await SearchViewModel() 42 | .getApplicationEntityListBySearch(widget.searched); 43 | setState(() { 44 | stateAppStreamList = applicationEntityList; 45 | }); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Column(children: [ 51 | Expanded( 52 | child: stateAppStreamList.isEmpty 53 | ? SizedBox( 54 | child: Center( 55 | child: Text(LocalizationApi() 56 | .tr('No_result_corresponding_to_this_research'))), 57 | ) 58 | : ApplicationListContent( 59 | handleGoTo: widget.handleGoTo, 60 | applicationEntityList: stateAppStreamList)) 61 | ]); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Infrastructure/Screen/View/user_settings_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 2 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/View/UserSettings/Form/darkmode_form.dart'; 3 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/View/UserSettings/Form/flathubapi_form.dart'; 4 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/View/UserSettings/Form/language_form.dart'; 5 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/View/UserSettings/Form/parameter_page_form.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Screen/View/UserSettings/Form/scope_form.dart'; 7 | 8 | import 'package:flutter/material.dart'; 9 | 10 | class UserSettingsView extends StatefulWidget { 11 | final Function handleGoTo; 12 | final Function handleReload; 13 | final Function handleReloadLanguage; 14 | 15 | const UserSettingsView( 16 | {super.key, 17 | required this.handleGoTo, 18 | required this.handleReload, 19 | required this.handleReloadLanguage}); 20 | 21 | @override 22 | State createState() => _UserSettingsViewState(); 23 | } 24 | 25 | class _UserSettingsViewState extends State { 26 | ScrollController scrollController = ScrollController(); 27 | 28 | bool isUserSettingsLoaded = false; 29 | late UserSettingsEntity stateUserSettingsEntity; 30 | 31 | @override 32 | void initState() { 33 | loadData(); 34 | 35 | super.initState(); 36 | } 37 | 38 | Future loadData() async { 39 | setState(() { 40 | stateUserSettingsEntity = UserSettingsEntity(); 41 | isUserSettingsLoaded = true; 42 | }); 43 | } 44 | 45 | updateStateUserSettings(UserSettingsEntity newUserSettings) { 46 | setState(() { 47 | stateUserSettingsEntity = newUserSettings; 48 | }); 49 | 50 | widget.handleReload(); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | //language 56 | //darkmode 57 | //scope install 58 | //installed app page (badge) 59 | return !isUserSettingsLoaded 60 | ? const LinearProgressIndicator() 61 | : Scrollbar( 62 | interactive: false, 63 | thumbVisibility: true, 64 | controller: scrollController, 65 | child: ListView( 66 | controller: scrollController, 67 | children: [ 68 | Column( 69 | children: [ 70 | LanguageForm( 71 | userSettings: stateUserSettingsEntity, 72 | handleUpdateUserSettings: updateStateUserSettings, 73 | handleReloadLanguage: widget.handleReloadLanguage), 74 | DarkmodeForm( 75 | userSettings: stateUserSettingsEntity, 76 | handleUpdateUserSettings: updateStateUserSettings), 77 | ScopeForm( 78 | userSettings: stateUserSettingsEntity, 79 | handleUpdateUserSettings: updateStateUserSettings), 80 | ParameterPageForm( 81 | userSettings: stateUserSettingsEntity, 82 | handleUpdateUserSettings: updateStateUserSettings), 83 | FlathubapiForm( 84 | userSettings: stateUserSettingsEntity, 85 | handleUpdateUserSettings: updateStateUserSettings) 86 | ], 87 | ) 88 | ], 89 | )); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/Infrastructure/Service/localisation_delegate_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | 5 | class LocalisationDelegateService 6 | extends LocalizationsDelegate { 7 | const LocalisationDelegateService(); 8 | 9 | @override 10 | bool isSupported(Locale locale) => 11 | LocalizationApi.languages().contains(locale.languageCode); 12 | 13 | @override 14 | Future load(Locale locale) { 15 | // Returning a SynchronousFuture here because an async "load" operation 16 | // isn't needed to produce an instance of AppLocalizations. 17 | 18 | return SynchronousFuture( 19 | LocalizationApi(newLanguageCode: locale.languageCode)); 20 | } 21 | 22 | @override 23 | bool shouldReload(LocalisationDelegateService old) => false; 24 | } 25 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:dupot_easy_flatpak/Domain/Entity/settings_entity.dart'; 5 | import 'package:dupot_easy_flatpak/Domain/Entity/user_settings_entity.dart'; 6 | import 'package:dupot_easy_flatpak/Infrastructure/Api/command_api.dart'; 7 | import 'package:dupot_easy_flatpak/Infrastructure/Api/localization_api.dart'; 8 | import 'package:dupot_easy_flatpak/Infrastructure/Api/logger_api.dart'; 9 | import 'package:dupot_easy_flatpak/Infrastructure/Api/path_api.dart'; 10 | import 'package:dupot_easy_flatpak/Infrastructure/application.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:package_info_plus/package_info_plus.dart'; 14 | 15 | import 'package:sqflite_common_ffi/sqflite_ffi.dart'; 16 | import 'package:path/path.dart' as p; 17 | import 'package:window_manager/window_manager.dart'; 18 | 19 | void main() async { 20 | try { 21 | WidgetsFlutterBinding.ensureInitialized(); 22 | 23 | await windowManager.ensureInitialized(); 24 | 25 | Directory configDirectory = Directory(PathApi.getConfigPath()); 26 | Directory cacheDirectory = Directory(PathApi.getCachePath()); 27 | Directory logDirectory = Directory(PathApi.getLogPath()); 28 | Directory iconsCacheDirectory = Directory(PathApi.getIconsCachePath()); 29 | Directory importConfigDirectory = Directory(PathApi.getImportConfigPath()); 30 | Directory exportConfigDirectory = Directory(PathApi.getExportConfigPath()); 31 | 32 | for (Directory mandatoryDirectoryLoop in [ 33 | configDirectory, 34 | cacheDirectory, 35 | logDirectory, 36 | iconsCacheDirectory, 37 | importConfigDirectory, 38 | exportConfigDirectory 39 | ]) { 40 | if (!mandatoryDirectoryLoop.existsSync()) { 41 | mandatoryDirectoryLoop.createSync(recursive: true); 42 | } 43 | } 44 | 45 | LoggerApi(File(p.join(logDirectory.path, 'application.log'))); 46 | 47 | bool shouldCopyDb = false; 48 | bool shouldCopyUserSettings = false; 49 | 50 | PackageInfo packageInfo = await PackageInfo.fromPlatform(); 51 | 52 | File buildInstalled = File(PathApi.getBuildConfigPath()); 53 | 54 | if (!buildInstalled.existsSync()) { 55 | shouldCopyDb = true; 56 | shouldCopyUserSettings = true; 57 | } else { 58 | String buildInfo = buildInstalled.readAsStringSync(); 59 | if (buildInfo == packageInfo.version) { 60 | LoggerApi().info('Build installed is the latest ($buildInfo)'); 61 | } else { 62 | LoggerApi().info( 63 | 'Build installed $buildInfo different from current ${packageInfo.version}'); 64 | shouldCopyDb = true; 65 | } 66 | } 67 | 68 | File userSettingsFile = File(PathApi.getUserSettingsJsonConfigPath()); 69 | if (!userSettingsFile.existsSync()) { 70 | shouldCopyDb = true; 71 | shouldCopyUserSettings = true; 72 | } else { 73 | String userSettingsString = userSettingsFile.readAsStringSync(); 74 | Map userSettingsObj = jsonDecode(userSettingsString); 75 | 76 | String jsonDefaultUserSettingsString = 77 | await rootBundle.loadString('assets/json/userSettings.json'); 78 | Map defaultUserSettingObj = 79 | jsonDecode(jsonDefaultUserSettingsString); 80 | if (!userSettingsObj.containsKey('version')) { 81 | shouldCopyUserSettings = true; 82 | } else if (defaultUserSettingObj['version'] == 2 && 83 | userSettingsObj['version'] == 1) { 84 | userSettingsObj['flathubApiEnabled'] = true; 85 | } else if (defaultUserSettingObj['version'] != 86 | userSettingsObj['version']) { 87 | shouldCopyUserSettings = true; 88 | } 89 | } 90 | 91 | if (shouldCopyDb) { 92 | await copyAssetFilePath('db/flathub_database.db', PathApi.getCachePath()); 93 | } 94 | if (shouldCopyUserSettings) { 95 | await copyAssetFilePath( 96 | 'json/userSettings.json', PathApi.getConfigPath()); 97 | } 98 | 99 | UserSettingsEntity userSettings = UserSettingsEntity(userSettingsFile.path); 100 | 101 | if (userSettings.userOverrideLanguageCode) { 102 | LocalizationApi().setLanguageCode(userSettings.getUserLanguageCode()); 103 | } else { 104 | LocalizationApi().setLanguageCode(Platform.localeName); 105 | } 106 | 107 | Settings settingsObj = Settings(); 108 | settingsObj.load().then((value) { 109 | CommandApi(settingsObj); 110 | }); 111 | 112 | LoggerApi().info('Starting application'); 113 | 114 | sqfliteFfiInit(); 115 | 116 | databaseFactory = databaseFactoryFfi; 117 | 118 | WindowOptions windowOptions = WindowOptions( 119 | size: Size(1280, 800), 120 | center: true, 121 | backgroundColor: Colors.transparent, 122 | skipTaskbar: false, 123 | titleBarStyle: TitleBarStyle.normal, 124 | ); 125 | windowManager.waitUntilReadyToShow(windowOptions, () async { 126 | await windowManager.show(); 127 | await windowManager.focus(); 128 | }); 129 | 130 | runApp(const Application()); 131 | } on Exception catch (e) { 132 | LoggerApi().error('Exception: $e'); 133 | } 134 | } 135 | 136 | Future copyAssetFilePath(String filePath, String targetPath) async { 137 | LoggerApi().info('Starting copy of $filePath'); 138 | final bytes = await rootBundle.load('assets/$filePath'); 139 | final targetFile = File('$targetPath/${p.basename(filePath)}'); 140 | await targetFile.writeAsBytes(bytes.buffer.asUint8List()); 141 | LoggerApi().info('Finished copying $filePath'); 142 | } 143 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void fl_register_plugins(FlPluginRegistry* registry) { 14 | g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = 15 | fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); 16 | screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); 17 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 18 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 19 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 20 | g_autoptr(FlPluginRegistrar) window_manager_registrar = 21 | fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); 22 | window_manager_plugin_register_with_registrar(window_manager_registrar); 23 | } 24 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | screen_retriever_linux 7 | url_launcher_linux 8 | window_manager 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication 11 | { 12 | GtkApplication parent_instance; 13 | char **dart_entrypoint_arguments; 14 | }; 15 | 16 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 17 | 18 | // Implements GApplication::activate. 19 | static void my_application_activate(GApplication *application) 20 | { 21 | MyApplication *self = MY_APPLICATION(application); 22 | GtkWindow *window = 23 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 24 | 25 | // Use a header bar when running in GNOME as this is the common style used 26 | // by applications and is the setup most users will be using (e.g. Ubuntu 27 | // desktop). 28 | // If running on X and not using GNOME then just use a traditional title bar 29 | // in case the window manager does more exotic layout, e.g. tiling. 30 | // If running on Wayland assume the header bar will work (may need changing 31 | // if future cases occur). 32 | gboolean use_header_bar = TRUE; 33 | #ifdef GDK_WINDOWING_X11 34 | GdkScreen *screen = gtk_window_get_screen(window); 35 | if (GDK_IS_X11_SCREEN(screen)) 36 | { 37 | const gchar *wm_name = gdk_x11_screen_get_window_manager_name(screen); 38 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) 39 | { 40 | use_header_bar = FALSE; 41 | } 42 | } 43 | #endif 44 | if (use_header_bar) 45 | { 46 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 47 | gtk_widget_show(GTK_WIDGET(header_bar)); 48 | gtk_header_bar_set_title(header_bar, "Easy Flatpak"); 49 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 50 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 51 | } 52 | else 53 | { 54 | gtk_window_set_title(window, "Easy Flatpak"); 55 | } 56 | 57 | gtk_window_set_default_size(window, 1280, 730); 58 | gtk_widget_show(GTK_WIDGET(window)); 59 | 60 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 61 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 62 | 63 | FlView *view = fl_view_new(project); 64 | gtk_widget_show(GTK_WIDGET(view)); 65 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 66 | 67 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 68 | 69 | gtk_widget_grab_focus(GTK_WIDGET(view)); 70 | } 71 | 72 | // Implements GApplication::local_command_line. 73 | static gboolean my_application_local_command_line(GApplication *application, gchar ***arguments, int *exit_status) 74 | { 75 | MyApplication *self = MY_APPLICATION(application); 76 | // Strip out the first argument as it is the binary name. 77 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 78 | 79 | g_autoptr(GError) error = nullptr; 80 | if (!g_application_register(application, nullptr, &error)) 81 | { 82 | g_warning("Failed to register: %s", error->message); 83 | *exit_status = 1; 84 | return TRUE; 85 | } 86 | 87 | g_application_activate(application); 88 | *exit_status = 0; 89 | 90 | return TRUE; 91 | } 92 | 93 | // Implements GApplication::startup. 94 | static void my_application_startup(GApplication *application) 95 | { 96 | // MyApplication* self = MY_APPLICATION(object); 97 | 98 | // Perform any actions required at application startup. 99 | 100 | G_APPLICATION_CLASS(my_application_parent_class)->startup(application); 101 | } 102 | 103 | // Implements GApplication::shutdown. 104 | static void my_application_shutdown(GApplication *application) 105 | { 106 | // MyApplication* self = MY_APPLICATION(object); 107 | 108 | // Perform any actions required at application shutdown. 109 | 110 | G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); 111 | } 112 | 113 | // Implements GObject::dispose. 114 | static void my_application_dispose(GObject *object) 115 | { 116 | MyApplication *self = MY_APPLICATION(object); 117 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 118 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 119 | } 120 | 121 | static void my_application_class_init(MyApplicationClass *klass) 122 | { 123 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 124 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 125 | G_APPLICATION_CLASS(klass)->startup = my_application_startup; 126 | G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; 127 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 128 | } 129 | 130 | static void my_application_init(MyApplication *self) {} 131 | 132 | MyApplication *my_application_new() 133 | { 134 | return MY_APPLICATION(g_object_new(my_application_get_type(), 135 | "application-id", APPLICATION_ID, 136 | "flags", G_APPLICATION_NON_UNIQUE, 137 | nullptr)); 138 | } -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /linux/packaging/appimage/make_config.yaml: -------------------------------------------------------------------------------- 1 | display_name: Easy Flatpak 2 | 3 | icon: export/deb/512x512.png 4 | 5 | keywords: 6 | - flatpak 7 | - store 8 | 9 | generic_name: Easy Flatpak 10 | 11 | categories: 12 | - Utility 13 | 14 | startup_notify: true 15 | 16 | # You can specify the shared libraries that you want to bundle with your app 17 | # 18 | # flutter_distributor automatically detects the shared libraries that your app 19 | # depends on, but you can also specify them manually here. 20 | # 21 | # The following example shows how to bundle the libcurl library with your app. 22 | # 23 | # include: 24 | # - libcurl.so.4 25 | include: 26 | - libsqlite3.so 27 | # You can also specify [metainfo](https://freedesktop.org/software/appstream/docs/chap-Quickstart.html) file 28 | # which contains metadata of the app. 29 | # metainfo: linux/packaging/myappid.appdata.xml 30 | -------------------------------------------------------------------------------- /linux/packaging/deb/make_config.yaml: -------------------------------------------------------------------------------- 1 | display_name: Easy Flatpak 2 | package_name: dupot-easy-flatpak 3 | 4 | maintainer: 5 | name: Michael Bertocchi 6 | email: imikado@gmail.com 7 | 8 | priority: optional 9 | 10 | section: x11 11 | 12 | installed_size: 21336 13 | 14 | dependencies: 15 | - flatpak 16 | - zenity 17 | - sqlite3 18 | - libsqlite3-dev 19 | - tar 20 | 21 | essential: false 22 | 23 | icon: export/deb/512x512.png 24 | 25 | postuninstall_scripts: 26 | - echo "Sorry to see you go." 27 | 28 | keywords: 29 | - flatpak 30 | - store 31 | 32 | generic_name: Easy Flatpak 33 | 34 | categories: 35 | - Utility 36 | 37 | startup_notify: true 38 | -------------------------------------------------------------------------------- /linux/packaging/rpm/make_config.yaml: -------------------------------------------------------------------------------- 1 | display_name: Easy Flatpak 2 | package_name: dupot_easy_flatpak 3 | 4 | icon: export/rpm/512x512.png 5 | summary: An application to simplify install of some flatpak 6 | group: Applications/Development Tools 7 | vendor: Michael Bertocchi 8 | packager: Michael Bertocchi 9 | packagerEmail: imikado@gmail.com 10 | license: LGPLv2.0 11 | url: https://github.com/imikado/dupotEasyFlatpak 12 | 13 | build_arch: x86_64 14 | 15 | requires: 16 | - flatpak 17 | - zenity 18 | - sqlite 19 | - sqlite-devel 20 | - tar 21 | 22 | keywords: 23 | - flatpak 24 | - store 25 | 26 | generic_name: Easy Flatpak 27 | 28 | categories: 29 | - Utility 30 | 31 | startup_notify: true 32 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: dupot_easy_flatpak 2 | description: "This application will help you install Flatpak apps in a user-friendly way. We have recipes for some of them to install them even better." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 3.16.3 20 | 21 | environment: 22 | sdk: ">=3.4.4 <4.0.0" 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | flutter_localizations: 34 | sdk: flutter 35 | intl: any 36 | path_provider: any 37 | sqflite: any 38 | sqflite_common_ffi: ^2.3.4+4 39 | flutter_widget_from_html_core: ^0.15.2 40 | dio: any 41 | package_info_plus: ^8.0.2 42 | archive: ^4.0.2 43 | adaptive_theme: any 44 | url_launcher: ^6.3.0 45 | logger: ^2.5.0 46 | 47 | # The following adds the Cupertino Icons font to your application. 48 | # Use with the CupertinoIcons class for iOS style icons. 49 | cupertino_icons: ^1.0.6 50 | 51 | prompt_dialog: ^1.0.16 52 | http: ^1.2.2 53 | 54 | path: any 55 | ini: ^2.1.0 56 | 57 | window_manager: ^0.4.2 58 | 59 | #dart_rss: ^3.0.3 60 | dev_dependencies: 61 | # The "flutter_lints" package below contains a set of recommended lints to 62 | # encourage good coding practices. The lint set provided by the package is 63 | # activated in the `analysis_options.yaml` file located at the root of your 64 | # package. See that file for information about deactivating specific lint 65 | # rules and activating additional ones. 66 | flutter_lints: ^5.0.0 67 | 68 | # For information on the generic Dart part of this file, see the 69 | # following page: https://dart.dev/tools/pub/pubspec 70 | 71 | # The following section is specific to Flutter packages. 72 | flutter: 73 | # The following line ensures that the Material Icons font is 74 | # included with your application, so that you can use the icons in 75 | # the material Icons class. 76 | uses-material-design: true 77 | 78 | # To add assets to your application, add an assets section, like this: 79 | # assets: 80 | # - images/a_dot_burr.jpeg 81 | # - images/a_dot_ham.jpeg 82 | assets: 83 | - assets/images/ 84 | - assets/recipies/ 85 | - assets/recipies.json 86 | - assets/bundles/ 87 | - assets/bundles.json 88 | - assets/icons/ 89 | - assets/db/ 90 | - assets/json/ 91 | - assets/settings.json 92 | - assets/logos/512x512.png 93 | - assets/logos/logodupot.svg 94 | - assets/logos/splash.png 95 | - assets/localizations/ 96 | --------------------------------------------------------------------------------