diff --git a/.github/actions/setup_build/action.yml b/.github/actions/setup_build/action.yml
index 816b28ae7..6790df13e 100644
--- a/.github/actions/setup_build/action.yml
+++ b/.github/actions/setup_build/action.yml
@@ -9,7 +9,7 @@ inputs:
default: 'zulu'
flutter-version:
description: 'Flutter Version'
- default: '3.24.4'
+ default: '3.27.0'
ruby-version:
description: 'Ruby Version'
default: '3.3'
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
index e94e04bd9..bac225eba 100644
--- a/.github/workflows/testing.yml
+++ b/.github/workflows/testing.yml
@@ -20,7 +20,7 @@ jobs:
- name: Run Tests
run: flutter test --coverage
- name: Upload coverage reports to Codecov
- uses: codecov/codecov-action@v5.0.7
+ uses: codecov/codecov-action@v5.1.2
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/lcov.info
diff --git a/.gitignore b/.gitignore
index 6aa9ac0ff..c41f70456 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,3 +75,7 @@ local.properties
.gradle/
dependency_cache.json
+
+android/app/google-services2.json
+
+ios/Runner/Assets.xcassets/LaunchImage.imageset/
diff --git a/VERSION b/VERSION
index bb83058ed..9256e2880 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.12
+1.0.13
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index dbb6882ef..36e73c1cf 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -109,8 +109,8 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:2.0.21"
testImplementation "junit:junit:4.13.2"
// https://developer.android.com/jetpack/androidx/releases/test/#1.2.0
- coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.3"
- implementation(platform("com.google.firebase:firebase-bom:33.6.0"))
+ coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.1.4"
+ implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
}
configurations.configureEach {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 6f3bd592b..917800c61 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -101,7 +101,9 @@
android:foregroundServiceType="connectedDevice"
android:stopWithTask="true" />
-
+
diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png
index 7fa40868f..33e1f1662 100644
Binary files a/android/app/src/main/ic_launcher-playstore.png and b/android/app/src/main/ic_launcher-playstore.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/android12splash.png b/android/app/src/main/res/drawable-hdpi/android12splash.png
index 931886a96..d103ec4b5 100644
Binary files a/android/app/src/main/res/drawable-hdpi/android12splash.png and b/android/app/src/main/res/drawable-hdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/ic_notification.png b/android/app/src/main/res/drawable-hdpi/ic_notification.png
index 240f9d6ab..0df0327f2 100644
Binary files a/android/app/src/main/res/drawable-hdpi/ic_notification.png and b/android/app/src/main/res/drawable-hdpi/ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/splash.png b/android/app/src/main/res/drawable-hdpi/splash.png
index 931886a96..d103ec4b5 100644
Binary files a/android/app/src/main/res/drawable-hdpi/splash.png and b/android/app/src/main/res/drawable-hdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/android12splash.png b/android/app/src/main/res/drawable-mdpi/android12splash.png
index 1c950c1dc..063f84471 100644
Binary files a/android/app/src/main/res/drawable-mdpi/android12splash.png and b/android/app/src/main/res/drawable-mdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_notification.png b/android/app/src/main/res/drawable-mdpi/ic_notification.png
index a90047022..440c81ead 100644
Binary files a/android/app/src/main/res/drawable-mdpi/ic_notification.png and b/android/app/src/main/res/drawable-mdpi/ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/splash.png b/android/app/src/main/res/drawable-mdpi/splash.png
index 1c950c1dc..063f84471 100644
Binary files a/android/app/src/main/res/drawable-mdpi/splash.png and b/android/app/src/main/res/drawable-mdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night-hdpi/android12splash.png b/android/app/src/main/res/drawable-night-hdpi/android12splash.png
index cf5a52547..24fe2ac3b 100644
Binary files a/android/app/src/main/res/drawable-night-hdpi/android12splash.png and b/android/app/src/main/res/drawable-night-hdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-hdpi/splash.png b/android/app/src/main/res/drawable-night-hdpi/splash.png
index cf5a52547..24fe2ac3b 100644
Binary files a/android/app/src/main/res/drawable-night-hdpi/splash.png and b/android/app/src/main/res/drawable-night-hdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night-mdpi/android12splash.png b/android/app/src/main/res/drawable-night-mdpi/android12splash.png
index d8ad0684b..bc73c97b1 100644
Binary files a/android/app/src/main/res/drawable-night-mdpi/android12splash.png and b/android/app/src/main/res/drawable-night-mdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-mdpi/splash.png b/android/app/src/main/res/drawable-night-mdpi/splash.png
index d8ad0684b..bc73c97b1 100644
Binary files a/android/app/src/main/res/drawable-night-mdpi/splash.png and b/android/app/src/main/res/drawable-night-mdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night-v21/background.png b/android/app/src/main/res/drawable-night-v21/background.png
index 4c1cab129..fbce56fe7 100644
Binary files a/android/app/src/main/res/drawable-night-v21/background.png and b/android/app/src/main/res/drawable-night-v21/background.png differ
diff --git a/android/app/src/main/res/drawable-night-xhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xhdpi/android12splash.png
index 524ef644b..63be6a344 100644
Binary files a/android/app/src/main/res/drawable-night-xhdpi/android12splash.png and b/android/app/src/main/res/drawable-night-xhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xhdpi/splash.png b/android/app/src/main/res/drawable-night-xhdpi/splash.png
index 524ef644b..63be6a344 100644
Binary files a/android/app/src/main/res/drawable-night-xhdpi/splash.png and b/android/app/src/main/res/drawable-night-xhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png
index baea7652e..a509cfcb4 100644
Binary files a/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png and b/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xxhdpi/splash.png b/android/app/src/main/res/drawable-night-xxhdpi/splash.png
index baea7652e..a509cfcb4 100644
Binary files a/android/app/src/main/res/drawable-night-xxhdpi/splash.png and b/android/app/src/main/res/drawable-night-xxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png b/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png
index f4d7fa63e..523e79423 100644
Binary files a/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png and b/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-night-xxxhdpi/splash.png b/android/app/src/main/res/drawable-night-xxxhdpi/splash.png
index f4d7fa63e..523e79423 100644
Binary files a/android/app/src/main/res/drawable-night-xxxhdpi/splash.png and b/android/app/src/main/res/drawable-night-xxxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-night/background.png b/android/app/src/main/res/drawable-night/background.png
index 4c1cab129..fbce56fe7 100644
Binary files a/android/app/src/main/res/drawable-night/background.png and b/android/app/src/main/res/drawable-night/background.png differ
diff --git a/android/app/src/main/res/drawable-v21/background.png b/android/app/src/main/res/drawable-v21/background.png
index 3107d37fa..8e2140480 100644
Binary files a/android/app/src/main/res/drawable-v21/background.png and b/android/app/src/main/res/drawable-v21/background.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/android12splash.png b/android/app/src/main/res/drawable-xhdpi/android12splash.png
index 3e5026aa4..e048cf89a 100644
Binary files a/android/app/src/main/res/drawable-xhdpi/android12splash.png and b/android/app/src/main/res/drawable-xhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_notification.png b/android/app/src/main/res/drawable-xhdpi/ic_notification.png
index a11449906..0d5cc3c80 100644
Binary files a/android/app/src/main/res/drawable-xhdpi/ic_notification.png and b/android/app/src/main/res/drawable-xhdpi/ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/splash.png b/android/app/src/main/res/drawable-xhdpi/splash.png
index 3e5026aa4..e048cf89a 100644
Binary files a/android/app/src/main/res/drawable-xhdpi/splash.png and b/android/app/src/main/res/drawable-xhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/android12splash.png b/android/app/src/main/res/drawable-xxhdpi/android12splash.png
index e6cf932b2..3a2767fb6 100644
Binary files a/android/app/src/main/res/drawable-xxhdpi/android12splash.png and b/android/app/src/main/res/drawable-xxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_notification.png b/android/app/src/main/res/drawable-xxhdpi/ic_notification.png
index 6e1a64770..0a0f66bd7 100644
Binary files a/android/app/src/main/res/drawable-xxhdpi/ic_notification.png and b/android/app/src/main/res/drawable-xxhdpi/ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/splash.png b/android/app/src/main/res/drawable-xxhdpi/splash.png
index e6cf932b2..3a2767fb6 100644
Binary files a/android/app/src/main/res/drawable-xxhdpi/splash.png and b/android/app/src/main/res/drawable-xxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/android12splash.png b/android/app/src/main/res/drawable-xxxhdpi/android12splash.png
index ff3733b21..be75147b0 100644
Binary files a/android/app/src/main/res/drawable-xxxhdpi/android12splash.png and b/android/app/src/main/res/drawable-xxxhdpi/android12splash.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png b/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png
index f81858fa8..0050b0fd4 100644
Binary files a/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png and b/android/app/src/main/res/drawable-xxxhdpi/ic_notification.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/splash.png b/android/app/src/main/res/drawable-xxxhdpi/splash.png
index ff3733b21..be75147b0 100644
Binary files a/android/app/src/main/res/drawable-xxxhdpi/splash.png and b/android/app/src/main/res/drawable-xxxhdpi/splash.png differ
diff --git a/android/app/src/main/res/drawable/background.png b/android/app/src/main/res/drawable/background.png
index 3107d37fa..8e2140480 100644
Binary files a/android/app/src/main/res/drawable/background.png and b/android/app/src/main/res/drawable/background.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
index d52f4b941..5f088c9ac 100644
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
index fdf309d04..5b1d1a098 100644
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
index df559469c..e647e99da 100644
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
index 6d3b7c5c2..f08cdfc64 100644
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
index 65d9407ec..5fa061aea 100644
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
index 24fca47c1..822e084a5 100644
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
index 9ce7c13ed..92dc1c1ac 100644
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
index 39bdd583b..9ff188edf 100644
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index 6cd12cb80..3ab5167d2 100644
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
index 4c2390eb6..fb259206e 100644
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
index ed9826902..edeee826f 100644
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
index c86855a56..10c5bf6bc 100644
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 923892fdb..6aae00d23 100644
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
index b1f5ca5bd..fc408102f 100644
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png
index 552c4ffa2..343ec92c3 100644
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
index d36ac55a3..fe371a411 100644
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index b42e2a69a..5096454bc 100644
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
index 4b281bdd5..94ae429f9 100644
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
index fb7831194..3eab7c8b3 100644
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
index 51da4b236..4691a7a96 100644
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/assets/dynamic_config.json b/assets/dynamic_config.json
index ac45d5601..605b10587 100644
--- a/assets/dynamic_config.json
+++ b/assets/dynamic_config.json
@@ -1,5 +1,5 @@
{
- "sentryTraces": 0.1,
- "sentryProfiles": 0.01,
+ "sentryTraces": 0.5,
+ "sentryProfiles": 0.1,
"sentryReplay": 0
}
diff --git a/build.yaml b/build.yaml
new file mode 100644
index 000000000..7e57e2bf5
--- /dev/null
+++ b/build.yaml
@@ -0,0 +1,23 @@
+targets:
+ $default:
+ builders:
+ json_serializable:
+ options:
+ # Options configure how source code is generated for every
+ # `@JsonSerializable`-annotated class in the package.
+ #
+ # The default value for each is listed.
+ any_map: false
+ checked: false
+ constructor: ""
+ create_factory: true
+ create_field_map: false
+ create_json_keys: false
+ create_per_field_to_json: false
+ create_to_json: true
+ disallow_unrecognized_keys: false
+ explicit_to_json: true
+ field_rename: none
+ generic_argument_factories: false
+ ignore_unannotated: false
+ include_if_null: true
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
index 22ced1fee..a29778484 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index c86cf99d6..b448eb994 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index e687d2ab2..f7cb05420 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index 2c3a85c6b..362c3b031 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index 4502d305e..913ba0278 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png
index 60d182332..0bf61e57c 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png
index fde1c944e..94a354391 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-38x38@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index 328599ded..15979746b 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index ca518f051..bd946ac3a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index ca518f051..bd946ac3a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index ebc01b5ba..d971cc0fb 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png
index 582550ae1..1141c3978 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png
index b42e2a69a..5096454bc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-64x64@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png
index 41588c965..bdcff2b7a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index 04c7b62e6..7402043e2 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index dad8bdd34..8ccbfa06c 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png
index 0e277b3e0..702a56425 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png
index 69e5dcd2f..405e95c0e 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png
index 1b4510da5..1b972cc2a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png
index 6af7e9eff..495b71035 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png
index 5c1527162..a57473d1d 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png
index 5d2606b90..0d03a3329 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png
index 3b6bc2750..fbf911887 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-38x38@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png
index c6019cd30..dc0cac984 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png
index aebb7fb47..fe2edc5a4 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png
index aebb7fb47..fe2edc5a4 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png
index e2309f22e..9fe4a1f6b 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png
index 4bf368238..bd8a03cde 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png
index b91634521..515a797ea 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-64x64@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png
index 7d51431ef..575a46327 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png
index f7e405916..8062a9124 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png
index fe9e45d4d..3c454728f 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Dark-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png
index c0ef72f2d..2add639d1 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-1024x1024@1x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png
index a14ce2d57..f91c0eb83 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png
index 18a239a01..7afcaff07 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-20x20@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png
index b0c566e98..c3f8b5d61 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png
index ba531ce60..69f61cf83 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-29x29@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png
index aa4f5ab56..12e82bc0d 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png
index 972cd5de4..49fcee080 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-38x38@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png
index 021cbf66c..0e4c77ac5 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png
index d5c8c951e..d36c090ce 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-40x40@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png
index d5c8c951e..d36c090ce 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png
index 1e2748b01..b8f9d71c3 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-60x60@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png
index 98c81821b..fa8af245a 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png
index 4ea7d9fd3..454f56e68 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-64x64@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png
index 962306bbb..7f1f12a7d 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png
index 7bfb59cec..92bc204cc 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png
index 9ca0ac3d8..740e4f0cf 100644
Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-Tinted-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
index 3107d37fa..8e2140480 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png
index 4c1cab129..fbce56fe7 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png and b/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
index 1c950c1dc..063f84471 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
index 3e5026aa4..e048cf89a 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
index e6cf932b2..3a2767fb6 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png
index d8ad0684b..bc73c97b1 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png
index 524ef644b..63be6a344 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png
index baea7652e..a509cfcb4 100644
Binary files a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png and b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png differ
diff --git a/lib/Backend/Bluetooth/bluetooth_manager_plus.dart b/lib/Backend/Bluetooth/bluetooth_manager_plus.dart
index 05d6a7c8a..a269fdb1a 100644
--- a/lib/Backend/Bluetooth/bluetooth_manager_plus.dart
+++ b/lib/Backend/Bluetooth/bluetooth_manager_plus.dart
@@ -28,16 +28,6 @@ import 'bluetooth_utils.dart';
part 'bluetooth_manager_plus.g.dart';
-StreamSubscription? _onConnectionStateChangedStreamSubscription;
-StreamSubscription? _onReadRssiStreamSubscription;
-StreamSubscription? _onDiscoveredServicesStreamSubscription;
-StreamSubscription? _onCharacteristicReceivedStreamSubscription;
-StreamSubscription? _onServicesResetStreamSubscription;
-StreamSubscription? _adapterStateStreamSubscription;
-StreamSubscription>? _onScanResultsStreamSubscription;
-StreamSubscription? _onMtuChanged;
-StreamSubscription? _keepAliveStreamSubscription;
-
final _bluetoothPlusLogger = log.Logger('BluetoothPlus');
ValueNotifier isBluetoothEnabled = ValueNotifier(false);
@@ -46,39 +36,165 @@ bool _didInitFlutterBluePlus = false;
FlutterBluePlusMockable flutterBluePlus = FlutterBluePlusMockable();
@Riverpod(keepAlive: true)
-Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
- if (_didInitFlutterBluePlus) {
- return;
+class initFlutterBluePlus extends _$initFlutterBluePlus {
+ StreamSubscription? _onServicesResetStreamSubscription;
+ StreamSubscription? _adapterStateStreamSubscription;
+
+ @override
+ Future build() async {
+ if (!await getBluetoothPermission(bluetoothLog)) {
+ ref.invalidateSelf();
+ _bluetoothPlusLogger.info("Bluetooth permission not granted");
+ return;
+ }
+
+ _didInitFlutterBluePlus = true;
+
+ await flutterBluePlus.setLogLevel(LogLevel.warning, color: true);
+ // first, check if bluetooth is supported by your hardware
+ // Note: The platform is initialized on the first call to any FlutterBluePlus method.
+ if (await flutterBluePlus.isSupported == false) {
+ _bluetoothPlusLogger.info("Bluetooth not supported by this device");
+ return;
+ }
+
+ _onServicesResetStreamSubscription = flutterBluePlus.events.onServicesReset.listen((event) async {
+ _bluetoothPlusLogger.info("${event.device.advName} onServicesReset");
+ await event.device.discoverServices();
+ });
+ // handle bluetooth on & off
+ // note: for iOS the initial state is typically BluetoothAdapterState.unknown
+ // note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized
+ _adapterStateStreamSubscription = flutterBluePlus.adapterState.listen((BluetoothAdapterState state) {
+ _bluetoothPlusLogger.info(state);
+ isBluetoothEnabled.value = state == BluetoothAdapterState.on;
+ });
+ ref.watch(_keepGearAwakeProvider);
+ ref.watch(_mTUChangedProvider);
+ ref.watch(_onCharacteristicReceivedProvider);
+ ref.watch(_onConnectionStateChangedProvider);
+ ref.watch(_onDiscoveredServicesProvider);
+ ref.watch(_rSSIChangedProvider);
+ ref.watch(_onScanResultsProvider);
+ // Shut down bluetooth related things
+ ref.onDispose(() async {
+ stopScan();
+ //Disconnect any gear
+ for (var element in flutterBluePlus.connectedDevices) {
+ await disconnect(element.remoteId.str);
+ }
+ await _onServicesResetStreamSubscription?.cancel();
+ _onServicesResetStreamSubscription = null;
+ await _adapterStateStreamSubscription?.cancel();
+ _adapterStateStreamSubscription = null;
+ ref.invalidate(_keepGearAwakeProvider);
+ ref.invalidate(_mTUChangedProvider);
+ ref.invalidate(_onCharacteristicReceivedProvider);
+ ref.invalidate(_onConnectionStateChangedProvider);
+ ref.invalidate(_onDiscoveredServicesProvider);
+ ref.invalidate(_rSSIChangedProvider);
+ ref.invalidate(_onScanResultsProvider);
+ // Mark all gear disconnected;
+ ref.read(knownDevicesProvider).forEach(
+ (key, value) => value.deviceConnectionState.value = ConnectivityState.disconnected,
+ );
+ isBluetoothEnabled.value = false;
+ _didInitFlutterBluePlus = false; // Allow restarting ble stack
+ });
+ ref.read(scanMonitorProvider);
}
- if (!await getBluetoothPermission(bluetoothLog)) {
- ref.invalidateSelf();
- _bluetoothPlusLogger.info("Bluetooth permission not granted");
- return;
+}
+
+@Riverpod(keepAlive: true)
+class _MTUChanged extends _$MTUChanged {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ streamSubscription = flutterBluePlus.events.onMtuChanged.listen(listener);
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
}
- _didInitFlutterBluePlus = true;
- await flutterBluePlus.setLogLevel(LogLevel.warning, color: true);
- // first, check if bluetooth is supported by your hardware
- // Note: The platform is initialized on the first call to any FlutterBluePlus method.
- if (await flutterBluePlus.isSupported == false) {
- _bluetoothPlusLogger.info("Bluetooth not supported by this device");
- return;
+ void listener(OnMtuChangedEvent event) {
+ _bluetoothPlusLogger.info('${event.device.advName} MTU:${event.mtu}');
+ BaseStatefulDevice? statefulDevice = ref.read(knownDevicesProvider)[event.device.remoteId.str];
+ statefulDevice?.mtu.value = event.mtu;
+ }
+}
+
+@Riverpod(keepAlive: true)
+class _OnDiscoveredServices extends _$OnDiscoveredServices {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ streamSubscription = flutterBluePlus.events.onDiscoveredServices.listen(listener, onError: (e, s) => _bluetoothPlusLogger.warning("Unable to discover services: $e", e, s));
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
+ }
+
+ Future listener(OnDiscoveredServicesEvent event) async {
+ //_bluetoothPlusLogger.info('${event.device} ${event.services}');
+ //Subscribes to all characteristics
+ for (BluetoothService service in event.services) {
+ BluetoothUartService? bluetoothUartService = uartServices.firstWhereOrNull(
+ (element) => element.bleDeviceService == service.serviceUuid.str,
+ );
+ if (bluetoothUartService != null) {
+ BaseStatefulDevice? statefulDevice = ref.read(knownDevicesProvider)[event.device.remoteId.str];
+ statefulDevice?.bluetoothUartService.value = bluetoothUartService;
+ }
+ for (BluetoothCharacteristic characteristic in service.characteristics) {
+ await characteristic.setNotifyValue(true);
+ }
+ }
+ }
+}
+
+@Riverpod(keepAlive: true)
+class _RSSIChanged extends _$RSSIChanged {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ streamSubscription = flutterBluePlus.events.onReadRssi.listen(listener, onError: (e, s) => _bluetoothPlusLogger.warning("Unable to read rssi: $e", e, s));
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
+ }
+
+ void listener(OnReadRssiEvent event) {
+ _bluetoothPlusLogger.info('${event.device.advName} RSSI:${event.rssi}');
+ BaseStatefulDevice? statefulDevice = ref.read(knownDevicesProvider)[event.device.remoteId.str];
+ statefulDevice?.rssi.value = event.rssi;
+ }
+}
+
+@Riverpod(keepAlive: true)
+class _OnConnectionStateChanged extends _$OnConnectionStateChanged {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ streamSubscription = flutterBluePlus.events.onConnectionStateChanged.listen(listener);
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
}
- // listen to *any device* connection state changes
- _onConnectionStateChangedStreamSubscription = flutterBluePlus.events.onConnectionStateChanged.listen((event) async {
+ Future listener(OnConnectionStateChangedEvent event) async {
_bluetoothPlusLogger.info('${event.device.advName} ${event.connectionState}');
BuiltMap knownDevices = ref.read(knownDevicesProvider);
BluetoothDevice bluetoothDevice = event.device;
BluetoothConnectionState bluetoothConnectionState = event.connectionState;
String deviceID = bluetoothDevice.remoteId.str;
- //final ISentrySpan transaction = Sentry.startTransaction('connectToDevice()', 'task');
BaseDeviceDefinition? deviceDefinition = DeviceRegistry.getByName(bluetoothDevice.advName);
if (deviceDefinition == null) {
bluetoothLog.warning("Unknown device found: ${bluetoothDevice.advName}");
- //transaction.status = const SpanStatus.notFound();
- //transaction.finish();
return;
}
@@ -93,16 +209,13 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
baseStoredDevice.conModePin = code.toString();
Future(() => ref.read(knownDevicesProvider.notifier).add(statefulDevice));
}
- //transaction.setTag('Known Device', 'Yes');
} else {
baseStoredDevice = BaseStoredDevice(deviceDefinition.uuid, deviceID, deviceDefinition.deviceType.color(ref: ref).value)..name = getNameFromBTName(deviceDefinition.btName);
int code = Random().nextInt(899999) + 100000;
baseStoredDevice.conModePin = code.toString();
statefulDevice = BaseStatefulDevice(deviceDefinition, baseStoredDevice);
- //transaction.setTag('Known Device', 'No');
Future(() => ref.read(knownDevicesProvider.notifier).add(statefulDevice));
}
- //transaction.setTag('Device Name', device.name);
statefulDevice.deviceConnectionState.value = event.connectionState == BluetoothConnectionState.connected ? ConnectivityState.connected : ConnectivityState.disconnected;
if (bluetoothConnectionState == BluetoothConnectionState.connected) {
bluetoothDevice.readRssi().catchError((e) => -1).onError(
@@ -138,10 +251,8 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
FlutterForegroundTask.startService(
notificationTitle: "Gear Connected",
notificationText: "Gear is connected to The Tail Company app",
- notificationIcon: const NotificationIconData(
- resType: ResourceType.drawable,
- resPrefix: ResourcePrefix.img,
- name: 'tc_logo_transparent_notext_small',
+ notificationIcon: const NotificationIcon(
+ metaDataName: 'com.codel1417.tailApp.notificationIcon',
),
);
FlutterForegroundTask.setOnLockScreenVisibility(true);
@@ -159,7 +270,6 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
HiveProxy.put(settings, gearDisconnectCount, count);
_bluetoothPlusLogger.finer('Setting gearDisconnectCount to $count');
}
- //ref.read(snackbarStreamProvider.notifier).add(SnackBar(content: Text("Disconnected from ${baseStatefulDevice.baseStoredDevice.name}")));
// remove foreground service if no devices connected
int deviceCount = knownDevices.values.where((element) => element.deviceConnectionState.value == ConnectivityState.connected).length;
@@ -182,39 +292,27 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
}
}
// if the forget button was used, remove the device
- if (knownDevices[bluetoothDevice.remoteId.str]!.forgetOnDisconnect) {
+ if (knownDevices[bluetoothDevice.remoteId.str] != null && knownDevices[bluetoothDevice.remoteId.str]!.forgetOnDisconnect) {
_bluetoothPlusLogger.finer('forgetting about gear');
ref.read(knownDevicesProvider.notifier).remove(bluetoothDevice.remoteId.str);
}
}
- });
- _onReadRssiStreamSubscription = flutterBluePlus.events.onReadRssi.listen((event) {
- _bluetoothPlusLogger.info('${event.device.advName} RSSI:${event.rssi}');
- BaseStatefulDevice? statefulDevice = ref.read(knownDevicesProvider)[event.device.remoteId.str];
- statefulDevice?.rssi.value = event.rssi;
- }, onError: (e, s) => _bluetoothPlusLogger.warning("Unable to read rssi: $e", e, s));
- _onMtuChanged = flutterBluePlus.events.onMtuChanged.listen((event) {
- _bluetoothPlusLogger.info('${event.device.advName} MTU:${event.mtu}');
- BaseStatefulDevice? statefulDevice = ref.read(knownDevicesProvider)[event.device.remoteId.str];
- statefulDevice?.mtu.value = event.mtu;
- }, onError: (e, s) => _bluetoothPlusLogger.warning("Unable to read mtu: $e", e, s));
- _onDiscoveredServicesStreamSubscription = flutterBluePlus.events.onDiscoveredServices.listen((event) async {
- //_bluetoothPlusLogger.info('${event.device} ${event.services}');
- //Subscribes to all characteristics
- for (BluetoothService service in event.services) {
- BluetoothUartService? bluetoothUartService = uartServices.firstWhereOrNull(
- (element) => element.bleDeviceService == service.serviceUuid.str,
- );
- if (bluetoothUartService != null) {
- BaseStatefulDevice? statefulDevice = ref.read(knownDevicesProvider)[event.device.remoteId.str];
- statefulDevice?.bluetoothUartService.value = bluetoothUartService;
- }
- for (BluetoothCharacteristic characteristic in service.characteristics) {
- await characteristic.setNotifyValue(true);
- }
- }
- }, onError: (e, s) => _bluetoothPlusLogger.warning("Unable to discover services: $e", e, s));
- _onCharacteristicReceivedStreamSubscription = flutterBluePlus.events.onCharacteristicReceived.listen((event) async {
+ }
+}
+
+@Riverpod(keepAlive: true)
+class _OnCharacteristicReceived extends _$OnCharacteristicReceived {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ streamSubscription = flutterBluePlus.events.onCharacteristicReceived.listen(listener);
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
+ }
+
+ Future listener(OnCharacteristicReceivedEvent event) async {
_bluetoothPlusLogger.info('onCharacteristicReceived ${event.device.advName} ${event.characteristic.uuid.str} ${event.value}');
BluetoothDevice bluetoothDevice = event.device;
@@ -233,8 +331,8 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
String value = const Utf8Decoder().convert(values);
statefulDevice.messageHistory.add(MessageHistoryEntry(type: MessageHistoryType.receive, message: value));
statefulDevice.batteryCharging.value = value == "CHARGE ON";
- } catch (e, s) {
- _bluetoothPlusLogger.warning("Unable to read values: $values", e, s);
+ } catch (e) {
+ _bluetoothPlusLogger.warning("Unable to read values: $values", e);
statefulDevice.messageHistory.add(MessageHistoryEntry(type: MessageHistoryType.receive, message: "Unknown: ${values.toString()}"));
return;
}
@@ -242,8 +340,8 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
String value = "";
try {
value = const Utf8Decoder().convert(values);
- } catch (e, s) {
- _bluetoothPlusLogger.warning("Unable to read values: $values $e", e, s);
+ } catch (e) {
+ _bluetoothPlusLogger.warning("Unable to read values: $values $e");
statefulDevice.messageHistory.add(MessageHistoryEntry(type: MessageHistoryType.receive, message: "Unknown: ${values.toString()}"));
return;
}
@@ -287,88 +385,66 @@ Future initFlutterBluePlus(InitFlutterBluePlusRef ref) async {
statefulDevice.batteryLevel.value = int.parse(value).toDouble();
}
}
- });
- _onServicesResetStreamSubscription = flutterBluePlus.events.onServicesReset.listen((event) async {
- _bluetoothPlusLogger.info("${event.device.advName} onServicesReset");
- await event.device.discoverServices();
- });
- // handle bluetooth on & off
- // note: for iOS the initial state is typically BluetoothAdapterState.unknown
- // note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized
- _adapterStateStreamSubscription = flutterBluePlus.adapterState.listen((BluetoothAdapterState state) {
- _bluetoothPlusLogger.info(state);
- isBluetoothEnabled.value = state == BluetoothAdapterState.on;
- });
- _onScanResultsStreamSubscription = flutterBluePlus.onScanResults.listen(
- (results) async {
- if (results.isNotEmpty) {
- ScanResult r = results.last; // the most recently found device
- _bluetoothPlusLogger.info('${r.device.remoteId}: "${r.advertisementData.advName}" found!');
- BuiltMap knownDevices = ref.read(knownDevicesProvider);
- if (knownDevices.containsKey(r.device.remoteId.str) && knownDevices[r.device.remoteId.str]?.deviceConnectionState.value == ConnectivityState.disconnected && !knownDevices[r.device.remoteId.str]!.disableAutoConnect) {
- knownDevices[r.device.remoteId.str]?.deviceConnectionState.value = ConnectivityState.connecting;
- await connect(r.device.remoteId.str);
+ }
+}
+
+@Riverpod(keepAlive: true, dependencies: [initFlutterBluePlus])
+class _KeepGearAwake extends _$KeepGearAwake {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
+ streamSubscription = Stream.periodic(const Duration(seconds: 15)).listen(listener);
+ }
+
+ void listener(dynamic event) {
+ BuiltMap knownDevices = ref.read(knownDevicesProvider);
+ for (var element in flutterBluePlus.connectedDevices) {
+ BaseStatefulDevice? device = knownDevices[element.remoteId.str];
+ if (device != null) {
+ device.commandQueue.addCommand(BluetoothMessage(message: "PING", device: device, priority: Priority.low, type: CommandType.system, timestamp: DateTime.now()));
+ device.commandQueue.addCommand(BluetoothMessage(message: "BATT", device: device, priority: Priority.low, type: CommandType.system, timestamp: DateTime.now()));
+ element.readRssi().catchError((e) => -1).onError(
+ (error, stackTrace) => -1,
+ );
+
+ if (device.baseDeviceDefinition.deviceType != DeviceType.ears && device.hasGlowtip.value == GlowtipStatus.unknown) {
+ device.commandQueue.addCommand(BluetoothMessage(message: "VER", device: device, priority: Priority.low, type: CommandType.system, timestamp: DateTime.now()));
}
}
- },
- onError: (e, s) => _bluetoothPlusLogger.severe("", e, s),
- );
+ }
+ }
+}
- _keepAliveStreamSubscription = Stream.periodic(const Duration(seconds: 15)).listen(
- (event) async {
+@Riverpod(keepAlive: true)
+class _OnScanResults extends _$OnScanResults {
+ StreamSubscription? streamSubscription;
+
+ @override
+ void build() {
+ ref.onDispose(
+ () => streamSubscription?.cancel(),
+ );
+ streamSubscription = flutterBluePlus.onScanResults.listen(
+ listener,
+ onError: (e, s) => _bluetoothPlusLogger.severe("", e, s),
+ );
+ }
+
+ Future listener(List results) async {
+ if (results.isNotEmpty) {
+ ScanResult r = results.last; // the most recently found device
+ _bluetoothPlusLogger.info('${r.device.remoteId}: "${r.advertisementData.advName}" found!');
BuiltMap knownDevices = ref.read(knownDevicesProvider);
- for (var element in flutterBluePlus.connectedDevices) {
- BaseStatefulDevice? device = knownDevices[element.remoteId.str];
- if (device != null) {
- device.commandQueue.addCommand(BluetoothMessage(message: "PING", device: device, priority: Priority.low, type: CommandType.system, timestamp: DateTime.now()));
- device.commandQueue.addCommand(BluetoothMessage(message: "BATT", device: device, priority: Priority.low, type: CommandType.system, timestamp: DateTime.now()));
- element.readRssi().catchError((e) => -1).onError(
- (error, stackTrace) => -1,
- );
-
- if (device.baseDeviceDefinition.deviceType != DeviceType.ears && device.hasGlowtip.value == GlowtipStatus.unknown) {
- device.commandQueue.addCommand(BluetoothMessage(message: "VER", device: device, priority: Priority.low, type: CommandType.system, timestamp: DateTime.now()));
- }
- }
+ if (knownDevices.containsKey(r.device.remoteId.str) && knownDevices[r.device.remoteId.str]?.deviceConnectionState.value == ConnectivityState.disconnected && !knownDevices[r.device.remoteId.str]!.disableAutoConnect) {
+ knownDevices[r.device.remoteId.str]?.deviceConnectionState.value = ConnectivityState.connecting;
+ await connect(r.device.remoteId.str);
}
- },
- cancelOnError: true,
- );
-
- // Shut down bluetooth related things
- ref.onDispose(() async {
- stopScan();
- //Disconnect any gear
- for (var element in flutterBluePlus.connectedDevices) {
- await disconnect(element.remoteId.str);
}
- //cancel streams
- await _keepAliveStreamSubscription?.cancel();
- _keepAliveStreamSubscription = null;
- await _onCharacteristicReceivedStreamSubscription?.cancel();
- _onCharacteristicReceivedStreamSubscription = null;
- await _onConnectionStateChangedStreamSubscription?.cancel();
- _onConnectionStateChangedStreamSubscription = null;
- await _onDiscoveredServicesStreamSubscription?.cancel();
- _onDiscoveredServicesStreamSubscription = null;
- await _onMtuChanged?.cancel();
- _onMtuChanged = null;
- await _adapterStateStreamSubscription?.cancel();
- _adapterStateStreamSubscription = null;
- await _onScanResultsStreamSubscription?.cancel();
- _onScanResultsStreamSubscription = null;
- await _onServicesResetStreamSubscription?.cancel();
- _onServicesResetStreamSubscription = null;
- await _onReadRssiStreamSubscription?.cancel();
- _onReadRssiStreamSubscription = null;
- // Mark all gear disconnected;
- ref.read(knownDevicesProvider).forEach(
- (key, value) => value.deviceConnectionState.value = ConnectivityState.disconnected,
- );
- isBluetoothEnabled.value = false;
- _didInitFlutterBluePlus = false; // Allow restarting ble stack
- });
- ref.read(scanMonitorProvider);
+ }
}
Future disconnect(String id) async {
@@ -423,7 +499,7 @@ enum ScanReason { background, addGear, manual, notScanning }
ScanReason _scanReason = ScanReason.notScanning;
Future beginScan({required ScanReason scanReason, Duration? timeout}) async {
- if (_didInitFlutterBluePlus && !flutterBluePlus.isScanningNow) {
+ if (_didInitFlutterBluePlus && !flutterBluePlus.isScanningNow && isBluetoothEnabled.value) {
_bluetoothPlusLogger.info("Starting scan");
_scanReason = scanReason;
await flutterBluePlus.startScan(withServices: DeviceRegistry.getAllIds().map(Guid.new).toList(), continuousUpdates: timeout == null, androidScanMode: AndroidScanMode.lowPower, timeout: timeout);
diff --git a/lib/Backend/Definitions/Device/device_definition.dart b/lib/Backend/Definitions/Device/device_definition.dart
index 57c086313..319b90cf0 100644
--- a/lib/Backend/Definitions/Device/device_definition.dart
+++ b/lib/Backend/Definitions/Device/device_definition.dart
@@ -123,12 +123,15 @@ enum DeviceState { standby, runAction, busy }
enum GlowtipStatus { glowtip, noGlowtip, unknown }
+enum TailControlStatus { tailControl, legacy, unknown }
+
@freezed
class BluetoothUartService with _$BluetoothUartService {
const factory BluetoothUartService({
required String bleDeviceService,
required String bleRxCharacteristic,
required String bleTxCharacteristic,
+ required String label,
}) = _BluetoothUartService;
}
@@ -137,16 +140,20 @@ final List uartServices = const [
bleDeviceService: "3af2108b-d066-42da-a7d4-55648fa0a9b6",
bleRxCharacteristic: "c6612b64-0087-4974-939e-68968ef294b0",
bleTxCharacteristic: "5bfd6484-ddee-4723-bfe6-b653372bbfd6",
+ label: "Legacy Gear",
),
BluetoothUartService(
bleDeviceService: "927dee04-ddd4-4582-8e42-69dc9fbfae66",
bleRxCharacteristic: "0b646a19-371e-4327-b169-9632d56c0e84",
bleTxCharacteristic: "05e026d8-b395-4416-9f8a-c00d6c3781b9",
+ label: "Legacy Ears",
),
+ // TailCoNTROL uuids
BluetoothUartService(
- bleDeviceService: "19F8ADE2-D0C6-4C0A-912A-30601D9B3060",
- bleRxCharacteristic: "5E4D86AC-EF2F-466F-A857-8776D45FFBC2",
- bleTxCharacteristic: "567A99D6-A442-4AC0-B676-4993BF95F805",
+ bleDeviceService: "19f8ade2-d0c6-4c0a-912a-30601d9b3060",
+ bleRxCharacteristic: "567a99d6-a442-4ac0-b676-4993bf95f805",
+ bleTxCharacteristic: "5e4d86ac-ef2f-466f-a857-8776d45ffbc2",
+ label: "TailCoNTROL",
),
];
@@ -181,6 +188,8 @@ class BaseStatefulDevice {
final ValueNotifier gearConfigInfo = ValueNotifier(GearConfigInfo());
final ValueNotifier fwInfo = ValueNotifier(null);
final ValueNotifier hasUpdate = ValueNotifier(false);
+ final ValueNotifier isTailCoNTROL = ValueNotifier(TailControlStatus.unknown);
+
late final Stream rxCharacteristicStream;
late final CommandQueue commandQueue;
List batlevels = [];
@@ -219,6 +228,22 @@ class BaseStatefulDevice {
batlevels.add(FlSpot(stopWatch.elapsed.inSeconds.toDouble(), batteryLevel.value));
batteryLow.value = batteryLevel.value < 20;
});
+
+ bluetoothUartService.addListener(
+ () {
+ if (bluetoothUartService.value == null) {
+ isTailCoNTROL.value = TailControlStatus.unknown;
+ return;
+ }
+
+ isTailCoNTROL.value = bluetoothUartService.value ==
+ uartServices.firstWhere(
+ (element) => element.bleDeviceService.toLowerCase() == "19f8ade2-d0c6-4c0a-912a-30601d9b3060",
+ )
+ ? TailControlStatus.tailControl
+ : TailControlStatus.legacy;
+ },
+ );
}
@override
@@ -239,6 +264,8 @@ class BaseStatefulDevice {
stopWatch.reset();
mtu.value = -1;
mandatoryOtaRequired.value = false;
+ isTailCoNTROL.value = TailControlStatus.unknown;
+ bluetoothUartService.value = null;
}
}
diff --git a/lib/Backend/action_registry.dart b/lib/Backend/action_registry.dart
index c2b8d5a72..f1bf5afc5 100644
--- a/lib/Backend/action_registry.dart
+++ b/lib/Backend/action_registry.dart
@@ -1,6 +1,7 @@
import 'package:built_collection/built_collection.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'Bluetooth/bluetooth_manager.dart';
@@ -22,7 +23,7 @@ class ActionRegistry {
CommandAction(
name: "Slow 1",
command: "TAILS1",
- deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.calm,
response: "TAILS1 END",
uuid: "c53e980e-899e-4148-a13e-f57a8f9707f4",
@@ -33,7 +34,7 @@ class ActionRegistry {
CommandAction(
name: "Slow 2",
command: "TAILS2",
- deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.calm,
response: "TAILS2 END",
uuid: "eb1bdfe7-d374-4e97-943a-13e89f27ddcd",
@@ -44,7 +45,7 @@ class ActionRegistry {
CommandAction(
name: "Slow 3",
command: "TAILS3",
- deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.calm,
response: "TAILS3 END",
uuid: "6937b9af-3ff7-43fb-ae62-a403e5dfaf95",
@@ -55,7 +56,7 @@ class ActionRegistry {
CommandAction(
name: "Fast",
command: "TAILFA",
- deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.fast,
response: "TAILFA END",
uuid: "a04b558f-0ad5-410f-8e39-8f5c594791d2",
@@ -66,7 +67,7 @@ class ActionRegistry {
CommandAction(
name: "Short",
command: "TAILSH",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.fast,
response: "TAILSH END",
uuid: "05a4c47b-45ee-4da8-bec2-4a46f4e04a7f",
@@ -77,7 +78,7 @@ class ActionRegistry {
CommandAction(
name: "Happy",
command: "TAILHA",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.fast,
response: "TAILHA END",
uuid: "86b13d13-b09c-46ba-a887-b40d8118b00a",
@@ -88,7 +89,7 @@ class ActionRegistry {
CommandAction(
name: "Stand Up",
command: "TAILER",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.fast,
response: "TAILER END",
uuid: "5b04ca3d-a22a-4aff-8f40-99363248fcaa",
@@ -99,7 +100,7 @@ class ActionRegistry {
CommandAction(
name: "Pulse",
command: "TAILEP",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.tense,
response: "TAILEP END",
uuid: "39bbe39d-aa92-4189-ac90-4bb821a59f5e",
@@ -110,7 +111,7 @@ class ActionRegistry {
CommandAction(
name: "Tremble 1",
command: "TAILT1",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.tense,
response: "TAILT1 END",
uuid: "8cc3fc60-b8d2-4f22-810a-1e042d3984f7",
@@ -121,7 +122,7 @@ class ActionRegistry {
CommandAction(
name: "Tremble 2",
command: "TAILT2",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.tense,
response: "TAILT2 END",
uuid: "123557a2-5489-43da-99e2-da37a36f055a",
@@ -132,7 +133,7 @@ class ActionRegistry {
CommandAction(
name: "Tremble 3",
command: "TAILET",
- deviceCategory: [DeviceType.tail, DeviceType.wings],
+ deviceCategory: [DeviceType.tail, DeviceType.wings, DeviceType.ears],
actionCategory: ActionCategory.tense,
response: "TAILET END",
uuid: "4909d4c2-0054-4f16-9589-6273ef6bf6c9",
@@ -143,49 +144,49 @@ class ActionRegistry {
CommandAction(
name: "LEDs off",
command: "LEDOFF",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "6b2a7fae-b58c-43f3-81bf-070aa21c2242",
),
CommandAction(
name: "Rectangle wave",
command: "LEDREC",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "34269c91-90bd-4a34-851d-d49daa6ac863",
),
CommandAction(
name: "Triangle wave",
command: "LEDTRI",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "64142e0b-4cc0-4b1e-845f-9c560875f993",
),
CommandAction(
name: "Sawtooth wave",
command: "LEDSAW",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "047b84ad-3eb8-4d9c-b59b-13186cf965ca",
),
CommandAction(
name: "SOS",
command: "LEDSOS",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "66164945-840f-4302-b27c-e7a7623bf475",
),
CommandAction(
name: "Beacon",
command: "LEDBEA",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "4955a936-7703-4ce6-8d4a-b18857c0ea0a",
),
CommandAction(
name: "Flame",
command: "LEDFLA",
- deviceCategory: [DeviceType.tail, DeviceType.miniTail],
+ deviceCategory: [DeviceType.tail, DeviceType.miniTail, DeviceType.ears],
actionCategory: ActionCategory.glowtip,
uuid: "e46566b4-1071-4866-815b-1aefbf06b573",
),
@@ -282,6 +283,9 @@ class GetAvailableActions extends _$GetAvailableActions {
baseStatefulDevice.hasGlowtip
..removeListener(_listener)
..addListener(_listener);
+ baseStatefulDevice.isTailCoNTROL
+ ..removeListener(_listener)
+ ..addListener(_listener);
}
return getState();
}
@@ -295,6 +299,21 @@ class GetAvailableActions extends _$GetAvailableActions {
for (BaseStatefulDevice baseStatefulDevice in availableGear) {
// check if command matches device type
if (baseAction.deviceCategory.contains(baseStatefulDevice.baseDeviceDefinition.deviceType) && ((baseAction.actionCategory == ActionCategory.glowtip && baseStatefulDevice.hasGlowtip.value == GlowtipStatus.glowtip) || baseAction.actionCategory != ActionCategory.glowtip)) {
+ // Handle migrating ears to unified firmware
+ if (baseAction.deviceCategory.contains(DeviceType.ears) && baseStatefulDevice.baseDeviceDefinition.deviceType == DeviceType.ears) {
+ if (baseStatefulDevice.isTailCoNTROL.value == TailControlStatus.tailControl) {
+ // skip legacy moves
+ if (baseAction is EarsMoveList) {
+ continue;
+ }
+ // skip unified moves for legacy firmware ears
+ } else if (baseStatefulDevice.isTailCoNTROL.value == TailControlStatus.legacy) {
+ if (baseAction is CommandAction) {
+ continue;
+ }
+ }
+ }
+
// get category if it exists
if (sortedActions.containsKey(baseAction.actionCategory)) {
baseActions = sortedActions[baseAction.actionCategory];
@@ -321,7 +340,7 @@ class GetAvailableActions extends _$GetAvailableActions {
}
@Riverpod(keepAlive: true)
-BuiltMap> getAllActions(GetAllActionsRef ref) {
+BuiltMap> getAllActions(Ref ref) {
Map> sortedActions = {};
final BuiltList moveLists = ref.watch(moveListsProvider);
final BuiltList audioActions = ref.watch(userAudioActionsProvider);
@@ -348,50 +367,7 @@ BuiltMap> getAllActions(GetAllActionsRef re
}
@Riverpod(keepAlive: true)
-BuiltMap> getAllActionsFiltered(GetAllActionsFilteredRef ref, BuiltSet deviceType) {
- Map> sortedActions = {};
- final BuiltMap> read = ref.watch(getAllActionsProvider);
- for (BaseAction baseAction in read.values.flattened) {
- Set? baseActions = {};
- // check if command matches device type
- if (baseAction.deviceCategory.toBuiltSet().intersection(deviceType).isNotEmpty) {
- // get category if it exists
- if (sortedActions.containsKey(baseAction.actionCategory)) {
- baseActions = sortedActions[baseAction.actionCategory];
- }
- // add action to category
- baseActions?.add(baseAction);
- }
- // store result
- if (baseActions != null && baseActions.isNotEmpty) {
- sortedActions[baseAction.actionCategory] = baseActions;
- }
- }
- return BuiltMap(
- sortedActions.map(
- (key, value) => MapEntry(key, value.build()),
- ),
- );
-}
-
-@Riverpod(keepAlive: true)
-BuiltList getAllActionsForCategory(GetAllActionsForCategoryRef ref, ActionCategory actionCategory) {
- final BuiltMap> allActions = ref.watch(getAllActionsProvider);
- if (allActions.containsKey(actionCategory)) {
- return allActions[actionCategory]!.toBuiltList();
- }
- return BuiltList();
-}
-
-BuiltList splitBaseAction(BaseAction baseAction) {
- if (baseAction.deviceCategory.length <= 1) {
- return [baseAction].build();
- }
- return BuiltList();
-}
-
-@Riverpod(keepAlive: true)
-BaseAction? getActionFromUUID(GetActionFromUUIDRef ref, String? uuid) {
+BaseAction? getActionFromUUID(Ref ref, String? uuid) {
if (uuid == null) {
return null;
}
diff --git a/lib/Backend/app_shortcuts.dart b/lib/Backend/app_shortcuts.dart
index 0cba00e91..53a28ad4e 100644
--- a/lib/Backend/app_shortcuts.dart
+++ b/lib/Backend/app_shortcuts.dart
@@ -1,5 +1,4 @@
import 'package:built_collection/built_collection.dart';
-import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:quick_actions/quick_actions.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -16,7 +15,7 @@ part 'app_shortcuts.g.dart';
const QuickActions quickActions = QuickActions();
@Riverpod(keepAlive: true)
-Future appShortcuts(AppShortcutsRef ref) async {
+Future appShortcuts(Ref ref) async {
await Future.delayed(const Duration(seconds: 5));
quickActions.initialize((shortcutType) {
BaseAction? action = ref.read(getActionFromUUIDProvider(shortcutType));
@@ -35,7 +34,7 @@ Future updateShortcuts(BuiltList favoriteActions, Ref ref)
.map(
(e) => ref.read(getActionFromUUIDProvider(e.actionUUID)),
)
- .whereNotNull();
+ .nonNulls;
quickActions.setShortcutItems(
allActions
diff --git a/lib/Backend/device_registry.dart b/lib/Backend/device_registry.dart
index 8d909f810..abe269c41 100644
--- a/lib/Backend/device_registry.dart
+++ b/lib/Backend/device_registry.dart
@@ -1,5 +1,6 @@
import 'package:built_collection/built_collection.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart' as log;
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -32,7 +33,7 @@ class DeviceRegistry {
uuid: "5fb21175-fef4-448a-a38b-c472d935abab",
btName: "minitail",
deviceType: DeviceType.miniTail,
- fwURL: "https://thetailcompany.com/fw/mini",
+ fwURL: "https://thetailcompany.com/fw/mini.json",
minVersion: Version(major: 5, minor: 0, patch: 0),
),
BaseDeviceDefinition(
@@ -74,7 +75,7 @@ class DeviceRegistry {
}
@Riverpod(keepAlive: true)
-BuiltSet getByAction(GetByActionRef ref, BaseAction baseAction) {
+BuiltSet getByAction(Ref ref, BaseAction baseAction) {
deviceRegistryLogger.info("Getting devices for action::$baseAction");
Set foundDevices = {};
final BuiltList watch = ref.watch(getAvailableIdleGearProvider);
@@ -109,7 +110,7 @@ class GetAvailableIdleGear extends _$GetAvailableIdleGear {
}
@Riverpod(keepAlive: true)
-BuiltSet getAvailableGearTypes(GetAvailableGearTypesRef ref) {
+BuiltSet getAvailableGearTypes(Ref ref) {
final BuiltList watch = ref.watch(getAvailableGearProvider);
return watch
.map(
@@ -119,13 +120,13 @@ BuiltSet getAvailableGearTypes(GetAvailableGearTypesRef ref) {
}
@Riverpod(keepAlive: true)
-BuiltList getAvailableIdleGearForAction(GetAvailableIdleGearForActionRef ref, BaseAction baseAction) {
+BuiltList getAvailableIdleGearForAction(Ref ref, BaseAction baseAction) {
final BuiltList watch = ref.watch(getAvailableIdleGearProvider);
return watch.where((element) => baseAction.deviceCategory.contains(element.baseDeviceDefinition.deviceType)).toBuiltList();
}
@Riverpod(keepAlive: true)
-BuiltList getAvailableIdleGearForType(GetAvailableIdleGearForTypeRef ref, BuiltSet deviceTypes) {
+BuiltList getAvailableIdleGearForType(Ref ref, BuiltSet deviceTypes) {
final Iterable watch = ref.watch(getAvailableIdleGearProvider);
return watch
.where(
@@ -135,7 +136,7 @@ BuiltList getAvailableIdleGearForType(GetAvailableIdleGearFo
}
@Riverpod(keepAlive: true)
-BuiltList getAvailableGearForType(GetAvailableGearForTypeRef ref, BuiltSet deviceTypes) {
+BuiltList getAvailableGearForType(Ref ref, BuiltSet deviceTypes) {
final BuiltList watch = ref.watch(getAvailableGearProvider);
return watch
.where(
@@ -145,7 +146,7 @@ BuiltList getAvailableGearForType(GetAvailableGearForTypeRef
}
@Riverpod(keepAlive: true)
-BuiltList getKnownGearForType(GetKnownGearForTypeRef ref, BuiltSet deviceTypes) {
+BuiltList getKnownGearForType(Ref ref, BuiltSet deviceTypes) {
final BuiltMap watch = ref.watch(knownDevicesProvider);
return watch.values
.where(
@@ -197,7 +198,7 @@ class GetAvailableGear extends _$GetAvailableGear {
}
@Riverpod(keepAlive: true)
-bool isAllKnownGearConnected(IsAllKnownGearConnectedRef ref) {
+bool isAllKnownGearConnected(Ref ref) {
var knownGear = ref.watch(knownDevicesProvider);
BuiltList connectedGear = ref.watch(getAvailableGearProvider);
return knownGear.length == connectedGear.length;
diff --git a/lib/Backend/firmware_update.dart b/lib/Backend/firmware_update.dart
index cd975bf48..b14fa03e7 100644
--- a/lib/Backend/firmware_update.dart
+++ b/lib/Backend/firmware_update.dart
@@ -1,15 +1,15 @@
import 'dart:async';
import 'dart:convert';
-import 'dart:isolate';
import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
-import 'package:flutter_blue_plus/flutter_blue_plus.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:logging/logging.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:tail_app/Backend/plausible_dio.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
@@ -39,7 +39,7 @@ class FWInfo with _$FWInfo {
}
@Riverpod(keepAlive: true)
-Future?> getBaseFirmwareInfo(GetBaseFirmwareInfoRef ref, String url) async {
+Future?> getBaseFirmwareInfo(Ref ref, String url) async {
Dio dio = await initDio();
Future> valueFuture = dio.get(url, options: Options(responseType: ResponseType.json))
..onError((error, stackTrace) {
@@ -58,7 +58,7 @@ Future?> getBaseFirmwareInfo(GetBaseFirmwareInfoRef ref, String url
}
@Riverpod()
-Future getFirmwareInfo(GetFirmwareInfoRef ref, String url, String hwVer) async {
+Future getFirmwareInfo(Ref ref, String url, String hwVer) async {
if (url.isEmpty || hwVer.isEmpty) {
return null;
}
@@ -88,7 +88,7 @@ Future getFirmwareInfo(GetFirmwareInfoRef ref, String url, String hwVer
}
@Riverpod()
-Future checkForFWUpdate(CheckForFWUpdateRef ref, BaseStatefulDevice baseStatefulDevice) async {
+Future checkForFWUpdate(Ref ref, BaseStatefulDevice baseStatefulDevice) async {
if (baseStatefulDevice.fwInfo.value != null) {
return baseStatefulDevice.fwInfo.value;
}
@@ -107,7 +107,7 @@ Future checkForFWUpdate(CheckForFWUpdateRef ref, BaseStatefulDevice bas
}
@Riverpod()
-Future hasOtaUpdate(HasOtaUpdateRef ref, BaseStatefulDevice baseStatefulDevice) async {
+Future hasOtaUpdate(Ref ref, BaseStatefulDevice baseStatefulDevice) async {
FWInfo? fwInfo = await ref.read(checkForFWUpdateProvider(baseStatefulDevice).future);
Version fwVersion = baseStatefulDevice.fwVersion.value;
@@ -190,6 +190,7 @@ class OtaUpdater {
Timer? _reconnectTimer;
Timer? _finalTimer;
final Logger _otaLogger = Logger('otaLogger');
+ ISentrySpan? transaction;
void setManualOtaFile(List? bytes) {
if (bytes == null) {
@@ -202,8 +203,9 @@ class OtaUpdater {
downloadProgress = 1;
}
- void _onError(OtaError error) {
+ void _onError(OtaError error, ISentrySpan? span) {
otaState = OtaState.error;
+ span?.status = SpanStatus.fromString(error.name);
if (onError != null) {
onError!(error);
}
@@ -231,8 +233,16 @@ class OtaUpdater {
}
Future beginUpdate() async {
+ transaction = Sentry.startTransaction('beginUpdate()', 'task');
+ transaction?.setData("Gear Model", baseStatefulDevice.baseDeviceDefinition.btName);
+ transaction?.setData("Current FW Version", baseStatefulDevice.fwVersion.value.toString());
+ transaction?.setData("Hardware Version", baseStatefulDevice.hwVersion.value);
+ transaction?.setData("Target Firmware Version", baseStatefulDevice.fwInfo.value?.version);
+
if (baseStatefulDevice.batteryLevel.value < 50) {
otaState = OtaState.lowBattery;
+ transaction?.status = SpanStatus.fromString("lowBattery");
+ transaction?.finish();
return;
}
WakelockPlus.enable();
@@ -248,6 +258,7 @@ class OtaUpdater {
if (firmwareInfo == null) {
return;
}
+ final ISentrySpan? downloadSpan = transaction?.startChild('downloadFirmware()', description: 'operation');
otaState = OtaState.download;
downloadProgress = 0;
_updateProgress();
@@ -267,12 +278,14 @@ class OtaUpdater {
if (digest.toString() == firmwareInfo!.md5sum) {
firmwareFile = rs.data;
} else {
- _onError(OtaError.md5Mismatch);
+ _onError(OtaError.md5Mismatch, downloadSpan);
}
}
} catch (e) {
- _onError(OtaError.downloadFailed);
+ downloadSpan?.throwable = e;
+ _onError(OtaError.downloadFailed, downloadSpan);
}
+ downloadSpan?.finish();
}
Future _verListener() async {
@@ -281,10 +294,13 @@ class OtaUpdater {
if (fwInfo != null && version.compareTo(const Version()) > 0 && otaState == OtaState.rebooting) {
bool updated = version.compareTo(getVersionSemVer(fwInfo.version)) >= 0;
if (!updated) {
- _onError(OtaError.gearVersionMismatch);
+ _onError(OtaError.gearVersionMismatch, transaction);
} else {
otaState = OtaState.completed;
}
+ if (transaction != null && !transaction!.finished) {
+ transaction?.finish();
+ }
}
}
@@ -301,7 +317,8 @@ class OtaUpdater {
const Duration(seconds: 30),
() {
_otaLogger.warning("Gear did not reconnect");
- _onError(OtaError.gearReconnectTimeout);
+ _onError(OtaError.gearReconnectTimeout, transaction);
+ transaction?.finish();
},
);
} else if (connectivityState == ConnectivityState.connected) {
@@ -311,6 +328,7 @@ class OtaUpdater {
}
Future _uploadFirmware() async {
+ final ISentrySpan? uploadSpan = transaction?.startChild('uploadFirmware()', description: 'operation');
otaState = OtaState.upload;
uploadProgress = 0;
if (firmwareFile != null) {
@@ -322,11 +340,11 @@ class OtaUpdater {
_otaLogger.info("Send OTA begin message");
List beginOTA = List.from(const Utf8Encoder().convert("OTA ${firmwareFile!.length} $downloadedMD5"));
await sendMessage(baseStatefulDevice, beginOTA);
-
+ uploadSpan?.setData("Gear MTU", mtu);
while (uploadProgress < 1 && otaState != OtaState.error) {
baseStatefulDevice.deviceState.value = DeviceState.busy; // hold the command queue
if (baseStatefulDevice.gearReturnedError.value) {
- _onError(OtaError.gearReturnedError);
+ _onError(OtaError.gearReturnedError, uploadSpan);
break;
}
@@ -337,8 +355,9 @@ class OtaUpdater {
} catch (e, s) {
_otaLogger.severe("Exception during ota upload:$e", e, s);
if ((currentFirmwareUploadPosition + chunk.length) / firmwareFile!.length < 0.99) {
- _onError(OtaError.uploadFailed);
-
+ _onError(OtaError.uploadFailed, uploadSpan);
+ uploadSpan?.throwable = e;
+ uploadSpan?.finish();
return;
}
}
@@ -353,7 +372,6 @@ class OtaUpdater {
if (uploadProgress == 1) {
_otaLogger.info("File Uploaded");
otaState = OtaState.rebooting;
- //TODO: check if gear disconnects
beginScan(
scanReason: ScanReason.manual,
timeout: const Duration(seconds: 60),
@@ -363,7 +381,8 @@ class OtaUpdater {
const Duration(seconds: 30),
() {
_otaLogger.warning("Gear did not disconnect");
- _onError(OtaError.gearDisconnectTimeout);
+ _onError(OtaError.gearDisconnectTimeout, transaction);
+ transaction?.finish();
},
);
// start scanning for the gear to reconnect
@@ -372,7 +391,8 @@ class OtaUpdater {
() {
if (otaState != OtaState.completed) {
_otaLogger.warning("Gear did not return correct version after reboot");
- _onError(OtaError.gearOtaFinalTimeout);
+ _onError(OtaError.gearOtaFinalTimeout, transaction);
+ transaction?.finish();
}
},
);
@@ -380,6 +400,7 @@ class OtaUpdater {
}
baseStatefulDevice.deviceState.value = DeviceState.standby; // release the command queue
}
+ uploadSpan?.finish();
}
OtaUpdater({this.onProgress, this.onStateChanged, required this.baseStatefulDevice, this.onError}) {
@@ -398,6 +419,9 @@ class OtaUpdater {
if (!HiveProxy.getOrDefault(settings, alwaysScanning, defaultValue: alwaysScanningDefault)) {
unawaited(stopScan());
}
+ if (transaction != null && !transaction!.finished) {
+ transaction?.finish(status: SpanStatus.aborted());
+ }
}
void _cancelTimers() {
@@ -406,36 +430,3 @@ class OtaUpdater {
_finalTimer?.cancel();
}
}
-
-@pragma('vm:entry-point')
-Future updaterIsolate({required String macAddress, required String service, required String txCharacteristic, required List firmwareFile, required String md5Hash, required SendPort port}) async {
- BluetoothDevice? bluetoothDevice = FlutterBluePlus.connectedDevices.where((element) => element.remoteId.str == macAddress).firstOrNull;
- if (bluetoothDevice == null) {
- return;
- }
- BluetoothCharacteristic? bluetoothCharacteristic = bluetoothDevice.servicesList.firstWhereOrNull((element) => element.uuid == Guid(service))?.characteristics.firstWhereOrNull((element) => element.characteristicUuid == Guid(txCharacteristic));
- if (bluetoothCharacteristic == null) {
- return;
- }
- List beginOTA = List.from(const Utf8Encoder().convert("OTA ${firmwareFile.length} $md5Hash"));
- await bluetoothCharacteristic.write(beginOTA);
- int current = 0;
- double uploadProgress = 0;
-
- List chunk = firmwareFile.skip(current).take(bluetoothDevice.mtuNow).toList();
- if (chunk.isNotEmpty) {
- try {
- await bluetoothCharacteristic.write(chunk);
- } catch (e, s) {
- if ((current + chunk.length) / firmwareFile.length < 0.99) {
- port.send("Error");
- return;
- }
- }
- current = current + chunk.length;
- } else {
- current = firmwareFile.length;
- }
- uploadProgress = current / firmwareFile.length;
- port.send(uploadProgress);
-}
diff --git a/lib/Backend/move_lists.dart b/lib/Backend/move_lists.dart
index 5a8f5ee4c..2e2768393 100644
--- a/lib/Backend/move_lists.dart
+++ b/lib/Backend/move_lists.dart
@@ -182,6 +182,7 @@ class MoveLists extends _$MoveLists {
}
}
+//TODO: move the core running moves OUT of move_lists
Future runAction(BaseAction action, BaseStatefulDevice device) async {
//cursed handling of ears specifically
//TODO: Remove with TAILCoNTROL update
@@ -213,7 +214,7 @@ Future runAction(BaseAction action, BaseStatefulDevice device) async {
} else if (action is MoveList) {
sequencesLogger.info("Starting MoveList ${action.name}.");
//plausible.event(name: "Run Sequence", props: {"Sequence Repeat": action.repeat.toInt().toString(), "Sequence Device Type": device.baseDeviceDefinition.deviceType.name, "Sequence Moves": action.moves.length.toString()});
- if (action.moves.isNotEmpty && action.moves.length <= 5 && device.baseDeviceDefinition.deviceType != DeviceType.ears) {
+ if (action.moves.isNotEmpty && action.moves.length <= 5 && (device.baseDeviceDefinition.deviceType != DeviceType.ears || device.isTailCoNTROL.value == TailControlStatus.tailControl)) {
int preset = 1; //TODO: store
String cmd = "USERMOVE U${preset}P${action.moves.length}N${action.repeat.toInt()}";
String a = ''; // servo 1 position
@@ -282,14 +283,14 @@ List generateMoveCommand(Move move, BaseStatefulDevice device,
List commands = [];
if (move.moveType == MoveType.home) {
//TODO: Remove for TAILCoNTROL update
- if (device.baseDeviceDefinition.deviceType == DeviceType.ears) {
+ if (device.baseDeviceDefinition.deviceType == DeviceType.ears && device.isTailCoNTROL.value != TailControlStatus.tailControl) {
commands.add(BluetoothMessage(message: "EARHOME", device: device, priority: priority, responseMSG: noResponseMsg ? null : "EARHOME END", type: type, timestamp: DateTime.now()));
} else {
commands.add(BluetoothMessage(message: "TAILHM", device: device, priority: priority, responseMSG: noResponseMsg ? null : "END TAILHM", type: type, timestamp: DateTime.now()));
}
} else if (move.moveType == MoveType.move) {
//TODO: Remove for TAILCoNTROL update
- if (device.baseDeviceDefinition.deviceType == DeviceType.ears) {
+ if (device.baseDeviceDefinition.deviceType == DeviceType.ears && device.isTailCoNTROL.value != TailControlStatus.tailControl) {
commands
..add(
BluetoothMessage(
diff --git a/lib/Backend/sensors.dart b/lib/Backend/sensors.dart
index 18de47079..d4f109a87 100644
--- a/lib/Backend/sensors.dart
+++ b/lib/Backend/sensors.dart
@@ -205,7 +205,7 @@ abstract class TriggerDefinition extends ChangeNotifier implements Comparable allActionsMapped = triggerAction.actions.map((element) => ref.read(getActionFromUUIDProvider(element))).whereNotNull().toList();
+ final List allActionsMapped = triggerAction.actions.map((element) => ref.read(getActionFromUUIDProvider(element))).nonNulls.toList();
// no moves exist
if (allActionsMapped.isEmpty) {
@@ -217,7 +217,13 @@ abstract class TriggerDefinition extends ChangeNotifier implements Comparable actionsToRun = [];
-
+ // we need to handle legacy ears for now
+ bool hasLegacyEars = ref
+ .read(getAvailableIdleGearForTypeProvider([DeviceType.ears].toBuiltSet()))
+ .where(
+ (p0) => p0.isTailCoNTROL.value == TailControlStatus.legacy,
+ )
+ .isNotEmpty;
// add a glowtip action if it exists
if (glowActions.isNotEmpty) {
final BaseAction glowAction = glowActions[_random.nextInt(glowActions.length)];
@@ -234,16 +240,36 @@ abstract class TriggerDefinition extends ChangeNotifier implements Comparable
missingGearAction = baseAction.deviceCategory.toSet().difference(baseAction.deviceCategory.toSet());
- final List remainingActions = moveActions
- .where(
- // Check if any actions contain the device type of the gear the first action is missing
- (element) => element.deviceCategory.toSet().intersection(missingGearAction).isNotEmpty,
+ final Set missingGearAction = baseAction.deviceCategory
+ .whereNot(
+ // filtering out the first actions ears entry if its a unified move but legacy gear is connected
+ (element) => DeviceType.ears == element && baseAction is CommandAction && hasLegacyEars,
)
- .toList();
+ .toSet()
+ .difference(flattenedDeviceTypes);
+ final List remainingActions = moveActions.where(
+ // Check if any actions contain the device type of the gear the first action is missing
+ (element) {
+ if (baseAction is CommandAction && missingGearAction.contains(DeviceType.ears)) {
+ if (element is EarsMoveList) {
+ return true;
+ } else if (element is CommandAction) {
+ return false;
+ }
+ } else if (baseAction is EarsMoveList && missingGearAction.contains(DeviceType.ears)) {
+ if (element is CommandAction) {
+ return true;
+ } else if (element is EarsMoveList) {
+ return false;
+ }
+ }
+ return element.deviceCategory.toSet().intersection(missingGearAction).isNotEmpty;
+ },
+ ).toList();
if (remainingActions.isNotEmpty) {
final BaseAction otherAction = remainingActions[_random.nextInt(remainingActions.length)];
actionsToRun.add(otherAction);
@@ -265,6 +291,12 @@ abstract class TriggerDefinition extends ChangeNotifier implements Comparable>? _contextStreamSubscription;
final _watch = WatchConnectivity();
@Riverpod(keepAlive: true)
-Future initWear(InitWearRef ref) async {
+Future initWear(Ref ref) async {
await Future.delayed(const Duration(seconds: 5));
try {
// Get the state of device connectivity
@@ -33,7 +35,7 @@ Future initWear(InitWearRef ref) async {
(event) => _wearLogger.info("Watch Context: $event"),
);
- ref.read(updateWearActionsProvider);
+ //ref.read(updateWearActionsProvider);
} catch (e, s) {
_wearLogger.severe("exception setting up Wear $e", e, s);
}
@@ -64,7 +66,10 @@ Future