From 6d5a9c6a1f3677e87bd33d1a6af0aa5ac0d09a02 Mon Sep 17 00:00:00 2001 From: otsmr Date: Wed, 7 Oct 2026 22:42:34 +0200 Subject: [PATCH] fix multiple smaller issues --- README.md | 10 + android/app/build.gradle | 3 + .../eu/twonly/location/NativeLocationTest.kt | 121 +++++++ android/app/src/debug/AndroidManifest.xml | 1 + .../twonly/location/LocationTestActivity.kt | 6 + .../eu/twonly/location/NativeLocation.kt | 58 +++- dependencies | 2 +- dependencies.py | 47 ++- dependencies.yaml | 8 + lib/core/backup/backup_archive.dart | 92 ++++++ lib/core/bridge/wrapper/backup.dart | 4 + lib/core/frb_generated.dart | 295 ++++++++++++++--- lib/core/frb_generated.io.dart | 67 ++++ lib/core/frb_generated.web.dart | 67 ++++ lib/src/database/daos/groups.dao.dart | 50 ++- .../visual/components/avatar_icon.comp.dart | 55 ++-- .../components/connection_status.comp.dart | 3 + .../camera_preview_controller_view.dart | 22 +- .../main_camera_controller.dart | 52 ++- .../share_image_contact_selection.view.dart | 277 ++++++++-------- .../views/camera/share_image_editor.view.dart | 3 +- .../database_sizes_developer.view.dart | 220 +++++++++++++ .../settings/developer/developer.view.dart | 7 + pubspec.lock | 9 +- pubspec.yaml | 5 +- ...7a51a9c093260171ec5b89934e3aee116f8e.json} | 54 +--- rust/src/backup/backup_archive.rs | 190 +++++++++++ rust/src/bridge/wrapper/backup.rs | 5 + rust/src/database/app/legacy_import.rs | 17 +- rust/src/frb_generated.rs | 305 +++++++++++++++--- rust/src/services/notifications.rs | 2 +- test/drift/groups_dao_test.dart | 144 +++++++++ test/scripts/test_dependencies.py | 117 +++++++ .../camera_preview_resolution_test.dart | 16 + test/visual/components/avatar_icon_test.dart | 88 +++++ 35 files changed, 2066 insertions(+), 356 deletions(-) create mode 100644 android/app/src/androidTest/kotlin/eu/twonly/location/NativeLocationTest.kt create mode 100644 android/app/src/debug/kotlin/eu/twonly/location/LocationTestActivity.kt create mode 100644 lib/core/backup/backup_archive.dart create mode 100644 lib/src/visual/views/settings/developer/database_sizes_developer.view.dart rename rust/.sqlx/{query-7ee254f54718b616580ea6ca979679f8ec8065490cf0ffd0a8f44818c1f57167.json => query-6b0d6a1df75da706448489fd05157a51a9c093260171ec5b89934e3aee116f8e.json} (65%) create mode 100644 test/drift/groups_dao_test.dart create mode 100644 test/scripts/test_dependencies.py create mode 100644 test/visual/camera_preview_resolution_test.dart create mode 100644 test/visual/components/avatar_icon_test.dart diff --git a/README.md b/README.md index 02894d5b..74afd659 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,16 @@ guarantee a bounty currently :/ Currently there are still some core features and rewrites open I want to do involving a huge rewrite in Rust. Because of this, contributions are currently not wanted. You can still view the code (if you find any security issues please contact me!). Also, issues are currently closed. If you find a bug, please use the in-app option as there you can upload your debug log which helps a lot. +## Custom dependencies + +Vendored packages are tracked in `dependencies.yaml`. Set `custom_changes: true` +on a dependency or an individual subpackage when it contains local modifications. +`dependencies.py` preserves these folders during normal and `--cache-only` +updates, and keeps their local versions and paths in the managed `pubspec.yaml` +sections. Update these packages manually and merge upstream changes with the +local modifications. Missing custom packages must be restored from the +`dependencies` repository. + ## Signing Keys When you download the app **via GitHub or F-Droid** you can verify the signing keys using for example the [AppVerifyer](https://github.com/soupslurpr/AppVerifier) and the following SHA-256 fingerprint of the signing certificate. diff --git a/android/app/build.gradle b/android/app/build.gradle index 7bc8a4ff..b41ec1ca 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -40,6 +40,7 @@ android { targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = [appName: "twonly"] } signingConfigs { @@ -75,6 +76,8 @@ flutter { } dependencies { + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'junit:junit:4.12' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4' implementation platform('com.google.firebase:firebase-bom:34.9.0') implementation 'com.google.firebase:firebase-messaging' diff --git a/android/app/src/androidTest/kotlin/eu/twonly/location/NativeLocationTest.kt b/android/app/src/androidTest/kotlin/eu/twonly/location/NativeLocationTest.kt new file mode 100644 index 00000000..45af657e --- /dev/null +++ b/android/app/src/androidTest/kotlin/eu/twonly/location/NativeLocationTest.kt @@ -0,0 +1,121 @@ +package eu.twonly.location + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.location.Criteria +import android.location.Location +import android.location.LocationManager +import android.os.Handler +import android.os.Looper +import android.os.SystemClock +import androidx.test.filters.SdkSuppress +import androidx.test.platform.app.InstrumentationRegistry +import org.json.JSONArray +import org.junit.After +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +/** Run on an emulator: the mock providers are removed after every test. */ +@SdkSuppress(minSdkVersion = 31) +class NativeLocationTest { + companion object { + private var activityStarted = false + } + private val instrumentation = InstrumentationRegistry.getInstrumentation() + private val context = instrumentation.targetContext + private val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + private val providers = listOf("fused", LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER) + + @Before + fun setUp() { + instrumentation.uiAutomation.grantRuntimePermission(context.packageName, Manifest.permission.ACCESS_COARSE_LOCATION) + instrumentation.uiAutomation.grantRuntimePermission(context.packageName, Manifest.permission.ACCESS_FINE_LOCATION) + if (!activityStarted) { + instrumentation.startActivitySync( + Intent(context, LocationTestActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), + ) + activityStarted = true + } + instrumentation.uiAutomation.executeShellCommand( + "appops set ${context.packageName} android:mock_location allow", + ).use { android.os.ParcelFileDescriptor.AutoCloseInputStream(it).readBytes() } + for (provider in providers) { + @Suppress("DEPRECATION") + manager.addTestProvider(provider, false, false, false, false, true, true, true, + Criteria.POWER_LOW, Criteria.ACCURACY_FINE) + manager.setTestProviderEnabled(provider, true) + } + } + + @After + fun tearDown() { + for (provider in providers) { + runCatching { manager.removeTestProvider(provider) } + } + instrumentation.uiAutomation.executeShellCommand( + "appops set ${context.packageName} android:mock_location default", + ).use { android.os.ParcelFileDescriptor.AutoCloseInputStream(it).readBytes() } + } + + private fun fix(provider: String, ageMillis: Long = 0, accuracyMeters: Float = 10f): Location = + Location(provider).apply { + latitude = 52.52 + longitude = 13.405 + accuracy = accuracyMeters + time = System.currentTimeMillis() - ageMillis + elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos() - ageMillis * 1_000_000 + } + + private fun assertFix(value: String?) { + assertNotNull("A recent precise fix should be returned", value) + val result = JSONArray(value!!) + assertEquals(52.52, result.getDouble(0), 0.000001) + assertEquals(13.405, result.getDouble(1), 0.000001) + assertEquals(10.0, result.getDouble(2), 0.000001) + } + + @Test + fun recentGpsFixIsReturnedWithoutWaitingForAnUpdate() { + manager.setTestProviderLocation(LocationManager.GPS_PROVIDER, fix(LocationManager.GPS_PROVIDER)) + assertFix(NativeLocation.current(500)) + } + + @Test + fun recentFusedFixIsReturnedWithoutWaitingForGps() { + manager.setTestProviderLocation("fused", fix("fused")) + assertFix(NativeLocation.current(500)) + } + + @Test + fun staleCachedFixIsNotStoredAsTheCaptureLocation() { + manager.setTestProviderLocation(LocationManager.GPS_PROVIDER, fix(LocationManager.GPS_PROVIDER, 240_000)) + assertNull(NativeLocation.current(200)) + } + + @Test + fun liveFusedUpdateIsUsedWhenThereIsNoCachedFix() { + manager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, false) + manager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, false) + Handler(Looper.getMainLooper()).postDelayed({ + manager.setTestProviderLocation("fused", fix("fused")) + }, 100) + assertFix(NativeLocation.current(2_000)) + } + + @Test + fun bestAvailableFixIsReturnedAtTheDeadline() { + manager.setTestProviderLocation(LocationManager.NETWORK_PROVIDER, + fix(LocationManager.NETWORK_PROVIDER, accuracyMeters = 100f)) + val value = NativeLocation.current(200) + assertNotNull(value) + assertEquals(100.0, JSONArray(value!!).getDouble(2), 0.000001) + } + + @Test + fun disabledProvidersReturnWithoutARequest() { + for (provider in providers) manager.setTestProviderEnabled(provider, false) + assertNull(NativeLocation.current(200)) + } +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 1fcfe704..3430f5d4 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -5,6 +5,7 @@ --> +