diff --git a/Phone App/lib/biometric.dart b/Phone App/lib/biometric.dart index aaffa47..2ae431e 100644 --- a/Phone App/lib/biometric.dart +++ b/Phone App/lib/biometric.dart @@ -13,9 +13,14 @@ import "package:local_auth_android/local_auth_android.dart"; /// (so each biometric sign-in mints a fresh session/token). class BiometricAuth { final LocalAuthentication _auth = LocalAuthentication(); - final FlutterSecureStorage _store = const FlutterSecureStorage( - aOptions: AndroidOptions(encryptedSharedPreferences: true), - ); + // Default options: the Jetpack Security backend this used to ask for by name + // is deprecated by Google and gone in flutter_secure_storage v11, and v10 + // ignores the parameter and migrates what is already stored to its own ciphers + // on first access. Which is why this app is pinned below v11 — going straight + // there from v9 would skip that migration and leave the saved credentials + // unreadable, quietly switching biometric login off for anyone who had it on. + // Once a build carrying v10 has run on a device, v11 is a free bump. + final FlutterSecureStorage _store = const FlutterSecureStorage(); static const _emailKey = "cc_bio_email"; static const _passwordKey = "cc_bio_password"; diff --git a/Phone App/lib/screens/settings_screen.dart b/Phone App/lib/screens/settings_screen.dart index 7517f58..403ed5f 100644 --- a/Phone App/lib/screens/settings_screen.dart +++ b/Phone App/lib/screens/settings_screen.dart @@ -1209,18 +1209,12 @@ class _DataSectionState extends State<_DataSection> { _result = null; }); - final picked = await FilePicker.platform.pickFiles( + final file = await FilePicker.pickFile( type: FileType.custom, allowedExtensions: const ["json"], - withData: true, ); - if (picked == null || picked.files.isEmpty) return; - final file = picked.files.first; - final bytes = file.bytes ?? (file.path != null ? await File(file.path!).readAsBytes() : null); - if (bytes == null) { - setState(() => _error = t("settings.advanced.notJson")); - return; - } + if (file == null) return; + final bytes = await file.readAsBytes(); Map payload; try { diff --git a/Phone App/lib/widgets/attachment_field.dart b/Phone App/lib/widgets/attachment_field.dart index c25e716..fa284c1 100644 --- a/Phone App/lib/widgets/attachment_field.dart +++ b/Phone App/lib/widgets/attachment_field.dart @@ -5,7 +5,6 @@ import "package:flutter/material.dart"; import "package:open_filex/open_filex.dart"; import "package:path_provider/path_provider.dart"; -import "../api.dart"; import "../i18n.dart"; import "../main.dart"; import "../models.dart"; @@ -35,9 +34,10 @@ class PendingAttachment { Future applyAttachment(String path, String id, PendingAttachment pending) async { final picked = pending.file; if (picked != null) { - final bytes = picked.bytes ?? - (picked.path != null ? await File(picked.path!).readAsBytes() : null); - if (bytes == null) throw ApiException(0, t("errors.noFile")); + // readAsBytes reads whatever the platform actually handed back — a path, a + // content:// URI, a blob — so there is no longer a "picked a file but got no + // bytes" case for the caller to guard. + final bytes = await picked.readAsBytes(); await apiClient.uploadAttachment(path, id, bytes, picked.name); return; } @@ -117,14 +117,13 @@ class AttachmentField extends StatefulWidget { class _AttachmentFieldState extends State { Future _pick() async { - final res = await FilePicker.platform.pickFiles( + final picked = await FilePicker.pickFile( type: FileType.custom, allowedExtensions: _allowedExtensions, - withData: true, ); - if (res == null || res.files.isEmpty) return; + if (picked == null) return; setState(() { - widget.pending.file = res.files.first; + widget.pending.file = picked; // Picking a replacement supersedes a pending detach. widget.pending.remove = false; }); diff --git a/Phone App/pubspec.lock b/Phone App/pubspec.lock index 4eaa0df..1e17d5d 100644 --- a/Phone App/pubspec.lock +++ b/Phone App/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + android_file_picker: + dependency: transitive + description: + name: android_file_picker + sha256: "665a5a57dfca27f91a715d300e4852a784f9f98e503dcff281bec9afb55767be" + url: "https://pub.dev" + source: hosted + version: "1.0.1" archive: dependency: transitive description: @@ -117,10 +125,18 @@ packages: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.2.0" + ffi_leak_tracker: + dependency: transitive + description: + name: ffi_leak_tracker + sha256: "4093d4ef9ca06ffe2786e73bfb25e22aa92112b9bb4ec941f11e3e6b61489a97" + url: "https://pub.dev" + source: hosted + version: "0.1.2" file: dependency: transitive description: @@ -133,10 +149,42 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" + sha256: afbaa8015d9efabd224f41084ed9fdeddfa65389ebd7cd3a9eb1476aca66b46d url: "https://pub.dev" source: hosted - version: "10.3.10" + version: "12.0.0" + file_picker_darwin: + dependency: transitive + description: + name: file_picker_darwin + sha256: "5d87d156c1d63920447a662b7117d3498c67e3444a44d2cb01ed955d6efa62ef" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + file_picker_linux: + dependency: transitive + description: + name: file_picker_linux + sha256: "93d3f62f97c657053e7b184fe0f5e22347d85067c053640a30b1ac8ad7844e3b" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + file_picker_platform_interface: + dependency: transitive + description: + name: file_picker_platform_interface + sha256: "9e7a7e01e179929241f0afeb2c8c69ac95e29e17c0abea36ed193881c6bef90c" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + file_picker_web: + dependency: transitive + description: + name: file_picker_web + sha256: f1af38b3c91fafe0ca97f659b5c6818a057473ef09bb8b722f9f3f5364aa7eed + url: "https://pub.dev" + source: hosted + version: "3.0.1" file_selector_linux: dependency: transitive description: @@ -202,50 +250,50 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e" url: "https://pub.dev" source: hosted - version: "9.2.4" + version: "10.3.1" + flutter_secure_storage_darwin: + dependency: transitive + description: + name: flutter_secure_storage_darwin + sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149" + url: "https://pub.dev" + source: hosted + version: "0.3.2" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux - sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + sha256: "76fa9c841b3b1619fc5b5bc36efc7d158fa2356f223b6caeb1d0c80a54168546" url: "https://pub.dev" source: hosted - version: "1.2.3" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" - url: "https://pub.dev" - source: hosted - version: "3.1.3" + version: "3.0.2" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + sha256: "788060052712555182aba55ecb5f8b6e5cb9cfe8f776c83249a61fe3ce877db4" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "2.0.3" flutter_secure_storage_web: dependency: transitive description: name: flutter_secure_storage_web - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + sha256: "073a62b3aeb866ab4ce795f960413948e51e5a42a9b0c8333b6daf5bb3208a1c" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "2.1.1" flutter_secure_storage_windows: dependency: transitive description: name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + sha256: "471951813a97006d899db4948acc654a4f28c440083ea08178935ce20b173ec1" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "4.2.2" flutter_test: dependency: "direct dev" description: flutter @@ -376,14 +424,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" json_annotation: dependency: transitive description: @@ -604,18 +644,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "5bcf0772a761b04f8c6bf814721713de6f3e5d9d89caf8d3fe031b02a342379e" + sha256: "0634e64bd719f89c012f392938e173521f535d3ecaf66558fa94a056d22b5cc7" url: "https://pub.dev" source: hosted - version: "2.4.11" + version: "2.4.27" shared_preferences_foundation: dependency: transitive description: @@ -636,10 +676,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_web: dependency: transitive description: @@ -745,10 +785,18 @@ packages: dependency: transitive description: name: win32 - sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" + sha256: a0b93865d5644f11cf6a8c3f6db909f1ec168958b5805f6cc684adea957cd63d url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.4.0" + windows_file_picker: + dependency: transitive + description: + name: windows_file_picker + sha256: "72cf23466e146f2c0f19e1d78be97ff6409dc15b0c090f8eb284f94f4a33de26" + url: "https://pub.dev" + source: hosted + version: "1.0.1" xdg_directories: dependency: transitive description: diff --git a/Phone App/pubspec.yaml b/Phone App/pubspec.yaml index 7a7ff3e..c879e41 100644 --- a/Phone App/pubspec.yaml +++ b/Phone App/pubspec.yaml @@ -15,13 +15,20 @@ dependencies: cupertino_icons: ^1.0.8 image_picker: ^1.1.2 # Attachments are PDFs as well as images, which image_picker cannot offer. - file_picker: ^10.1.2 + # 12 or newer: 10 and 11 apply the Kotlin Gradle Plugin themselves, which a + # future Flutter will refuse to build. + file_picker: ^12.0.0 # Opens a downloaded attachment with whatever viewer the phone has for it. open_filex: ^4.5.0 path_provider: ^2.1.4 local_auth: ^2.3.0 local_auth_android: ^1.0.46 - flutter_secure_storage: ^9.2.2 + # Below 11 on purpose, not because 11 is broken: v10 migrates what v9 wrote + # with the now-deprecated Jetpack Security backend, and v11 removes that path. + # Bump the ceiling once a v10 build has run on every device that had biometric + # login on — see lib/biometric.dart. 10 is also what file_picker 12 needs, its + # win32 ^6 being incompatible with v9. + flutter_secure_storage: '>=10.0.0 <11.0.0' google_fonts: ^6.2.1 dev_dependencies: