58
									
								
								.drone.yml
									
									
									
									
									
								
							
							
						
						| @@ -4,9 +4,17 @@ type: docker | ||||
| name: default | ||||
|  | ||||
| steps: | ||||
| # Needs a full git clone | ||||
| - name: fetch | ||||
|   image: alpine/git | ||||
|   commands: | ||||
|   - git fetch --tags | ||||
|  | ||||
| # Frontend | ||||
| - name: web_build | ||||
|   image: node:23 | ||||
|   depends_on: | ||||
|   - fetch | ||||
|   volumes: | ||||
|     - name: web_app | ||||
|       path: /tmp/web_build | ||||
| @@ -20,6 +28,8 @@ steps: | ||||
| # Backend | ||||
| - name: backend_fetch_deps | ||||
|   image: rust | ||||
|   depends_on: | ||||
|   - fetch | ||||
|   volumes: | ||||
|     - name: rust_registry | ||||
|       path: /usr/local/cargo/registry | ||||
| @@ -54,6 +64,9 @@ steps: | ||||
|  | ||||
| - name: backend_build | ||||
|   image: rust | ||||
|   when: | ||||
|     event: | ||||
|       - tag | ||||
|   volumes: | ||||
|   - name: rust_registry | ||||
|     path: /usr/local/cargo/registry | ||||
| @@ -72,12 +85,57 @@ steps: | ||||
|   - ls -lah target/release/moneymgr_backend target/release/examples/api_curl | ||||
|   - cp target/release/moneymgr_backend target/release/examples/api_curl /tmp/release | ||||
|  | ||||
| # Mobile app code quality | ||||
| - name: mobile_app_code_quality | ||||
|   image: ghcr.io/cirruslabs/flutter:latest | ||||
|   depends_on: | ||||
|   - fetch | ||||
|   commands: | ||||
|   - echo "Build version:" $(git describe --tags --abbrev=0) | ||||
|   - echo "Build number:" $(git rev-list --count $(git describe --tags --abbrev=0)) | ||||
|   - cd moneymgr_mobile | ||||
|   - flutter --disable-analytics | ||||
|   - flutter pub get --enforce-lockfile | ||||
|   - dart run build_runner build | ||||
|   - flutter analyze | ||||
|  | ||||
| # Mobile app build | ||||
| - name: mobile_app_build | ||||
|   image: ghcr.io/cirruslabs/flutter:latest | ||||
|   depends_on: | ||||
|   - backend_build # prevent synchronous backend & frontend build | ||||
|   - mobile_app_code_quality | ||||
|   when: | ||||
|     event: | ||||
|       - tag | ||||
|   environment: | ||||
|     JKS_KEYSTORE: | ||||
|       from_secret: JKS_KEYSTORE | ||||
|     JKS_KEYSTORE_PASSWORD: | ||||
|       from_secret: JKS_KEYSTORE_PASSWORD | ||||
|   volumes: | ||||
|   - name: release | ||||
|     path: /tmp/release | ||||
|   commands: | ||||
|   - cd moneymgr_mobile | ||||
|   - flutter --disable-analytics | ||||
|   - bash android/ci_write_keystore.sh | ||||
|   - flutter pub get --enforce-lockfile | ||||
|   - dart run build_runner build | ||||
|   - flutter build apk | ||||
|     --release | ||||
|     --flavor publish | ||||
|     --build-name $(git describe --tags --abbrev=0)  | ||||
|     --target-platform android-arm64  | ||||
|     --build-number $(git rev-list --count $(git describe --tags --abbrev=0)) | ||||
|   - cp build/app/outputs/flutter-apk/app-publish-release.apk /tmp/release/moneymgr_mobile_arm64-v8a.apk | ||||
|  | ||||
| # Release | ||||
| - name: gitea_release | ||||
|   image: plugins/gitea-release | ||||
|   depends_on: | ||||
|   - backend_build | ||||
|   - mobile_app_build | ||||
|   when: | ||||
|     event: | ||||
|       - tag | ||||
|   | ||||
| @@ -76,4 +76,5 @@ services: | ||||
|       - S3_ACCESS_KEY=$MINIO_ROOT_USER | ||||
|       - S3_SECRET_KEY=$MINIO_ROOT_PASSWORD | ||||
|       - REDIS_HOSTNAME=redis | ||||
|       - REDIS_PASSWORD=${REDIS_PASS:-secretredis} | ||||
|       - REDIS_PASSWORD=${REDIS_PASS:-secretredis} | ||||
|       - UNSECURE_AUTO_LOGIN_EMAIL=$UNSECURE_AUTO_LOGIN_EMAIL | ||||
|   | ||||
							
								
								
									
										49
									
								
								moneymgr_mobile/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,49 @@ | ||||
| # Miscellaneous | ||||
| *.class | ||||
| *.log | ||||
| *.pyc | ||||
| *.swp | ||||
| .DS_Store | ||||
| .atom/ | ||||
| .build/ | ||||
| .buildlog/ | ||||
| .history | ||||
| .svn/ | ||||
| .swiftpm/ | ||||
| migrate_working_dir/ | ||||
|  | ||||
| # IntelliJ related | ||||
| *.iml | ||||
| *.ipr | ||||
| *.iws | ||||
| .idea/ | ||||
|  | ||||
| # The .vscode folder contains launch configuration and tasks you configure in | ||||
| # VS Code which you may wish to be included in version control, so this line | ||||
| # is commented out by default. | ||||
| #.vscode/ | ||||
|  | ||||
| # Flutter/Dart/Pub related | ||||
| **/doc/api/ | ||||
| **/ios/Flutter/.last_build_id | ||||
| .dart_tool/ | ||||
| .flutter-plugins | ||||
| .flutter-plugins-dependencies | ||||
| .pub-cache/ | ||||
| .pub/ | ||||
| /build/ | ||||
|  | ||||
| # Symbolication related | ||||
| app.*.symbols | ||||
|  | ||||
| # Obfuscation related | ||||
| app.*.map.json | ||||
|  | ||||
| # Android Studio will place build artifacts here | ||||
| /android/app/debug | ||||
| /android/app/profile | ||||
| /android/app/release | ||||
|  | ||||
|  | ||||
| *.g.dart | ||||
| *.freezed.dart | ||||
							
								
								
									
										45
									
								
								moneymgr_mobile/.metadata
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,45 @@ | ||||
| # This file tracks properties of this Flutter project. | ||||
| # Used by Flutter tool to assess capabilities and perform upgrades etc. | ||||
| # | ||||
| # This file should be version controlled and should not be manually edited. | ||||
|  | ||||
| version: | ||||
|   revision: "fcf2c11572af6f390246c056bc905eca609533a0" | ||||
|   channel: "stable" | ||||
|  | ||||
| project_type: app | ||||
|  | ||||
| # Tracks metadata for the flutter migrate command | ||||
| migration: | ||||
|   platforms: | ||||
|     - platform: root | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|     - platform: android | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|     - platform: ios | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|     - platform: linux | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|     - platform: macos | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|     - platform: web | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|     - platform: windows | ||||
|       create_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|       base_revision: fcf2c11572af6f390246c056bc905eca609533a0 | ||||
|  | ||||
|   # User provided section | ||||
|  | ||||
|   # List of Local paths (relative to this file) that should be | ||||
|   # ignored by the migrate tool. | ||||
|   # | ||||
|   # Files that are not part of the templates will be ignored by default. | ||||
|   unmanaged_files: | ||||
|     - 'lib/main.dart' | ||||
|     - 'ios/Runner.xcodeproj/project.pbxproj' | ||||
							
								
								
									
										8
									
								
								moneymgr_mobile/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | ||||
| # MoneyMgrMobile | ||||
|  | ||||
| Mobile application for MoneyMgr, built using Flutter | ||||
|  | ||||
|  | ||||
| ## Build | ||||
| 1. Run `flutter pub get` to get dependencies. | ||||
| 2. Run `dart run build_runner build` to generate required files. You can also run `dart run build_runner watch` to generate files and let [build_runner] watch for changes and rebuild if necessary. | ||||
							
								
								
									
										28
									
								
								moneymgr_mobile/analysis_options.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| # This file configures the analyzer, which statically analyzes Dart code to | ||||
| # check for errors, warnings, and lints. | ||||
| # | ||||
| # The issues identified by the analyzer are surfaced in the UI of Dart-enabled | ||||
| # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be | ||||
| # invoked from the command line by running `flutter analyze`. | ||||
|  | ||||
| # The following line activates a set of recommended lints for Flutter apps, | ||||
| # packages, and plugins designed to encourage good coding practices. | ||||
| include: package:flutter_lints/flutter.yaml | ||||
|  | ||||
| linter: | ||||
|   # The lint rules applied to this project can be customized in the | ||||
|   # section below to disable rules from the `package:flutter_lints/flutter.yaml` | ||||
|   # included above or to enable additional rules. A list of all available lints | ||||
|   # and their documentation is published at https://dart.dev/lints. | ||||
|   # | ||||
|   # Instead of disabling a lint rule for the entire project in the | ||||
|   # section below, it can also be suppressed for a single line of code | ||||
|   # or a specific dart file by using the `// ignore: name_of_lint` and | ||||
|   # `// ignore_for_file: name_of_lint` syntax on the line or in the file | ||||
|   # producing the lint. | ||||
|   rules: | ||||
|     # avoid_print: false  # Uncomment to disable the `avoid_print` rule | ||||
|     # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule | ||||
|  | ||||
| # Additional information about this file can be found at | ||||
| # https://dart.dev/guides/language/analysis-options | ||||
							
								
								
									
										16
									
								
								moneymgr_mobile/android/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,16 @@ | ||||
| publish_key.properties | ||||
|  | ||||
| gradle-wrapper.jar | ||||
| /.gradle | ||||
| /captures/ | ||||
| /gradlew | ||||
| /gradlew.bat | ||||
| /local.properties | ||||
| GeneratedPluginRegistrant.java | ||||
| .cxx/ | ||||
|  | ||||
| # Remember to never publicly share your keystore. | ||||
| # See https://flutter.dev/to/reference-keystore | ||||
| key.properties | ||||
| **/*.keystore | ||||
| **/*.jks | ||||
							
								
								
									
										8
									
								
								moneymgr_mobile/android/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | ||||
| # Android version of application | ||||
|  | ||||
| Generate keystore: | ||||
|  | ||||
| ```bash | ||||
| keytool -genkey -v -keystore ./keystore.jks -keyalg RSA \ | ||||
|         -keysize 2048 -validity 20000 -alias moneymgr | ||||
| ``` | ||||
							
								
								
									
										74
									
								
								moneymgr_mobile/android/app/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,74 @@ | ||||
| import java.util.Properties | ||||
| import java.io.FileInputStream | ||||
|  | ||||
| plugins { | ||||
|     id("com.android.application") | ||||
|     id("kotlin-android") | ||||
|     // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. | ||||
|     id("dev.flutter.flutter-gradle-plugin") | ||||
| } | ||||
|  | ||||
| val keystoreProperties = Properties() | ||||
| val keystorePropertiesFile = rootProject.file("publish_key.properties") | ||||
| if (keystorePropertiesFile.exists()) { | ||||
|     keystoreProperties.load(FileInputStream(keystorePropertiesFile)) | ||||
| } | ||||
|  | ||||
| android { | ||||
|     namespace = "org.communiquons.moneymgr" | ||||
|     compileSdk = flutter.compileSdkVersion | ||||
|     // ndkVersion = flutter.ndkVersion | ||||
|     ndkVersion = "27.0.12077973" | ||||
|  | ||||
|     compileOptions { | ||||
|         sourceCompatibility = JavaVersion.VERSION_11 | ||||
|         targetCompatibility = JavaVersion.VERSION_11 | ||||
|     } | ||||
|  | ||||
|     kotlinOptions { | ||||
|         jvmTarget = JavaVersion.VERSION_11.toString() | ||||
|     } | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId = "org.communiquons.moneymgr" | ||||
|         // You can update the following values to match your application needs. | ||||
|         // For more information, see: https://flutter.dev/to/review-gradle-config. | ||||
|         minSdk = flutter.minSdkVersion | ||||
|         targetSdk = flutter.targetSdkVersion | ||||
|         versionCode = flutter.versionCode | ||||
|         versionName = flutter.versionName | ||||
|     } | ||||
|  | ||||
|     signingConfigs { | ||||
|         create("publish") { | ||||
|             keyAlias = keystoreProperties["keyAlias"] as String | ||||
|             keyPassword = keystoreProperties["keyPassword"] as String | ||||
|             storeFile = keystoreProperties["storeFile"]?.let { file(it) } | ||||
|             storePassword = keystoreProperties["storePassword"] as String | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             // signingConfig = signingConfigs.getByName("debug") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     flavorDimensions += "default" | ||||
|     productFlavors { | ||||
|         create("development") { | ||||
|             dimension = "default" | ||||
|             applicationIdSuffix = ".debug" | ||||
|             signingConfig = signingConfigs.getByName("debug") | ||||
|         } | ||||
|         create("publish") { | ||||
|             dimension = "default" | ||||
|             signingConfig = signingConfigs.getByName("publish") | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| flutter { | ||||
|     source = "../.." | ||||
| } | ||||
							
								
								
									
										14
									
								
								moneymgr_mobile/android/app/src/debug/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|     <!-- The INTERNET permission is required for development. Specifically, | ||||
|          the Flutter tool needs it to communicate with the running application | ||||
|          to allow setting breakpoints, to provide hot reload, etc. | ||||
|     --> | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|  | ||||
|     <!-- In debug mode, unsecure traffic is permitted --> | ||||
|     <application | ||||
|         android:label="MoneyMgr Debug" | ||||
|         android:usesCleartextTraffic="true" | ||||
|         tools:replace="android:label" /> | ||||
| </manifest> | ||||
							
								
								
									
										48
									
								
								moneymgr_mobile/android/app/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,48 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <application | ||||
|         android:label="MoneyMgr" | ||||
|         android:name="${applicationName}" | ||||
|         android:icon="@mipmap/launcher_icon"> | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:exported="true" | ||||
|             android:launchMode="singleTop" | ||||
|             android:taskAffinity="" | ||||
|             android:theme="@style/LaunchTheme" | ||||
|             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||||
|             android:hardwareAccelerated="true" | ||||
|             android:windowSoftInputMode="adjustResize"> | ||||
|             <!-- Specifies an Android theme to apply to this Activity as soon as | ||||
|                  the Android process has started. This theme is visible to the user | ||||
|                  while the Flutter UI initializes. After that, this theme continues | ||||
|                  to determine the Window background behind the Flutter UI. --> | ||||
|             <meta-data | ||||
|               android:name="io.flutter.embedding.android.NormalTheme" | ||||
|               android:resource="@style/NormalTheme" | ||||
|               /> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN"/> | ||||
|                 <category android:name="android.intent.category.LAUNCHER"/> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|         <!-- Don't delete the meta-data below. | ||||
|              This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | ||||
|         <meta-data | ||||
|             android:name="flutterEmbedding" | ||||
|             android:value="2" /> | ||||
|     </application> | ||||
|     <!-- Required to query activities that can process text, see: | ||||
|          https://developer.android.com/training/package-visibility and | ||||
|          https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT. | ||||
|  | ||||
|          In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --> | ||||
|     <queries> | ||||
|         <intent> | ||||
|             <action android:name="android.intent.action.PROCESS_TEXT"/> | ||||
|             <data android:mimeType="text/plain"/> | ||||
|         </intent> | ||||
|     </queries> | ||||
|  | ||||
|     <!-- Communication with backend --> | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
| </manifest> | ||||
| @@ -0,0 +1,5 @@ | ||||
| package org.communiquons.moneymgr | ||||
|  | ||||
| import io.flutter.embedding.android.FlutterActivity | ||||
|  | ||||
| class MainActivity : FlutterActivity() | ||||
| After Width: | Height: | Size: 69 B | 
| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item> | ||||
|         <bitmap android:gravity="fill" android:src="@drawable/background"/> | ||||
|     </item> | ||||
| </layer-list> | ||||
							
								
								
									
										
											BIN
										
									
								
								moneymgr_mobile/android/app/src/main/res/drawable/background.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 69 B | 
| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item> | ||||
|         <bitmap android:gravity="fill" android:src="@drawable/background"/> | ||||
|     </item> | ||||
| </layer-list> | ||||
| After Width: | Height: | Size: 544 B | 
| After Width: | Height: | Size: 833 B | 
| After Width: | Height: | Size: 442 B | 
| After Width: | Height: | Size: 536 B | 
| After Width: | Height: | Size: 721 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 1.6 KiB | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 2.2 KiB | 
| @@ -0,0 +1,19 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | ||||
|     <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||||
|         <item name="android:forceDarkAllowed">false</item> | ||||
|         <item name="android:windowFullscreen">false</item> | ||||
|         <item name="android:windowDrawsSystemBarBackgrounds">false</item> | ||||
|         <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> | ||||
|     </style> | ||||
|     <!-- Theme applied to the Android Window as soon as the process has started. | ||||
|          This theme determines the color of the Android Window while your | ||||
|          Flutter UI initializes, as well as behind your Flutter UI while its | ||||
|          running. | ||||
|           | ||||
|          This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||||
|     <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||||
|         <item name="android:windowBackground">?android:colorBackground</item> | ||||
|     </style> | ||||
| </resources> | ||||
| @@ -0,0 +1,22 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | ||||
|     <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||||
|         <!-- Show a splash screen on the activity. Automatically removed when | ||||
|              the Flutter engine draws its first frame --> | ||||
|         <item name="android:windowBackground">@drawable/launch_background</item> | ||||
|         <item name="android:forceDarkAllowed">false</item> | ||||
|         <item name="android:windowFullscreen">false</item> | ||||
|         <item name="android:windowDrawsSystemBarBackgrounds">false</item> | ||||
|         <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> | ||||
|     </style> | ||||
|     <!-- Theme applied to the Android Window as soon as the process has started. | ||||
|          This theme determines the color of the Android Window while your | ||||
|          Flutter UI initializes, as well as behind your Flutter UI while its | ||||
|          running. | ||||
|  | ||||
|          This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||||
|     <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||||
|         <item name="android:windowBackground">?android:colorBackground</item> | ||||
|     </style> | ||||
| </resources> | ||||
| @@ -0,0 +1,19 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> | ||||
|     <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||||
|         <item name="android:forceDarkAllowed">false</item> | ||||
|         <item name="android:windowFullscreen">false</item> | ||||
|         <item name="android:windowDrawsSystemBarBackgrounds">false</item> | ||||
|         <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> | ||||
|     </style> | ||||
|     <!-- Theme applied to the Android Window as soon as the process has started. | ||||
|          This theme determines the color of the Android Window while your | ||||
|          Flutter UI initializes, as well as behind your Flutter UI while its | ||||
|          running. | ||||
|           | ||||
|          This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||||
|     <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||||
|         <item name="android:windowBackground">?android:colorBackground</item> | ||||
|     </style> | ||||
| </resources> | ||||
							
								
								
									
										22
									
								
								moneymgr_mobile/android/app/src/main/res/values/styles.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> | ||||
|     <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||||
|         <!-- Show a splash screen on the activity. Automatically removed when | ||||
|              the Flutter engine draws its first frame --> | ||||
|         <item name="android:windowBackground">@drawable/launch_background</item> | ||||
|         <item name="android:forceDarkAllowed">false</item> | ||||
|         <item name="android:windowFullscreen">false</item> | ||||
|         <item name="android:windowDrawsSystemBarBackgrounds">false</item> | ||||
|         <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> | ||||
|     </style> | ||||
|     <!-- Theme applied to the Android Window as soon as the process has started. | ||||
|          This theme determines the color of the Android Window while your | ||||
|          Flutter UI initializes, as well as behind your Flutter UI while its | ||||
|          running. | ||||
|  | ||||
|          This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||||
|     <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||||
|         <item name="android:windowBackground">?android:colorBackground</item> | ||||
|     </style> | ||||
| </resources> | ||||
| @@ -0,0 +1,7 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <!-- The INTERNET permission is required for development. Specifically, | ||||
|          the Flutter tool needs it to communicate with the running application | ||||
|          to allow setting breakpoints, to provide hot reload, etc. | ||||
|     --> | ||||
|     <uses-permission android:name="android.permission.INTERNET"/> | ||||
| </manifest> | ||||
| @@ -0,0 +1,7 @@ | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|  | ||||
|     <application | ||||
|         android:allowBackup="false" | ||||
|         android:dataExtractionRules="@xml/data_extraction_rules" | ||||
|         android:fullBackupContent="false" /> | ||||
| </manifest> | ||||
| @@ -0,0 +1,17 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <data-extraction-rules> | ||||
|     <cloud-backup> | ||||
|         <exclude domain="root" /> | ||||
|         <exclude domain="file" /> | ||||
|         <exclude domain="database" /> | ||||
|         <exclude domain="sharedpref" /> | ||||
|         <exclude domain="external" /> | ||||
|     </cloud-backup> | ||||
|     <device-transfer> | ||||
|         <exclude domain="root" /> | ||||
|         <exclude domain="file" /> | ||||
|         <exclude domain="database" /> | ||||
|         <exclude domain="sharedpref" /> | ||||
|         <exclude domain="external" /> | ||||
|     </device-transfer> | ||||
| </data-extraction-rules> | ||||
							
								
								
									
										21
									
								
								moneymgr_mobile/android/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| allprojects { | ||||
|     repositories { | ||||
|         google() | ||||
|         mavenCentral() | ||||
|     } | ||||
| } | ||||
|  | ||||
| val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() | ||||
| rootProject.layout.buildDirectory.value(newBuildDir) | ||||
|  | ||||
| subprojects { | ||||
|     val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) | ||||
|     project.layout.buildDirectory.value(newSubprojectBuildDir) | ||||
| } | ||||
| subprojects { | ||||
|     project.evaluationDependsOn(":app") | ||||
| } | ||||
|  | ||||
| tasks.register<Delete>("clean") { | ||||
|     delete(rootProject.layout.buildDirectory) | ||||
| } | ||||
							
								
								
									
										23
									
								
								moneymgr_mobile/android/ci_write_keystore.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | ||||
| #!/bin/bash | ||||
|  | ||||
| SCRIPT_DIR="$(temp=$( realpath "$0"  ) && dirname "$temp")" | ||||
|  | ||||
| KEYSTORE_PATH="$SCRIPT_DIR/keystore.jks" | ||||
| PROPERTIES_PATH="$SCRIPT_DIR/publish_key.properties" | ||||
|  | ||||
| echo Keystore path : $KEYSTORE_PATH | ||||
| echo Properties path : $PROPERTIES_PATH | ||||
|  | ||||
| [ ! -n "$JKS_KEYSTORE" ] && echo 'Missing JKS_KEYSTORE variable!'&& exit 1 | ||||
| [ ! -n "$JKS_KEYSTORE_PASSWORD" ] && echo 'Missing JKS_KEYSTORE_PASSWORD variable!' && exit 1 | ||||
|  | ||||
| # Write keystore | ||||
| echo $JKS_KEYSTORE | base64 -d > "$KEYSTORE_PATH" | ||||
|  | ||||
| # Write keystore config | ||||
| cat > "$PROPERTIES_PATH" <<_EOF | ||||
| storePassword=$JKS_KEYSTORE_PASSWORD | ||||
| keyPassword=$JKS_KEYSTORE_PASSWORD | ||||
| keyAlias=moneymgr | ||||
| storeFile=$KEYSTORE_PATH | ||||
| _EOF | ||||
							
								
								
									
										3
									
								
								moneymgr_mobile/android/gradle.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError | ||||
| android.useAndroidX=true | ||||
| android.enableJetifier=true | ||||
							
								
								
									
										5
									
								
								moneymgr_mobile/android/gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | ||||
| distributionBase=GRADLE_USER_HOME | ||||
| distributionPath=wrapper/dists | ||||
| zipStoreBase=GRADLE_USER_HOME | ||||
| zipStorePath=wrapper/dists | ||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip | ||||
							
								
								
									
										4
									
								
								moneymgr_mobile/android/publish_key.properties.sample
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| storePassword=<password-from-previous-step> | ||||
| keyPassword=<password-from-previous-step> | ||||
| keyAlias=upload | ||||
| storeFile=<keystore-file-location> | ||||
							
								
								
									
										25
									
								
								moneymgr_mobile/android/settings.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| pluginManagement { | ||||
|     val flutterSdkPath = run { | ||||
|         val properties = java.util.Properties() | ||||
|         file("local.properties").inputStream().use { properties.load(it) } | ||||
|         val flutterSdkPath = properties.getProperty("flutter.sdk") | ||||
|         require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } | ||||
|         flutterSdkPath | ||||
|     } | ||||
|  | ||||
|     includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") | ||||
|  | ||||
|     repositories { | ||||
|         google() | ||||
|         mavenCentral() | ||||
|         gradlePluginPortal() | ||||
|     } | ||||
| } | ||||
|  | ||||
| plugins { | ||||
|     id("dev.flutter.flutter-plugin-loader") version "1.0.0" | ||||
|     id("com.android.application") version "8.7.3" apply false | ||||
|     id("org.jetbrains.kotlin.android") version "2.1.0" apply false | ||||
| } | ||||
|  | ||||
| include(":app") | ||||
							
								
								
									
										
											BIN
										
									
								
								moneymgr_mobile/assets/icon/icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.2 KiB | 
							
								
								
									
										3
									
								
								moneymgr_mobile/devtools_options.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | ||||
| description: This file stores settings for Dart & Flutter DevTools. | ||||
| documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states | ||||
| extensions: | ||||
							
								
								
									
										34
									
								
								moneymgr_mobile/flutter_launcher_icons.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| # flutter pub run flutter_launcher_icons | ||||
| flutter_launcher_icons: | ||||
|   image_path: "assets/icon/icon.png" | ||||
|  | ||||
|   android: "launcher_icon" | ||||
|   # image_path_android: "assets/icon/icon.png" | ||||
|   min_sdk_android: 21 # android min sdk min:16, default 21 | ||||
|   # adaptive_icon_background: "assets/icon/background.png" | ||||
|   # adaptive_icon_foreground: "assets/icon/foreground.png" | ||||
|   # adaptive_icon_foreground_inset: 16 | ||||
|   # adaptive_icon_monochrome: "assets/icon/monochrome.png" | ||||
|  | ||||
|   ios: true | ||||
|   # image_path_ios: "assets/icon/icon.png" | ||||
|   remove_alpha_ios: true | ||||
|   # image_path_ios_dark_transparent: "assets/icon/icon_dark.png" | ||||
|   # image_path_ios_tinted_grayscale: "assets/icon/icon_tinted.png" | ||||
|   # desaturate_tinted_to_grayscale_ios: true | ||||
|   # background_color_ios: "#ffffff" | ||||
|  | ||||
| #  web: | ||||
| #    generate: true | ||||
| #    image_path: "path/to/image.png" | ||||
| #    background_color: "#hexcode" | ||||
| #    theme_color: "#hexcode" | ||||
|  | ||||
| #  windows: | ||||
| #    generate: true | ||||
| #    image_path: "path/to/image.png" | ||||
| #    icon_size: 48 # min:48, max:256, default: 48 | ||||
|  | ||||
| #  macos: | ||||
| #    generate: true | ||||
| #    image_path: "path/to/image.png" | ||||
							
								
								
									
										154
									
								
								moneymgr_mobile/flutter_native_splash.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,154 @@ | ||||
| flutter_native_splash: | ||||
|   # This package generates native code to customize Flutter's default white native splash screen | ||||
|   # with background color and splash image. | ||||
|   # Customize the parameters below, and run the following command in the terminal: | ||||
|   # dart run flutter_native_splash:create | ||||
|   # To restore Flutter's default white splash screen, run the following command in the terminal: | ||||
|   # dart run flutter_native_splash:remove | ||||
|  | ||||
|   # IMPORTANT NOTE: These parameter do not affect the configuration of Android 12 and later, which | ||||
|   # handle splash screens differently that prior versions of Android.  Android 12 and later must be | ||||
|   # configured specifically in the android_12 section below. | ||||
|  | ||||
|   # color or background_image is the only required parameter.  Use color to set the background | ||||
|   # of your splash screen to a solid color.  Use background_image to set the background of your | ||||
|   # splash screen to a png image.  This is useful for gradients. The image will be stretch to the | ||||
|   # size of the app. Only one parameter can be used, color and background_image cannot both be set. | ||||
|   color: "#42a5f5" | ||||
|   #background_image: "assets/background.png" | ||||
|  | ||||
|   # Optional parameters are listed below.  To enable a parameter, uncomment the line by removing | ||||
|   # the leading # character. | ||||
|  | ||||
|   # The image parameter allows you to specify an image used in the splash screen.  It must be a | ||||
|   # png file and should be sized for 4x pixel density. | ||||
|   #image: assets/splash.png | ||||
|  | ||||
|   # The branding property allows you to specify an image used as branding in the splash screen. | ||||
|   # It must be a png file. It is supported for Android, iOS and the Web.  For Android 12, | ||||
|   # see the Android 12 section below. | ||||
|   #branding: assets/dart.png | ||||
|  | ||||
|   # To position the branding image at the bottom of the screen you can use bottom, bottomRight, | ||||
|   # and bottomLeft. The default values is bottom if not specified or specified something else. | ||||
|   #branding_mode: bottom | ||||
|  | ||||
|   # Set the branding padding from the bottom of the screen.  The default value is 0 | ||||
|   # (Not supported on web yet) | ||||
|   # branding_bottom_padding: 24 | ||||
|  | ||||
|   # The color_dark, background_image_dark, image_dark, branding_dark are parameters that set the background | ||||
|   # and image when the device is in dark mode. If they are not specified, the app will use the | ||||
|   # parameters from above.  If there is no parameter above, the app will use the light mode values. | ||||
|   # If the image_dark parameter is specified, color_dark or background_image_dark must be specified. | ||||
|   # color_dark and background_image_dark cannot both be set. | ||||
|   #color_dark: "#042a49" | ||||
|   #background_image_dark: "assets/dark-background.png" | ||||
|   #image_dark: assets/splash-invert.png | ||||
|   #branding_dark: assets/dart_dark.png | ||||
|  | ||||
|   # From Android 12 onwards, the splash screen is handled differently than in previous versions. | ||||
|   # Please visit https://developer.android.com/guide/topics/ui/splash-screen | ||||
|   # Following are specific parameters for Android 12+. | ||||
|   android_12: | ||||
|   # The image parameter sets the splash screen icon image.  If this parameter is not specified, | ||||
|   # the app's launcher icon will be used instead. | ||||
|   # Please note that the splash screen will be clipped to a circle on the center of the screen. | ||||
|   # App icon with an icon background: This should be 960×960 pixels, and fit within a circle | ||||
|   # 640 pixels in diameter. | ||||
|   # App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle | ||||
|   # 768 pixels in diameter.  To fit a 1152x1152 image within a circle with a 768 diameter, simply | ||||
|   # ensure that the most important design elements of your image are placed within a circular area | ||||
|   # with a 768 diameter at the center of the 1152x1152 canvas. | ||||
|   #image: assets/android12splash.png | ||||
|  | ||||
|   # Splash screen background color. | ||||
|   #color: "#42a5f5" | ||||
|  | ||||
|   # App icon background color. | ||||
|   #icon_background_color: "#111111" | ||||
|  | ||||
|   # The branding property allows you to specify an image used as branding in the splash screen. | ||||
|   #branding: assets/dart.png | ||||
|  | ||||
|   # The image_dark, color_dark, icon_background_color_dark, and branding_dark set values that | ||||
|   # apply when the device is in dark mode. If they are not specified, the app will use the | ||||
|   # parameters from above.  If there is no parameter above, the app will use the light mode values. | ||||
|   #image_dark: assets/android12splash-invert.png | ||||
|   #color_dark: "#042a49" | ||||
|   #icon_background_color_dark: "#eeeeee" | ||||
|  | ||||
|   # The android, ios and web parameters can be used to disable generating a splash screen on a given | ||||
|   # platform. | ||||
|   #android: false | ||||
|   #ios: false | ||||
|   #web: false | ||||
|  | ||||
|   # Platform specific images can be specified with the following parameters, which will override | ||||
|   # the respective parameter.  You may specify all, selected, or none of these parameters: | ||||
|   #color_android: "#42a5f5" | ||||
|   #color_dark_android: "#042a49" | ||||
|   #color_ios: "#42a5f5" | ||||
|   #color_dark_ios: "#042a49" | ||||
|   #color_web: "#42a5f5" | ||||
|   #color_dark_web: "#042a49" | ||||
|   #image_android: assets/splash-android.png | ||||
|   #image_dark_android: assets/splash-invert-android.png | ||||
|   #image_ios: assets/splash-ios.png | ||||
|   #image_dark_ios: assets/splash-invert-ios.png | ||||
|   #image_web: assets/splash-web.gif | ||||
|   #image_dark_web: assets/splash-invert-web.gif | ||||
|   #background_image_android: "assets/background-android.png" | ||||
|   #background_image_dark_android: "assets/dark-background-android.png" | ||||
|   #background_image_ios: "assets/background-ios.png" | ||||
|   #background_image_dark_ios: "assets/dark-background-ios.png" | ||||
|   #background_image_web: "assets/background-web.png" | ||||
|   #background_image_dark_web: "assets/dark-background-web.png" | ||||
|   #branding_android: assets/brand-android.png | ||||
|   #branding_bottom_padding_android: 24 | ||||
|   #branding_dark_android: assets/dart_dark-android.png | ||||
|   #branding_ios: assets/brand-ios.png | ||||
|   #branding_bottom_padding_ios: 24 | ||||
|   #branding_dark_ios: assets/dart_dark-ios.png | ||||
|   #branding_web: assets/brand-web.gif | ||||
|   #branding_dark_web: assets/dart_dark-web.gif | ||||
|  | ||||
|   # The position of the splash image can be set with android_gravity, ios_content_mode, and | ||||
|   # web_image_mode parameters.  All default to center. | ||||
|   # | ||||
|   # android_gravity can be one of the following Android Gravity (see | ||||
|   # https://developer.android.com/reference/android/view/Gravity): bottom, center, | ||||
|   # center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, | ||||
|   # fill_vertical, left, right, start, or top. android_gravity can be combined using the | operator to achieve multiple effects. | ||||
|   # For example: | ||||
|   # `android_gravity: fill|clip_vertical` - This will fill the width while maintaining the image's vertical aspect ratio | ||||
|   #android_gravity: center | ||||
|   # | ||||
|   # ios_content_mode can be one of the following iOS UIView.ContentMode (see | ||||
|   # https://developer.apple.com/documentation/uikit/uiview/contentmode): scaleToFill, | ||||
|   # scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, | ||||
|   # bottomLeft, or bottomRight. | ||||
|   #ios_content_mode: center | ||||
|   # | ||||
|   # web_image_mode can be one of the following modes: center, contain, stretch, and cover. | ||||
|   #web_image_mode: center | ||||
|  | ||||
|   # The screen orientation can be set in Android with the android_screen_orientation parameter. | ||||
|   # Valid parameters can be found here: | ||||
|   # https://developer.android.com/guide/topics/manifest/activity-element#screen | ||||
|   #android_screen_orientation: sensorLandscape | ||||
|  | ||||
|   # To hide the notification bar, use the fullscreen parameter.  Has no effect in web since web | ||||
|   # has no notification bar.  Defaults to false. | ||||
|   # NOTE: Unlike Android, iOS will not automatically show the notification bar when the app loads. | ||||
|   #       To show the notification bar, add the following code to your Flutter app: | ||||
|   #       WidgetsFlutterBinding.ensureInitialized(); | ||||
|   #       SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top], ); | ||||
|   #fullscreen: true | ||||
|  | ||||
|   # If you have changed the name(s) of your info.plist file(s), you can specify the filename(s) | ||||
|   # with the info_plist_files parameter.  Remove only the # characters in the three lines below, | ||||
|   # do not remove any spaces: | ||||
|   #info_plist_files: | ||||
|   #  - 'ios/Runner/Info-Debug.plist' | ||||
|   #  - 'ios/Runner/Info-Release.plist' | ||||
							
								
								
									
										34
									
								
								moneymgr_mobile/ios/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| **/dgph | ||||
| *.mode1v3 | ||||
| *.mode2v3 | ||||
| *.moved-aside | ||||
| *.pbxuser | ||||
| *.perspectivev3 | ||||
| **/*sync/ | ||||
| .sconsign.dblite | ||||
| .tags* | ||||
| **/.vagrant/ | ||||
| **/DerivedData/ | ||||
| Icon? | ||||
| **/Pods/ | ||||
| **/.symlinks/ | ||||
| profile | ||||
| xcuserdata | ||||
| **/.generated/ | ||||
| Flutter/App.framework | ||||
| Flutter/Flutter.framework | ||||
| Flutter/Flutter.podspec | ||||
| Flutter/Generated.xcconfig | ||||
| Flutter/ephemeral/ | ||||
| Flutter/app.flx | ||||
| Flutter/app.zip | ||||
| Flutter/flutter_assets/ | ||||
| Flutter/flutter_export_environment.sh | ||||
| ServiceDefinitions.json | ||||
| Runner/GeneratedPluginRegistrant.* | ||||
|  | ||||
| # Exceptions to above rules. | ||||
| !default.mode1v3 | ||||
| !default.mode2v3 | ||||
| !default.pbxuser | ||||
| !default.perspectivev3 | ||||
							
								
								
									
										26
									
								
								moneymgr_mobile/ios/Flutter/AppFrameworkInfo.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
|   <key>CFBundleDevelopmentRegion</key> | ||||
|   <string>en</string> | ||||
|   <key>CFBundleExecutable</key> | ||||
|   <string>App</string> | ||||
|   <key>CFBundleIdentifier</key> | ||||
|   <string>io.flutter.flutter.app</string> | ||||
|   <key>CFBundleInfoDictionaryVersion</key> | ||||
|   <string>6.0</string> | ||||
|   <key>CFBundleName</key> | ||||
|   <string>App</string> | ||||
|   <key>CFBundlePackageType</key> | ||||
|   <string>FMWK</string> | ||||
|   <key>CFBundleShortVersionString</key> | ||||
|   <string>1.0</string> | ||||
|   <key>CFBundleSignature</key> | ||||
|   <string>????</string> | ||||
|   <key>CFBundleVersion</key> | ||||
|   <string>1.0</string> | ||||
|   <key>MinimumOSVersion</key> | ||||
|   <string>12.0</string> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										1
									
								
								moneymgr_mobile/ios/Flutter/Debug.xcconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| #include "Generated.xcconfig" | ||||
							
								
								
									
										1
									
								
								moneymgr_mobile/ios/Flutter/Release.xcconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| #include "Generated.xcconfig" | ||||
							
								
								
									
										616
									
								
								moneymgr_mobile/ios/Runner.xcodeproj/project.pbxproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,616 @@ | ||||
| // !$*UTF8*$! | ||||
| { | ||||
| 	archiveVersion = 1; | ||||
| 	classes = { | ||||
| 	}; | ||||
| 	objectVersion = 54; | ||||
| 	objects = { | ||||
|  | ||||
| /* Begin PBXBuildFile section */ | ||||
| 		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | ||||
| 		331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; | ||||
| 		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | ||||
| 		74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | ||||
| 		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | ||||
| 		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | ||||
| 		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | ||||
| /* End PBXBuildFile section */ | ||||
|  | ||||
| /* Begin PBXContainerItemProxy section */ | ||||
| 		331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { | ||||
| 			isa = PBXContainerItemProxy; | ||||
| 			containerPortal = 97C146E61CF9000F007C117D /* Project object */; | ||||
| 			proxyType = 1; | ||||
| 			remoteGlobalIDString = 97C146ED1CF9000F007C117D; | ||||
| 			remoteInfo = Runner; | ||||
| 		}; | ||||
| /* End PBXContainerItemProxy section */ | ||||
|  | ||||
| /* Begin PBXCopyFilesBuildPhase section */ | ||||
| 		9705A1C41CF9048500538489 /* Embed Frameworks */ = { | ||||
| 			isa = PBXCopyFilesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			dstPath = ""; | ||||
| 			dstSubfolderSpec = 10; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			name = "Embed Frameworks"; | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXCopyFilesBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXFileReference section */ | ||||
| 		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | ||||
| 		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | ||||
| 		331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; | ||||
| 		331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | ||||
| 		74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | ||||
| 		74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||||
| 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | ||||
| 		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | ||||
| 		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | ||||
| 		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||||
| 		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; | ||||
| 		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | ||||
| 		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | ||||
| 		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||||
| /* End PBXFileReference section */ | ||||
|  | ||||
| /* Begin PBXFrameworksBuildPhase section */ | ||||
| 		97C146EB1CF9000F007C117D /* Frameworks */ = { | ||||
| 			isa = PBXFrameworksBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXFrameworksBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXGroup section */ | ||||
| 		331C8082294A63A400263BE5 /* RunnerTests */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				331C807B294A618700263BE5 /* RunnerTests.swift */, | ||||
| 			); | ||||
| 			path = RunnerTests; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		9740EEB11CF90186004384FC /* Flutter */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, | ||||
| 				9740EEB21CF90195004384FC /* Debug.xcconfig */, | ||||
| 				7AFA3C8E1D35360C0083082E /* Release.xcconfig */, | ||||
| 				9740EEB31CF90195004384FC /* Generated.xcconfig */, | ||||
| 			); | ||||
| 			name = Flutter; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		97C146E51CF9000F007C117D = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				9740EEB11CF90186004384FC /* Flutter */, | ||||
| 				97C146F01CF9000F007C117D /* Runner */, | ||||
| 				97C146EF1CF9000F007C117D /* Products */, | ||||
| 				331C8082294A63A400263BE5 /* RunnerTests */, | ||||
| 			); | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		97C146EF1CF9000F007C117D /* Products */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				97C146EE1CF9000F007C117D /* Runner.app */, | ||||
| 				331C8081294A63A400263BE5 /* RunnerTests.xctest */, | ||||
| 			); | ||||
| 			name = Products; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		97C146F01CF9000F007C117D /* Runner */ = { | ||||
| 			isa = PBXGroup; | ||||
| 			children = ( | ||||
| 				97C146FA1CF9000F007C117D /* Main.storyboard */, | ||||
| 				97C146FD1CF9000F007C117D /* Assets.xcassets */, | ||||
| 				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, | ||||
| 				97C147021CF9000F007C117D /* Info.plist */, | ||||
| 				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, | ||||
| 				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, | ||||
| 				74858FAE1ED2DC5600515810 /* AppDelegate.swift */, | ||||
| 				74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, | ||||
| 			); | ||||
| 			path = Runner; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| /* End PBXGroup section */ | ||||
|  | ||||
| /* Begin PBXNativeTarget section */ | ||||
| 		331C8080294A63A400263BE5 /* RunnerTests */ = { | ||||
| 			isa = PBXNativeTarget; | ||||
| 			buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; | ||||
| 			buildPhases = ( | ||||
| 				331C807D294A63A400263BE5 /* Sources */, | ||||
| 				331C807F294A63A400263BE5 /* Resources */, | ||||
| 			); | ||||
| 			buildRules = ( | ||||
| 			); | ||||
| 			dependencies = ( | ||||
| 				331C8086294A63A400263BE5 /* PBXTargetDependency */, | ||||
| 			); | ||||
| 			name = RunnerTests; | ||||
| 			productName = RunnerTests; | ||||
| 			productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; | ||||
| 			productType = "com.apple.product-type.bundle.unit-test"; | ||||
| 		}; | ||||
| 		97C146ED1CF9000F007C117D /* Runner */ = { | ||||
| 			isa = PBXNativeTarget; | ||||
| 			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | ||||
| 			buildPhases = ( | ||||
| 				9740EEB61CF901F6004384FC /* Run Script */, | ||||
| 				97C146EA1CF9000F007C117D /* Sources */, | ||||
| 				97C146EB1CF9000F007C117D /* Frameworks */, | ||||
| 				97C146EC1CF9000F007C117D /* Resources */, | ||||
| 				9705A1C41CF9048500538489 /* Embed Frameworks */, | ||||
| 				3B06AD1E1E4923F5004D2608 /* Thin Binary */, | ||||
| 			); | ||||
| 			buildRules = ( | ||||
| 			); | ||||
| 			dependencies = ( | ||||
| 			); | ||||
| 			name = Runner; | ||||
| 			productName = Runner; | ||||
| 			productReference = 97C146EE1CF9000F007C117D /* Runner.app */; | ||||
| 			productType = "com.apple.product-type.application"; | ||||
| 		}; | ||||
| /* End PBXNativeTarget section */ | ||||
|  | ||||
| /* Begin PBXProject section */ | ||||
| 		97C146E61CF9000F007C117D /* Project object */ = { | ||||
| 			isa = PBXProject; | ||||
| 			attributes = { | ||||
| 				BuildIndependentTargetsInParallel = YES; | ||||
| 				LastUpgradeCheck = 1510; | ||||
| 				ORGANIZATIONNAME = ""; | ||||
| 				TargetAttributes = { | ||||
| 					331C8080294A63A400263BE5 = { | ||||
| 						CreatedOnToolsVersion = 14.0; | ||||
| 						TestTargetID = 97C146ED1CF9000F007C117D; | ||||
| 					}; | ||||
| 					97C146ED1CF9000F007C117D = { | ||||
| 						CreatedOnToolsVersion = 7.3.1; | ||||
| 						LastSwiftMigration = 1100; | ||||
| 					}; | ||||
| 				}; | ||||
| 			}; | ||||
| 			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; | ||||
| 			compatibilityVersion = "Xcode 9.3"; | ||||
| 			developmentRegion = en; | ||||
| 			hasScannedForEncodings = 0; | ||||
| 			knownRegions = ( | ||||
| 				en, | ||||
| 				Base, | ||||
| 			); | ||||
| 			mainGroup = 97C146E51CF9000F007C117D; | ||||
| 			productRefGroup = 97C146EF1CF9000F007C117D /* Products */; | ||||
| 			projectDirPath = ""; | ||||
| 			projectRoot = ""; | ||||
| 			targets = ( | ||||
| 				97C146ED1CF9000F007C117D /* Runner */, | ||||
| 				331C8080294A63A400263BE5 /* RunnerTests */, | ||||
| 			); | ||||
| 		}; | ||||
| /* End PBXProject section */ | ||||
|  | ||||
| /* Begin PBXResourcesBuildPhase section */ | ||||
| 		331C807F294A63A400263BE5 /* Resources */ = { | ||||
| 			isa = PBXResourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| 		97C146EC1CF9000F007C117D /* Resources */ = { | ||||
| 			isa = PBXResourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, | ||||
| 				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, | ||||
| 				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, | ||||
| 				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXResourcesBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXShellScriptBuildPhase section */ | ||||
| 		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			alwaysOutOfDate = 1; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputPaths = ( | ||||
| 				"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", | ||||
| 			); | ||||
| 			name = "Thin Binary"; | ||||
| 			outputPaths = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | ||||
| 		}; | ||||
| 		9740EEB61CF901F6004384FC /* Run Script */ = { | ||||
| 			isa = PBXShellScriptBuildPhase; | ||||
| 			alwaysOutOfDate = 1; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 			); | ||||
| 			inputPaths = ( | ||||
| 			); | ||||
| 			name = "Run Script"; | ||||
| 			outputPaths = ( | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 			shellPath = /bin/sh; | ||||
| 			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | ||||
| 		}; | ||||
| /* End PBXShellScriptBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXSourcesBuildPhase section */ | ||||
| 		331C807D294A63A400263BE5 /* Sources */ = { | ||||
| 			isa = PBXSourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| 		97C146EA1CF9000F007C117D /* Sources */ = { | ||||
| 			isa = PBXSourcesBuildPhase; | ||||
| 			buildActionMask = 2147483647; | ||||
| 			files = ( | ||||
| 				74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, | ||||
| 				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, | ||||
| 			); | ||||
| 			runOnlyForDeploymentPostprocessing = 0; | ||||
| 		}; | ||||
| /* End PBXSourcesBuildPhase section */ | ||||
|  | ||||
| /* Begin PBXTargetDependency section */ | ||||
| 		331C8086294A63A400263BE5 /* PBXTargetDependency */ = { | ||||
| 			isa = PBXTargetDependency; | ||||
| 			target = 97C146ED1CF9000F007C117D /* Runner */; | ||||
| 			targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; | ||||
| 		}; | ||||
| /* End PBXTargetDependency section */ | ||||
|  | ||||
| /* Begin PBXVariantGroup section */ | ||||
| 		97C146FA1CF9000F007C117D /* Main.storyboard */ = { | ||||
| 			isa = PBXVariantGroup; | ||||
| 			children = ( | ||||
| 				97C146FB1CF9000F007C117D /* Base */, | ||||
| 			); | ||||
| 			name = Main.storyboard; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| 		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { | ||||
| 			isa = PBXVariantGroup; | ||||
| 			children = ( | ||||
| 				97C147001CF9000F007C117D /* Base */, | ||||
| 			); | ||||
| 			name = LaunchScreen.storyboard; | ||||
| 			sourceTree = "<group>"; | ||||
| 		}; | ||||
| /* End PBXVariantGroup section */ | ||||
|  | ||||
| /* Begin XCBuildConfiguration section */ | ||||
| 		249021D3217E4FDB00AE95B9 /* Profile */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||||
| 				ENABLE_NS_ASSERTIONS = NO; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				ENABLE_USER_SCRIPT_SANDBOXING = NO; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu99; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = NO; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				SUPPORTED_PLATFORMS = iphoneos; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2"; | ||||
| 				VALIDATE_PRODUCT = YES; | ||||
| 			}; | ||||
| 			name = Profile; | ||||
| 		}; | ||||
| 		249021D4217E4FDB00AE95B9 /* Profile */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | ||||
| 			buildSettings = { | ||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||||
| 				ENABLE_BITCODE = NO; | ||||
| 				INFOPLIST_FILE = Runner/Info.plist; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = org.communiquons.moneymgrMobile; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				VERSIONING_SYSTEM = "apple-generic"; | ||||
| 			}; | ||||
| 			name = Profile; | ||||
| 		}; | ||||
| 		331C8088294A63A400263BE5 /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				BUNDLE_LOADER = "$(TEST_HOST)"; | ||||
| 				CODE_SIGN_STYLE = Automatic; | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = org.communiquons.moneymgrMobile.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		331C8089294A63A400263BE5 /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				BUNDLE_LOADER = "$(TEST_HOST)"; | ||||
| 				CODE_SIGN_STYLE = Automatic; | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = org.communiquons.moneymgrMobile.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| 		331C808A294A63A400263BE5 /* Profile */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				BUNDLE_LOADER = "$(TEST_HOST)"; | ||||
| 				CODE_SIGN_STYLE = Automatic; | ||||
| 				CURRENT_PROJECT_VERSION = 1; | ||||
| 				GENERATE_INFOPLIST_FILE = YES; | ||||
| 				MARKETING_VERSION = 1.0; | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = org.communiquons.moneymgrMobile.RunnerTests; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||||
| 			}; | ||||
| 			name = Profile; | ||||
| 		}; | ||||
| 		97C147031CF9000F007C117D /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = dwarf; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				ENABLE_TESTABILITY = YES; | ||||
| 				ENABLE_USER_SCRIPT_SANDBOXING = NO; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu99; | ||||
| 				GCC_DYNAMIC_NO_PIC = NO; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_OPTIMIZATION_LEVEL = 0; | ||||
| 				GCC_PREPROCESSOR_DEFINITIONS = ( | ||||
| 					"DEBUG=1", | ||||
| 					"$(inherited)", | ||||
| 				); | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = YES; | ||||
| 				ONLY_ACTIVE_ARCH = YES; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2"; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		97C147041CF9000F007C117D /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			buildSettings = { | ||||
| 				ALWAYS_SEARCH_USER_PATHS = NO; | ||||
| 				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon; | ||||
| 				CLANG_ANALYZER_NONNULL = YES; | ||||
| 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||||
| 				CLANG_CXX_LIBRARY = "libc++"; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CLANG_ENABLE_OBJC_ARC = YES; | ||||
| 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||||
| 				CLANG_WARN_BOOL_CONVERSION = YES; | ||||
| 				CLANG_WARN_COMMA = YES; | ||||
| 				CLANG_WARN_CONSTANT_CONVERSION = YES; | ||||
| 				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||||
| 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||||
| 				CLANG_WARN_EMPTY_BODY = YES; | ||||
| 				CLANG_WARN_ENUM_CONVERSION = YES; | ||||
| 				CLANG_WARN_INFINITE_RECURSION = YES; | ||||
| 				CLANG_WARN_INT_CONVERSION = YES; | ||||
| 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||||
| 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||||
| 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||||
| 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||||
| 				CLANG_WARN_STRICT_PROTOTYPES = YES; | ||||
| 				CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||||
| 				CLANG_WARN_UNREACHABLE_CODE = YES; | ||||
| 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||||
| 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||||
| 				COPY_PHASE_STRIP = NO; | ||||
| 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||||
| 				ENABLE_NS_ASSERTIONS = NO; | ||||
| 				ENABLE_STRICT_OBJC_MSGSEND = YES; | ||||
| 				ENABLE_USER_SCRIPT_SANDBOXING = NO; | ||||
| 				GCC_C_LANGUAGE_STANDARD = gnu99; | ||||
| 				GCC_NO_COMMON_BLOCKS = YES; | ||||
| 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||||
| 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||||
| 				GCC_WARN_UNDECLARED_SELECTOR = YES; | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 12.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = NO; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				SUPPORTED_PLATFORMS = iphoneos; | ||||
| 				SWIFT_COMPILATION_MODE = wholemodule; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-O"; | ||||
| 				TARGETED_DEVICE_FAMILY = "1,2"; | ||||
| 				VALIDATE_PRODUCT = YES; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| 		97C147061CF9000F007C117D /* Debug */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; | ||||
| 			buildSettings = { | ||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||||
| 				ENABLE_BITCODE = NO; | ||||
| 				INFOPLIST_FILE = Runner/Info.plist; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = org.communiquons.moneymgrMobile; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||||
| 				SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				VERSIONING_SYSTEM = "apple-generic"; | ||||
| 			}; | ||||
| 			name = Debug; | ||||
| 		}; | ||||
| 		97C147071CF9000F007C117D /* Release */ = { | ||||
| 			isa = XCBuildConfiguration; | ||||
| 			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | ||||
| 			buildSettings = { | ||||
| 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||||
| 				CLANG_ENABLE_MODULES = YES; | ||||
| 				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||||
| 				ENABLE_BITCODE = NO; | ||||
| 				INFOPLIST_FILE = Runner/Info.plist; | ||||
| 				LD_RUNPATH_SEARCH_PATHS = ( | ||||
| 					"$(inherited)", | ||||
| 					"@executable_path/Frameworks", | ||||
| 				); | ||||
| 				PRODUCT_BUNDLE_IDENTIFIER = org.communiquons.moneymgrMobile; | ||||
| 				PRODUCT_NAME = "$(TARGET_NAME)"; | ||||
| 				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||||
| 				SWIFT_VERSION = 5.0; | ||||
| 				VERSIONING_SYSTEM = "apple-generic"; | ||||
| 			}; | ||||
| 			name = Release; | ||||
| 		}; | ||||
| /* End XCBuildConfiguration section */ | ||||
|  | ||||
| /* Begin XCConfigurationList section */ | ||||
| 		331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				331C8088294A63A400263BE5 /* Debug */, | ||||
| 				331C8089294A63A400263BE5 /* Release */, | ||||
| 				331C808A294A63A400263BE5 /* Profile */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| 		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				97C147031CF9000F007C117D /* Debug */, | ||||
| 				97C147041CF9000F007C117D /* Release */, | ||||
| 				249021D3217E4FDB00AE95B9 /* Profile */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| 		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { | ||||
| 			isa = XCConfigurationList; | ||||
| 			buildConfigurations = ( | ||||
| 				97C147061CF9000F007C117D /* Debug */, | ||||
| 				97C147071CF9000F007C117D /* Release */, | ||||
| 				249021D4217E4FDB00AE95B9 /* Profile */, | ||||
| 			); | ||||
| 			defaultConfigurationIsVisible = 0; | ||||
| 			defaultConfigurationName = Release; | ||||
| 		}; | ||||
| /* End XCConfigurationList section */ | ||||
| 	}; | ||||
| 	rootObject = 97C146E61CF9000F007C117D /* Project object */; | ||||
| } | ||||
							
								
								
									
										7
									
								
								moneymgr_mobile/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "self:"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>IDEDidComputeMac32BitWarning</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>PreviewsEnabled</key> | ||||
| 	<false/> | ||||
| </dict> | ||||
| </plist> | ||||
| @@ -0,0 +1,101 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Scheme | ||||
|    LastUpgradeVersion = "1510" | ||||
|    version = "1.3"> | ||||
|    <BuildAction | ||||
|       parallelizeBuildables = "YES" | ||||
|       buildImplicitDependencies = "YES"> | ||||
|       <BuildActionEntries> | ||||
|          <BuildActionEntry | ||||
|             buildForTesting = "YES" | ||||
|             buildForRunning = "YES" | ||||
|             buildForProfiling = "YES" | ||||
|             buildForArchiving = "YES" | ||||
|             buildForAnalyzing = "YES"> | ||||
|             <BuildableReference | ||||
|                BuildableIdentifier = "primary" | ||||
|                BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||||
|                BuildableName = "Runner.app" | ||||
|                BlueprintName = "Runner" | ||||
|                ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|             </BuildableReference> | ||||
|          </BuildActionEntry> | ||||
|       </BuildActionEntries> | ||||
|    </BuildAction> | ||||
|    <TestAction | ||||
|       buildConfiguration = "Debug" | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES"> | ||||
|       <MacroExpansion> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||||
|             BuildableName = "Runner.app" | ||||
|             BlueprintName = "Runner" | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </MacroExpansion> | ||||
|       <Testables> | ||||
|          <TestableReference | ||||
|             skipped = "NO" | ||||
|             parallelizable = "YES"> | ||||
|             <BuildableReference | ||||
|                BuildableIdentifier = "primary" | ||||
|                BlueprintIdentifier = "331C8080294A63A400263BE5" | ||||
|                BuildableName = "RunnerTests.xctest" | ||||
|                BlueprintName = "RunnerTests" | ||||
|                ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|             </BuildableReference> | ||||
|          </TestableReference> | ||||
|       </Testables> | ||||
|    </TestAction> | ||||
|    <LaunchAction | ||||
|       buildConfiguration = "Debug" | ||||
|       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||||
|       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||||
|       customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" | ||||
|       launchStyle = "0" | ||||
|       useCustomWorkingDirectory = "NO" | ||||
|       ignoresPersistentStateOnLaunch = "NO" | ||||
|       debugDocumentVersioning = "YES" | ||||
|       debugServiceExtension = "internal" | ||||
|       enableGPUValidationMode = "1" | ||||
|       allowLocationSimulation = "YES"> | ||||
|       <BuildableProductRunnable | ||||
|          runnableDebuggingMode = "0"> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||||
|             BuildableName = "Runner.app" | ||||
|             BlueprintName = "Runner" | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </BuildableProductRunnable> | ||||
|    </LaunchAction> | ||||
|    <ProfileAction | ||||
|       buildConfiguration = "Profile" | ||||
|       shouldUseLaunchSchemeArgsEnv = "YES" | ||||
|       savedToolIdentifier = "" | ||||
|       useCustomWorkingDirectory = "NO" | ||||
|       debugDocumentVersioning = "YES"> | ||||
|       <BuildableProductRunnable | ||||
|          runnableDebuggingMode = "0"> | ||||
|          <BuildableReference | ||||
|             BuildableIdentifier = "primary" | ||||
|             BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||||
|             BuildableName = "Runner.app" | ||||
|             BlueprintName = "Runner" | ||||
|             ReferencedContainer = "container:Runner.xcodeproj"> | ||||
|          </BuildableReference> | ||||
|       </BuildableProductRunnable> | ||||
|    </ProfileAction> | ||||
|    <AnalyzeAction | ||||
|       buildConfiguration = "Debug"> | ||||
|    </AnalyzeAction> | ||||
|    <ArchiveAction | ||||
|       buildConfiguration = "Release" | ||||
|       revealArchiveInOrganizer = "YES"> | ||||
|    </ArchiveAction> | ||||
| </Scheme> | ||||
							
								
								
									
										7
									
								
								moneymgr_mobile/ios/Runner.xcworkspace/contents.xcworkspacedata
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <Workspace | ||||
|    version = "1.0"> | ||||
|    <FileRef | ||||
|       location = "group:Runner.xcodeproj"> | ||||
|    </FileRef> | ||||
| </Workspace> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>IDEDidComputeMac32BitWarning</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| <dict> | ||||
| 	<key>PreviewsEnabled</key> | ||||
| 	<false/> | ||||
| </dict> | ||||
| </plist> | ||||
							
								
								
									
										13
									
								
								moneymgr_mobile/ios/Runner/AppDelegate.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | ||||
| import Flutter | ||||
| import UIKit | ||||
|  | ||||
| @main | ||||
| @objc class AppDelegate: FlutterAppDelegate { | ||||
|   override func application( | ||||
|     _ application: UIApplication, | ||||
|     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? | ||||
|   ) -> Bool { | ||||
|     GeneratedPluginRegistrant.register(with: self) | ||||
|     return super.application(application, didFinishLaunchingWithOptions: launchOptions) | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| {"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}} | ||||
| After Width: | Height: | Size: 24 KiB | 
| After Width: | Height: | Size: 345 B | 
| After Width: | Height: | Size: 504 B | 
| After Width: | Height: | Size: 859 B | 
| After Width: | Height: | Size: 488 B | 
| After Width: | Height: | Size: 845 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 504 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 777 B | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 811 B | 
| After Width: | Height: | Size: 1.6 KiB | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 2.4 KiB | 
| After Width: | Height: | Size: 967 B | 
| After Width: | Height: | Size: 2.0 KiB | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 2.0 KiB | 
| After Width: | Height: | Size: 2.2 KiB | 
							
								
								
									
										21
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "background.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 69 B | 
							
								
								
									
										23
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | ||||
| { | ||||
|   "images" : [ | ||||
|     { | ||||
|       "filename" : "LaunchImage.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "1x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "LaunchImage@2x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "2x" | ||||
|     }, | ||||
|     { | ||||
|       "filename" : "LaunchImage@3x.png", | ||||
|       "idiom" : "universal", | ||||
|       "scale" : "3x" | ||||
|     } | ||||
|   ], | ||||
|   "info" : { | ||||
|     "author" : "xcode", | ||||
|     "version" : 1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 69 B | 
							
								
								
									
										
											BIN
										
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 69 B | 
							
								
								
									
										
											BIN
										
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 69 B | 
							
								
								
									
										5
									
								
								moneymgr_mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | ||||
| # Launch Screen Assets | ||||
|  | ||||
| You can customize the launch screen with your own desired assets by replacing the image files in this directory. | ||||
|  | ||||
| You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. | ||||
| @@ -0,0 +1,44 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||||
|     <dependencies> | ||||
|         <deployment identifier="iOS"/> | ||||
|         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> | ||||
|     </dependencies> | ||||
|     <scenes> | ||||
|         <!--View Controller--> | ||||
|         <scene sceneID="EHf-IW-A2E"> | ||||
|             <objects> | ||||
|                 <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||||
|                     <layoutGuides> | ||||
|                         <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> | ||||
|                         <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> | ||||
|                     </layoutGuides> | ||||
|                     <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||||
|                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||
|                         <subviews> | ||||
|                             <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" image="LaunchBackground" translatesAutoresizingMaskIntoConstraints="NO" id="tWc-Dq-wcI"/> | ||||
|                             <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"></imageView> | ||||
|                         </subviews> | ||||
|                         <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||||
|                         <constraints> | ||||
|                             <constraint firstItem="YRO-k0-Ey4" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="3T2-ad-Qdv"/> | ||||
|                             <constraint firstItem="tWc-Dq-wcI" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="RPx-PI-7Xg"/> | ||||
|                             <constraint firstItem="tWc-Dq-wcI" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="SdS-ul-q2q"/> | ||||
|                             <constraint firstAttribute="trailing" secondItem="tWc-Dq-wcI" secondAttribute="trailing" id="Swv-Gf-Rwn"/> | ||||
|                             <constraint firstAttribute="trailing" secondItem="YRO-k0-Ey4" secondAttribute="trailing" id="TQA-XW-tRk"/> | ||||
|                             <constraint firstItem="YRO-k0-Ey4" firstAttribute="bottom" secondItem="Ze5-6b-2t3" secondAttribute="bottom" id="duK-uY-Gun"/> | ||||
|                             <constraint firstItem="tWc-Dq-wcI" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="kV7-tw-vXt"/> | ||||
|                             <constraint firstItem="YRO-k0-Ey4" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="xPn-NY-SIU"/> | ||||
|                         </constraints> | ||||
|                     </view> | ||||
|                 </viewController> | ||||
|                 <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||||
|             </objects> | ||||
|             <point key="canvasLocation" x="53" y="375"/> | ||||
|         </scene> | ||||
|     </scenes> | ||||
|     <resources> | ||||
|         <image name="LaunchImage" width="168" height="185"/> | ||||
|         <image name="LaunchBackground" width="1" height="1"/> | ||||
|     </resources> | ||||
| </document> | ||||
							
								
								
									
										26
									
								
								moneymgr_mobile/ios/Runner/Base.lproj/Main.storyboard
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> | ||||
|     <dependencies> | ||||
|         <deployment identifier="iOS"/> | ||||
|         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> | ||||
|     </dependencies> | ||||
|     <scenes> | ||||
|         <!--Flutter View Controller--> | ||||
|         <scene sceneID="tne-QT-ifu"> | ||||
|             <objects> | ||||
|                 <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> | ||||
|                     <layoutGuides> | ||||
|                         <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> | ||||
|                         <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> | ||||
|                     </layoutGuides> | ||||
|                     <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> | ||||
|                         <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> | ||||
|                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||||
|                         <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> | ||||
|                     </view> | ||||
|                 </viewController> | ||||
|                 <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> | ||||
|             </objects> | ||||
|         </scene> | ||||
|     </scenes> | ||||
| </document> | ||||
							
								
								
									
										51
									
								
								moneymgr_mobile/ios/Runner/Info.plist
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,51 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||||
| <plist version="1.0"> | ||||
| 	<dict> | ||||
| 		<key>CFBundleDevelopmentRegion</key> | ||||
| 		<string>$(DEVELOPMENT_LANGUAGE)</string> | ||||
| 		<key>CFBundleDisplayName</key> | ||||
| 		<string>Moneymgr Mobile</string> | ||||
| 		<key>CFBundleExecutable</key> | ||||
| 		<string>$(EXECUTABLE_NAME)</string> | ||||
| 		<key>CFBundleIdentifier</key> | ||||
| 		<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||||
| 		<key>CFBundleInfoDictionaryVersion</key> | ||||
| 		<string>6.0</string> | ||||
| 		<key>CFBundleName</key> | ||||
| 		<string>moneymgr</string> | ||||
| 		<key>CFBundlePackageType</key> | ||||
| 		<string>APPL</string> | ||||
| 		<key>CFBundleShortVersionString</key> | ||||
| 		<string>$(FLUTTER_BUILD_NAME)</string> | ||||
| 		<key>CFBundleSignature</key> | ||||
| 		<string>????</string> | ||||
| 		<key>CFBundleVersion</key> | ||||
| 		<string>$(FLUTTER_BUILD_NUMBER)</string> | ||||
| 		<key>LSRequiresIPhoneOS</key> | ||||
| 		<true/> | ||||
| 		<key>UILaunchStoryboardName</key> | ||||
| 		<string>LaunchScreen</string> | ||||
| 		<key>UIMainStoryboardFile</key> | ||||
| 		<string>Main</string> | ||||
| 		<key>UISupportedInterfaceOrientations</key> | ||||
| 		<array> | ||||
| 			<string>UIInterfaceOrientationPortrait</string> | ||||
| 			<string>UIInterfaceOrientationLandscapeLeft</string> | ||||
| 			<string>UIInterfaceOrientationLandscapeRight</string> | ||||
| 		</array> | ||||
| 		<key>UISupportedInterfaceOrientations~ipad</key> | ||||
| 		<array> | ||||
| 			<string>UIInterfaceOrientationPortrait</string> | ||||
| 			<string>UIInterfaceOrientationPortraitUpsideDown</string> | ||||
| 			<string>UIInterfaceOrientationLandscapeLeft</string> | ||||
| 			<string>UIInterfaceOrientationLandscapeRight</string> | ||||
| 		</array> | ||||
| 		<key>CADisableMinimumFrameDurationOnPhone</key> | ||||
| 		<true/> | ||||
| 		<key>UIApplicationSupportsIndirectInputEvents</key> | ||||
| 		<true/> | ||||
| 		<key>UIStatusBarHidden</key> | ||||
| 		<false/> | ||||
| 	</dict> | ||||
| </plist> | ||||
							
								
								
									
										1
									
								
								moneymgr_mobile/ios/Runner/Runner-Bridging-Header.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| #import "GeneratedPluginRegistrant.h" | ||||
							
								
								
									
										12
									
								
								moneymgr_mobile/ios/RunnerTests/RunnerTests.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,12 @@ | ||||
| import Flutter | ||||
| import UIKit | ||||
| import XCTest | ||||
|  | ||||
| class RunnerTests: XCTestCase { | ||||
|  | ||||
|   func testExample() { | ||||
|     // If you add code to the Runner application, consider adding tests here. | ||||
|     // See https://developer.apple.com/documentation/xctest for more information about using XCTest. | ||||
|   } | ||||
|  | ||||
| } | ||||
							
								
								
									
										113
									
								
								moneymgr_mobile/lib/main.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,113 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:flutter_native_splash/flutter_native_splash.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:logging/logging.dart'; | ||||
| import 'package:moneymgr_mobile/providers/settings.dart'; | ||||
| import 'package:moneymgr_mobile/services/router/router.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/prefs.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/secure_storage.dart'; | ||||
| import 'package:moneymgr_mobile/utils/provider_observer.dart'; | ||||
| import 'package:moneymgr_mobile/utils/theme_utils.dart'; | ||||
| import 'package:scanbot_sdk/scanbot_sdk.dart'; | ||||
|  | ||||
| // Inspired from https://github.com/dhafinrayhan/dummymart | ||||
|  | ||||
| Future<void> main() async { | ||||
|   WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); | ||||
|  | ||||
|   // We preserve the native splash screen, which will then removed once the main | ||||
|   // app is inserted to the widget tree. | ||||
|   FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); | ||||
|  | ||||
|   // Configure logger | ||||
|   Logger.root.level = Level.ALL; // defaults to Level.INFO | ||||
|   Logger.root.onRecord.listen( | ||||
|     (record) => | ||||
|         // ignore: avoid_print | ||||
|         print('${record.level.name}: ${record.time}: ${record.message}'), | ||||
|   ); | ||||
|  | ||||
|   // Initialize scanbot sdk | ||||
|   var config = ScanbotSdkConfig( | ||||
|     loggingEnabled: true, | ||||
|     allowGpuAcceleration: true, | ||||
|     allowXnnpackAcceleration: true, | ||||
|   ); | ||||
|   ScanbotSdk.initScanbotSdk(config); | ||||
|  | ||||
|   runApp( | ||||
|     ProviderScope( | ||||
|       observers: [AppProviderObserver()], | ||||
|       child: const MoneyMgrApp(), | ||||
|     ), | ||||
|   ); | ||||
| } | ||||
|  | ||||
| class MoneyMgrApp extends StatelessWidget { | ||||
|   const MoneyMgrApp({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return const _EagerInitialization(child: _MainApp()); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _EagerInitialization extends ConsumerWidget { | ||||
|   const _EagerInitialization({required this.child}); | ||||
|  | ||||
|   final Widget child; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final values = [ref.watch(prefsProvider), ref.watch(secureStorageProvider)]; | ||||
|  | ||||
|     if (values.every((value) => value.hasValue)) { | ||||
|       return child; | ||||
|     } | ||||
|  | ||||
|     return const SizedBox(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _MainApp extends StatefulHookConsumerWidget { | ||||
|   const _MainApp(); | ||||
|  | ||||
|   @override | ||||
|   ConsumerState<_MainApp> createState() => _MainAppState(); | ||||
| } | ||||
|  | ||||
| class _MainAppState extends ConsumerState<_MainApp> { | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     FlutterNativeSplash.remove(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final router = ref.watch(routerProvider); | ||||
|     final themeMode = ref.watch(currentThemeModeProvider); | ||||
|  | ||||
|     final (lightTheme, darkTheme) = useMemoized( | ||||
|       () => createDualThemeData( | ||||
|         seedColor: Colors.blue, | ||||
|         useMaterial3: true, | ||||
|         transformer: (data) => data.copyWith( | ||||
|           inputDecorationTheme: const InputDecorationTheme( | ||||
|             border: OutlineInputBorder(), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|     return MaterialApp.router( | ||||
|       title: 'MoneyMgr', | ||||
|       themeMode: themeMode, | ||||
|       theme: lightTheme, | ||||
|       darkTheme: darkTheme, | ||||
|       routerConfig: router, | ||||
|       debugShowCheckedModeBanner: false, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										89
									
								
								moneymgr_mobile/lib/providers/auth_state.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,89 @@ | ||||
| import 'package:moneymgr_mobile/services/api/api_client.dart'; | ||||
| import 'package:moneymgr_mobile/services/api/api_token.dart'; | ||||
| import 'package:moneymgr_mobile/services/api/auth_api.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/prefs.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/secure_storage.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
|  | ||||
| import '../services/router/routes_list.dart'; | ||||
|  | ||||
| part 'auth_state.g.dart'; | ||||
|  | ||||
| /// The current authentication state of the app. | ||||
| /// | ||||
| /// This notifier is responsible for saving/removing the token and profile info | ||||
| /// to the storage through the [setAuthToken] and [logout] methods. | ||||
| @riverpod | ||||
| class CurrentAuthState extends _$CurrentAuthState { | ||||
|   @override | ||||
|   AuthState build() { | ||||
|     final secureStorage = ref.watch(secureStorageProvider).requireValue; | ||||
|     final token = secureStorage.token(); | ||||
|     return token != null ? AuthState.authenticated : AuthState.unauthenticated; | ||||
|   } | ||||
|  | ||||
|   /// Attempts to authenticate with [token] and saves the token and profile info to storage. | ||||
|   /// Will invalidate the state if success and throw an exception in case of failure | ||||
|   Future<void> setAuthToken(ApiToken token) async { | ||||
|     // Attempt to use provided token | ||||
|     await ApiClient( | ||||
|       token: token, | ||||
|       prefs: await ref.watch(prefsProvider.future), | ||||
|     ).authInfo(); | ||||
|  | ||||
|     final secureStorage = ref.read(secureStorageProvider).requireValue; | ||||
|     await secureStorage.setToken(token); | ||||
|  | ||||
|     ref | ||||
|         // Invalidate the state so the auth state will be updated to authenticated. | ||||
|         .invalidateSelf(); | ||||
|   } | ||||
|  | ||||
|   /// Logs out, deletes the saved token and profile info from storage, and invalidates | ||||
|   /// the state. | ||||
|   Future<void> logout() async { | ||||
|     final prefs = ref.read(prefsProvider).requireValue; | ||||
|  | ||||
|     final secureStorage = ref.read(secureStorageProvider).requireValue; | ||||
|     await secureStorage.removeToken(); | ||||
|  | ||||
|     prefs.clearServerConfig(); | ||||
|     prefs.clearAuthInfo(); | ||||
|  | ||||
|     ref | ||||
|     // Invalidate the state so the auth state will be updated to authenticated. | ||||
|         .invalidateSelf(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// The possible authentication states of the app. | ||||
| enum AuthState { | ||||
|   unknown(redirectPath: homePage, allowedPaths: [homePage]), | ||||
|   unauthenticated( | ||||
|     redirectPath: authPage, | ||||
|     allowedPaths: [authPage, qrAuthPath, manualAuthPage, settingsPage], | ||||
|   ), | ||||
|   authenticated( | ||||
|     redirectPath: homePage, | ||||
|     allowedPaths: null, | ||||
|     forbiddenPaths: [authPage, manualAuthPage], | ||||
|   ); | ||||
|  | ||||
|   const AuthState({ | ||||
|     required this.redirectPath, | ||||
|     required this.allowedPaths, | ||||
|     this.forbiddenPaths, | ||||
|   }); | ||||
|  | ||||
|   /// The target path to redirect when the current route is not allowed in this | ||||
|   /// auth state. | ||||
|   final String redirectPath; | ||||
|  | ||||
|   /// List of paths allowed when the app is in this auth state. May be set to null if there is no | ||||
|   /// restriction applicable | ||||
|   final List<String>? allowedPaths; | ||||
|  | ||||
|   /// List of paths not allowed when the app is in this auth state. May be set to null if there is no | ||||
|   /// restriction applicable | ||||
|   final List<String>? forbiddenPaths; | ||||
| } | ||||
							
								
								
									
										37
									
								
								moneymgr_mobile/lib/providers/settings.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
|  | ||||
| import '../../../services/storage/prefs.dart'; | ||||
|  | ||||
| part 'settings.g.dart'; | ||||
|  | ||||
| /// The current theme mode of the app. | ||||
| /// | ||||
| /// When this provider is first read, it will read the saved value from storage, | ||||
| /// and defaults to [ThemeMode.system] if the theme mode has not been set before. | ||||
| @riverpod | ||||
| class CurrentThemeMode extends _$CurrentThemeMode { | ||||
|   @override | ||||
|   ThemeMode build() { | ||||
|     final prefs = ref.watch(prefsProvider).requireValue; | ||||
|  | ||||
|     // Load the saved theme mode setting from shared preferences. | ||||
|     final themeModeName = prefs.getString('themeMode'); | ||||
|  | ||||
|     // Return [ThemeMode] based on the saved setting, or [ThemeMode.system] | ||||
|     // if there's no saved setting yet. | ||||
|     return ThemeMode.values.singleWhere( | ||||
|           (themeMode) => themeMode.name == themeModeName, | ||||
|       orElse: () => ThemeMode.system, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   void set(ThemeMode themeMode) { | ||||
|     final prefs = ref.read(prefsProvider).requireValue; | ||||
|  | ||||
|     // Save the new theme mode to shared preferences. | ||||
|     prefs.setString('themeMode', themeMode.name); | ||||
|  | ||||
|     ref.invalidateSelf(); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										50
									
								
								moneymgr_mobile/lib/routes/login/base_auth_page.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,50 @@ | ||||
| import 'package:flextras/flextras.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gutter/flutter_gutter.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
|  | ||||
| import '../../services/router/routes_list.dart'; | ||||
|  | ||||
| class BaseAuthPage extends StatelessWidget { | ||||
|   final List<Widget> children; | ||||
|   final String? title; | ||||
|   final bool? showSettings; | ||||
|  | ||||
|   const BaseAuthPage({ | ||||
|     super.key, | ||||
|     required this.children, | ||||
|     this.title, | ||||
|     this.showSettings, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     void onSettingsPressed() => context.push(settingsPage); | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: Text(title ?? 'MoneyMgr'), | ||||
|         actions: [ | ||||
|           // Settings button | ||||
|           showSettings != false | ||||
|               ? IconButton( | ||||
|                   onPressed: onSettingsPressed, | ||||
|                   icon: const Icon(Icons.settings), | ||||
|                 ) | ||||
|               : Container(), | ||||
|         ], | ||||
|       ), | ||||
|       body: SingleChildScrollView( | ||||
|         child: IntrinsicHeight( | ||||
|           child: SeparatedColumn( | ||||
|             padding: EdgeInsets.all(context.gutter), | ||||
|             separatorBuilder: () => const Gutter(), | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             crossAxisAlignment: CrossAxisAlignment.stretch, | ||||
|             children: children, | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										66
									
								
								moneymgr_mobile/lib/routes/login/login_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,66 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gutter/flutter_gutter.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:moneymgr_mobile/routes/login/base_auth_page.dart'; | ||||
| import 'package:moneymgr_mobile/services/router/routes_list.dart'; | ||||
|  | ||||
| class LoginScreen extends HookConsumerWidget { | ||||
|   const LoginScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     return BaseAuthPage( | ||||
|       children: [ | ||||
|         Gutter(scaleFactor: 3), | ||||
|         Text( | ||||
|           "This application requires a token from MoneyMgr to be used.\n\nPlease create a token  on your Money Manager instance and make sure to click on \"For mobile app\" button. You can then enter here generated credentials.", | ||||
|           textAlign: TextAlign.justify, | ||||
|         ), | ||||
|         Expanded(child: Container()), | ||||
|         _LoginChoice( | ||||
|           route: manualAuthPage, | ||||
|           icon: Icons.edit_document, | ||||
|           label: "Enter manually authentication information", | ||||
|         ), | ||||
|         _LoginChoice( | ||||
|           route: qrAuthPath, | ||||
|           icon: Icons.qr_code_2, | ||||
|           label: "Scan authentication Qr Code", | ||||
|         ), | ||||
|         Gutter(scaleFactor: 3), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class _LoginChoice extends StatelessWidget { | ||||
|   const _LoginChoice({ | ||||
|     required this.route, | ||||
|     required this.label, | ||||
|     required this.icon, | ||||
|   }); | ||||
|  | ||||
|   final String route; | ||||
|   final String label; | ||||
|   final IconData icon; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return FilledButton( | ||||
|       onPressed: () => context.push(route), | ||||
|       style: ButtonStyle( | ||||
|         padding: WidgetStatePropertyAll( | ||||
|           EdgeInsetsGeometry.symmetric(vertical: 20.0, horizontal: 30.0), | ||||
|         ), | ||||
|       ), | ||||
|       child: Row( | ||||
|         children: [ | ||||
|           Icon(icon, size: 25.0), | ||||
|           Gutter(), | ||||
|           Flexible(child: Text(label)), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										91
									
								
								moneymgr_mobile/lib/routes/login/manual_auth_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,91 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_gutter/flutter_gutter.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:logging/logging.dart'; | ||||
| import 'package:moneymgr_mobile/providers/auth_state.dart'; | ||||
| import 'package:moneymgr_mobile/routes/login/base_auth_page.dart'; | ||||
| import 'package:moneymgr_mobile/services/api/api_token.dart'; | ||||
| import 'package:moneymgr_mobile/services/router/routes_list.dart'; | ||||
| import 'package:moneymgr_mobile/utils/extensions.dart'; | ||||
| import 'package:moneymgr_mobile/widgets/app_button.dart'; | ||||
|  | ||||
| class ManualAuthScreen extends HookConsumerWidget { | ||||
|   const ManualAuthScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     var apiUrlController = useTextEditingController(); | ||||
|     var tokenIdController = useTextEditingController(); | ||||
|     var tokenValueController = useTextEditingController(); | ||||
|  | ||||
|     Future<void> onSubmit() async { | ||||
|       try { | ||||
|         await ref | ||||
|             .read(currentAuthStateProvider.notifier) | ||||
|             .setAuthToken( | ||||
|               ApiToken( | ||||
|                 apiUrl: apiUrlController.text, | ||||
|                 tokenId: int.tryParse(tokenIdController.text) ?? 1, | ||||
|                 tokenValue: tokenValueController.text, | ||||
|               ), | ||||
|             ); | ||||
|  | ||||
|         if (context.mounted) { | ||||
|           if (context.canPop()) context.pop(); | ||||
|           context.replace(profilePage); | ||||
|         } | ||||
|       } catch (e, s) { | ||||
|         Logger.root.severe("Failed to authenticate user! $e $s"); | ||||
|         if (context.mounted) { | ||||
|           context.showTextSnackBar("Failed to authenticate user! $e"); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return BaseAuthPage( | ||||
|       title: "Manual authentication", | ||||
|       showSettings: false, | ||||
|       children: [ | ||||
|         Gutter(scaleFactor: 3), | ||||
|         Text( | ||||
|           "On this screen you can manually enter authentication information.", | ||||
|         ), | ||||
|         Gutter(), | ||||
|         TextField( | ||||
|           controller: apiUrlController, | ||||
|           keyboardType: TextInputType.url, | ||||
|           decoration: const InputDecoration( | ||||
|             labelText: 'API URL', | ||||
|             helperText: "Format: http://moneymgr.corp.com/api", | ||||
|           ), | ||||
|           textInputAction: TextInputAction.next, | ||||
|         ), | ||||
|         Gutter(), | ||||
|         TextField( | ||||
|           controller: tokenIdController, | ||||
|           keyboardType: TextInputType.number, | ||||
|           decoration: const InputDecoration( | ||||
|             labelText: 'Token ID', | ||||
|             helperText: "The ID of the token", | ||||
|           ), | ||||
|           textInputAction: TextInputAction.next, | ||||
|         ), | ||||
|         Gutter(), | ||||
|         TextField( | ||||
|           controller: tokenValueController, | ||||
|           keyboardType: TextInputType.text, | ||||
|           decoration: const InputDecoration( | ||||
|             labelText: 'Token value', | ||||
|             helperText: "The value of the token itself", | ||||
|           ), | ||||
|           textInputAction: TextInputAction.done, | ||||
|         ), | ||||
|         Gutter(), | ||||
|         AppButton(onPressed: onSubmit, label: "Submit"), | ||||
|         Gutter(scaleFactor: 3), | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										80
									
								
								moneymgr_mobile/lib/routes/login/qr_auth_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,80 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_hooks/flutter_hooks.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:logging/logging.dart'; | ||||
| import 'package:mobile_scanner/mobile_scanner.dart'; | ||||
| import 'package:moneymgr_mobile/providers/auth_state.dart'; | ||||
| import 'package:moneymgr_mobile/services/api/api_token.dart'; | ||||
| import 'package:moneymgr_mobile/services/router/routes_list.dart'; | ||||
| import 'package:moneymgr_mobile/utils/extensions.dart'; | ||||
|  | ||||
| class QrAuthScreen extends HookConsumerWidget { | ||||
|   const QrAuthScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final loading = useState(false); | ||||
|  | ||||
|     handleCapture(BarcodeCapture barcodes) async { | ||||
|       if (loading.value) return; | ||||
|  | ||||
|       if (barcodes.barcodes.length != 1) return; | ||||
|       final b = barcodes.barcodes[0]; | ||||
|  | ||||
|       if (b.format != BarcodeFormat.qrCode) { | ||||
|         context.showTextSnackBar( | ||||
|           "Only QrCode are supported!", | ||||
|           duration: Duration(seconds: 1), | ||||
|         ); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       final value = b.rawValue ?? ""; | ||||
|  | ||||
|       Logger.root.finest("Decoded QrCode: $value"); | ||||
|  | ||||
|       if (!value.startsWith("moneymgr://")) { | ||||
|         context.showTextSnackBar( | ||||
|           "Not a MoneyMgr Qr Code!", | ||||
|           duration: Duration(seconds: 1), | ||||
|         ); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // Decode token | ||||
|       final uri = Uri.parse( | ||||
|         value.replaceFirst("moneymgr://", "http://test.com/?"), | ||||
|       ); | ||||
|       final token = ApiToken( | ||||
|         apiUrl: uri.queryParameters["api"] ?? "", | ||||
|         tokenId: int.tryParse(uri.queryParameters["id"] ?? "") ?? 0, | ||||
|         tokenValue: uri.queryParameters["secret"] ?? "", | ||||
|       ); | ||||
|  | ||||
|       // Attempt to authenticate using token | ||||
|       try { | ||||
|         loading.value = true; | ||||
|  | ||||
|         await ref.read(currentAuthStateProvider.notifier).setAuthToken(token); | ||||
|  | ||||
|         if (context.mounted) { | ||||
|           if (context.canPop()) context.pop(); | ||||
|           context.replace(profilePage); | ||||
|         } | ||||
|       } catch (e, s) { | ||||
|         Logger.root.severe("Failed to authenticate user! $e $s"); | ||||
|         if (context.mounted) { | ||||
|           context.showTextSnackBar("Failed to authenticate user! $e"); | ||||
|         } | ||||
|       } finally { | ||||
|         loading.value = false; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar(title: Text('QR Authentication')), | ||||
|       body: MobileScanner(onDetect: handleCapture), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										112
									
								
								moneymgr_mobile/lib/routes/profile/profile_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,112 @@ | ||||
| import 'package:alert_dialog/alert_dialog.dart'; | ||||
| import 'package:confirm_dialog/confirm_dialog.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:logging/logging.dart'; | ||||
| import 'package:moneymgr_mobile/providers/auth_state.dart'; | ||||
| import 'package:moneymgr_mobile/services/api/api_client.dart'; | ||||
| import 'package:moneymgr_mobile/services/router/routes_list.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/prefs.dart'; | ||||
|  | ||||
| class ProfileScreen extends HookConsumerWidget { | ||||
|   const ProfileScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final data = ref.watch(prefsProvider); | ||||
|     final api = ref.watch(apiServiceProvider); | ||||
|     if (data.value == null) return CircularProgressIndicator(); | ||||
|  | ||||
|     final profile = data.value?.authInfo(); | ||||
|  | ||||
|     void onSettingsPressed() => context.push(settingsPage); | ||||
|  | ||||
|     handleSignOut() async { | ||||
|       try { | ||||
|         if (!await confirm( | ||||
|           context, | ||||
|           content: Text("Do you really want to sign out of your account?"), | ||||
|         )) { | ||||
|           return; | ||||
|         } | ||||
|  | ||||
|         await ref.read(currentAuthStateProvider.notifier).logout(); | ||||
|       } catch (e, s) { | ||||
|         Logger.root.warning("Failed to sign out! $e $s"); | ||||
|  | ||||
|         if (context.mounted) { | ||||
|           await alert(context, content: Text("Failed to sign you out! $e")); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: Text("Profile"), | ||||
|         actions: [ | ||||
|           IconButton( | ||||
|             onPressed: onSettingsPressed, | ||||
|             icon: const Icon(Icons.settings), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|       body: ListView( | ||||
|         children: [ | ||||
|           ListEntry( | ||||
|             title: "Server URL", | ||||
|             value: api?.token.apiUrl, | ||||
|             icon: Icons.link, | ||||
|           ), | ||||
|           ListEntry( | ||||
|             title: "Token ID", | ||||
|             value: api?.token.tokenId.toString(), | ||||
|             icon: Icons.key, | ||||
|           ), | ||||
|           ListEntry( | ||||
|             title: "User ID", | ||||
|             value: profile?.id.toString(), | ||||
|             icon: Icons.perm_contact_calendar_outlined, | ||||
|           ), | ||||
|           ListEntry( | ||||
|             title: "User name", | ||||
|             value: profile?.name, | ||||
|             icon: Icons.person, | ||||
|           ), | ||||
|           ListEntry(title: "User mail", value: profile?.mail, icon: Icons.mail), | ||||
|           Divider(), | ||||
|           ListEntry( | ||||
|             title: "Sign out", | ||||
|             icon: Icons.logout, | ||||
|             onTap: handleSignOut, | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class ListEntry extends StatelessWidget { | ||||
|   final String title; | ||||
|   final String? value; | ||||
|   final IconData icon; | ||||
|   final Function()? onTap; | ||||
|  | ||||
|   const ListEntry({ | ||||
|     super.key, | ||||
|     required this.title, | ||||
|     this.value, | ||||
|     required this.icon, | ||||
|     this.onTap, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListTile( | ||||
|       title: Text(title), | ||||
|       subtitle: value != null ? Text(value!) : null, | ||||
|       leading: Icon(icon), | ||||
|       onTap: onTap, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										126
									
								
								moneymgr_mobile/lib/routes/scan/scan_screen.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,126 @@ | ||||
| import 'dart:io'; | ||||
| import 'dart:typed_data'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:logging/logging.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/expenses.dart'; | ||||
| import 'package:moneymgr_mobile/widgets/expense_editor.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
| import 'package:scanbot_sdk/scanbot_sdk.dart'; | ||||
| import 'package:scanbot_sdk/scanbot_sdk_ui_v2.dart' hide IconButton, EdgeInsets; | ||||
|  | ||||
| part 'scan_screen.g.dart'; | ||||
|  | ||||
| /// Scan a document & return generated PDF as byte file | ||||
| @riverpod | ||||
| Future<Uint8List?> _scanDocument(Ref ref) async { | ||||
|   var configuration = DocumentScanningFlow( | ||||
|     appearance: DocumentFlowAppearanceConfiguration( | ||||
|       statusBarMode: StatusBarMode.DARK, | ||||
|     ), | ||||
|     cleanScanningSession: true, | ||||
|     outputSettings: DocumentScannerOutputSettings(pagesScanLimit: 1), | ||||
|     screens: DocumentScannerScreens( | ||||
|       review: ReviewScreenConfiguration(enabled: false), | ||||
|     ), | ||||
|   ); | ||||
|   var documentResult = await ScanbotSdkUiV2.startDocumentScanner(configuration); | ||||
|  | ||||
|   if (documentResult.status != OperationStatus.OK) { | ||||
|     throw Exception("Scanner failed with status ${documentResult.status}"); | ||||
|   } | ||||
|  | ||||
|   // Convert result to PDF | ||||
|   var result = await ScanbotSdk.document.createPDFForDocument( | ||||
|     PDFFromDocumentParams( | ||||
|       documentID: documentResult.data!.uuid, | ||||
|       pdfConfiguration: PdfConfiguration(), | ||||
|     ), | ||||
|   ); | ||||
|   final pdfPath = result.pdfFileUri.replaceFirst("file://", ""); | ||||
|   return File(pdfPath).readAsBytes(); | ||||
| } | ||||
|  | ||||
| class ScanScreen extends HookConsumerWidget { | ||||
|   const ScanScreen({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final scanDocProvider = ref.watch(_scanDocumentProvider); | ||||
|     final expenses = ref.watch(expensesProvider).requireValue; | ||||
|  | ||||
|     restartScan() async { | ||||
|       try { | ||||
|         final val = ref.refresh(_scanDocumentProvider); | ||||
|         Logger.root.info("Load again startup result: $val"); | ||||
|       } catch (e, s) { | ||||
|         Logger.root.shout("Failed to try again startup loading! $e $s"); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return Padding( | ||||
|       padding: const EdgeInsets.all(8.0), | ||||
|       child: switch (scanDocProvider) { | ||||
|         AsyncData(:final value) when value != null => ExpenseEditor( | ||||
|           file: value, | ||||
|           onFinished: (expense) async { | ||||
|             await expenses.add( | ||||
|               info: expense, | ||||
|               fileContent: value, | ||||
|               fileMimeType: "application/pdf", | ||||
|             ); | ||||
|             restartScan(); | ||||
|           }, | ||||
|           onRescan: restartScan, | ||||
|         ), | ||||
|  | ||||
|         // No data | ||||
|         AsyncData(:final value) when value == null => ScanErrorScreen( | ||||
|           message: "No document scanned!", | ||||
|           onTryAgain: restartScan, | ||||
|         ), | ||||
|  | ||||
|         // Error | ||||
|         AsyncError(:final error) => ScanErrorScreen( | ||||
|           message: error.toString(), | ||||
|           onTryAgain: restartScan, | ||||
|         ), | ||||
|         _ => const Center(child: CircularProgressIndicator()), | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class ScanErrorScreen extends StatelessWidget { | ||||
|   final String message; | ||||
|   final Function() onTryAgain; | ||||
|  | ||||
|   const ScanErrorScreen({ | ||||
|     super.key, | ||||
|     required this.message, | ||||
|     required this.onTryAgain, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Center( | ||||
|       child: Column( | ||||
|         mainAxisAlignment: MainAxisAlignment.center, | ||||
|         crossAxisAlignment: CrossAxisAlignment.center, | ||||
|         children: [ | ||||
|           Spacer(flex: 5), | ||||
|           Text("An error occurred while scanning"), | ||||
|           Spacer(flex: 1), | ||||
|           Text(message, textAlign: TextAlign.center), | ||||
|           Spacer(flex: 1), | ||||
|           MaterialButton( | ||||
|             onPressed: onTryAgain, | ||||
|             child: Text("Try again".toUpperCase()), | ||||
|           ), | ||||
|           Spacer(flex: 5), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										86
									
								
								moneymgr_mobile/lib/routes/scan_details/scan_details.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,86 @@ | ||||
| import 'dart:typed_data'; | ||||
|  | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:go_router/go_router.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:logging/logging.dart'; | ||||
| import 'package:moneymgr_mobile/services/storage/expenses.dart'; | ||||
| import 'package:moneymgr_mobile/utils/extensions.dart'; | ||||
| import 'package:moneymgr_mobile/widgets/expense_editor.dart'; | ||||
| import 'package:moneymgr_mobile/widgets/full_screen_error.dart'; | ||||
| import 'package:moneymgr_mobile/widgets/loading_scaffold.dart'; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
|  | ||||
| part 'scan_details.g.dart'; | ||||
|  | ||||
| @riverpod | ||||
| Future<(Expense, Uint8List)?> _getExpense(Ref ref, {required int id}) async { | ||||
|   final expProvider = ref.watch(expensesProvider).requireValue; | ||||
|   final expense = await expProvider.getById(id); | ||||
|   if (expense == null) return null; | ||||
|   final file = await expProvider.loadFile(expense); | ||||
|  | ||||
|   return (expense, file); | ||||
| } | ||||
|  | ||||
| class ScanDetailScreen extends HookConsumerWidget { | ||||
|   final int id; | ||||
|  | ||||
|   const ScanDetailScreen({super.key, required this.id}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
|     final expenses = ref.watch(expensesProvider).requireValue; | ||||
|     final expense = ref.watch(_getExpenseProvider(id: id)); | ||||
|  | ||||
|     handleUpdate(BaseExpenseInfo newInfo) async { | ||||
|       try { | ||||
|         await expenses.updateExpense(expense.requireValue!.$1, newInfo); | ||||
|         if (context.mounted) { | ||||
|           context.pop(); | ||||
|         } | ||||
|  | ||||
|         ref.invalidate(expensesProvider); | ||||
|       } catch (e, s) { | ||||
|         Logger.root.warning("Failed to update expense! $e$s"); | ||||
|         if (context.mounted) { | ||||
|           context.showTextSnackBar("Failed to update expense! $e"); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     handleDelete() async { | ||||
|       try { | ||||
|         await expenses.deleteExpense(expense.requireValue!.$1); | ||||
|         if (context.mounted) { | ||||
|           context.pop(); | ||||
|         } | ||||
|  | ||||
|         ref.invalidate(expensesProvider); | ||||
|       } catch (e, s) { | ||||
|         Logger.root.warning("Failed to delete expense! $e$s"); | ||||
|         if (context.mounted) { | ||||
|           context.showTextSnackBar("Failed to delete expense! $e"); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return switch (expense) { | ||||
|       AsyncData(:final value) when value == null => FullScreenError( | ||||
|         message: "Expense does not exists!", | ||||
|         error: 'NONE', | ||||
|       ), | ||||
|       AsyncData(:final value) => ExpenseEditor( | ||||
|         file: value!.$2, | ||||
|         initialData: value.$1.baseExpense, | ||||
|         onFinished: handleUpdate, | ||||
|         onDelete: handleDelete, | ||||
|       ), | ||||
|       AsyncError(:final error) => FullScreenError( | ||||
|         message: "Failed to load expense information!", | ||||
|         error: error.toString(), | ||||
|       ), | ||||
|       _ => LoadingScaffold(title: "Expense $id"), | ||||
|     }; | ||||
|   } | ||||
| } | ||||