diff --git a/mobile/apps/photos/lib/db/ml/db.dart b/mobile/apps/photos/lib/db/ml/db.dart index 851e349d00..5ab5d28c8b 100644 --- a/mobile/apps/photos/lib/db/ml/db.dart +++ b/mobile/apps/photos/lib/db/ml/db.dart @@ -360,6 +360,10 @@ class MLDataDB with SqlDbBase implements IMLDataDB { } return mapRowToFace(faceMaps.first); } + } else if (clusterID == null) { + _logger.severe( + "Didn't find any faces for personID $personID in `getCoverFaceForPerson`.", + ); } if (clusterID != null) { const String queryFaceID = ''' @@ -380,11 +384,19 @@ class MLDataDB with SqlDbBase implements IMLDataDB { return face; } } + } else { + _logger.severe( + "Didn't find any faces for clusterID $clusterID in `getCoverFaceForPerson`. faces: $faces", + ); } } if (personID == null && clusterID == null) { + _logger.severe("personID and clusterID cannot be null both"); throw Exception("personID and clusterID cannot be null"); } + _logger.severe( + "Something went wrong finding a face from `getCoverFaceForPerson` (personID: $personID, clusterID: $clusterID)", + ); return null; } diff --git a/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart b/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart index 2e79215cf1..b11ff67fb5 100644 --- a/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/face_ml/face_clustering/face_clustering_service.dart @@ -8,11 +8,11 @@ import "package:ml_linalg/dtype.dart"; import "package:ml_linalg/vector.dart"; import "package:photos/generated/protos/ente/common/vector.pb.dart"; import "package:photos/models/base/id.dart"; -import "package:photos/services/isolate_functions.dart"; -import "package:photos/services/isolate_service.dart"; import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart"; import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart"; import "package:photos/services/machine_learning/ml_result.dart"; +import "package:photos/utils/isolate/isolate_operations.dart"; +import "package:photos/utils/isolate/super_isolate.dart"; class FaceInfo { final String faceID; @@ -507,7 +507,8 @@ ClusteringResult _runCompleteClustering(Map args) { EVector.fromBuffer(entry.value).values, dtype: DType.float32, ), - fileCreationTime: fileIDToCreationTime?[getFileIdFromFaceId(entry.key)], + fileCreationTime: + fileIDToCreationTime?[getFileIdFromFaceId(entry.key)], ), ); } diff --git a/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart b/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart index 4980f2e904..6e09744cff 100644 --- a/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart +++ b/mobile/apps/photos/lib/services/machine_learning/face_thumbnail_generator.dart @@ -1,15 +1,13 @@ import 'dart:async'; import 'dart:typed_data' show Uint8List; -import "package:computer/computer.dart"; import "package:logging/logging.dart"; import "package:photos/models/ml/face/box.dart"; -import "package:photos/services/isolate_functions.dart"; -import "package:photos/services/isolate_service.dart"; import "package:photos/utils/image_ml_util.dart"; +import "package:photos/utils/isolate/isolate_operations.dart"; +import "package:photos/utils/isolate/super_isolate.dart"; -final Computer _computer = Computer.shared(); - +@pragma('vm:entry-point') class FaceThumbnailGenerator extends SuperIsolate { @override Logger get logger => _logger; @@ -37,20 +35,30 @@ class FaceThumbnailGenerator extends SuperIsolate { String imagePath, List faceBoxes, ) async { - final List> faceBoxesJson = - faceBoxes.map((box) => box.toJson()).toList(); - final List faces = await runInIsolate( - IsolateOperation.generateFaceThumbnails, - { - 'imagePath': imagePath, - 'faceBoxesList': faceBoxesJson, - }, - ).then((value) => value.cast()); - final compressedFaces = - await compressFaceThumbnails({'listPngBytes': faces}); - _logger.fine( - "Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes", - ); - return compressedFaces; + try { + _logger.info( + "Generating face thumbnails for ${faceBoxes.length} face boxes in $imagePath", + ); + final List> faceBoxesJson = + faceBoxes.map((box) => box.toJson()).toList(); + final List faces = await runInIsolate( + IsolateOperation.generateFaceThumbnails, + { + 'imagePath': imagePath, + 'faceBoxesList': faceBoxesJson, + }, + ).then((value) => value.cast()); + _logger.info("Generated face thumbnails"); + final compressedFaces = + await compressFaceThumbnails({'listPngBytes': faces}); + _logger.fine( + "Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes", + ); + return compressedFaces; + } catch (e, s) { + _logger.severe("Failed to generate face thumbnails", e, s); + + rethrow; + } } } diff --git a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart index edbc16b7ff..939d486f64 100644 --- a/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart +++ b/mobile/apps/photos/lib/services/machine_learning/ml_computer.dart @@ -2,12 +2,12 @@ import 'dart:async'; import "package:logging/logging.dart"; import "package:photos/models/ml/vector.dart"; -import "package:photos/services/isolate_functions.dart"; -import "package:photos/services/isolate_service.dart"; import "package:photos/services/machine_learning/ml_constants.dart"; import "package:photos/services/machine_learning/semantic_search/clip/clip_text_encoder.dart"; import "package:photos/services/machine_learning/semantic_search/query_result.dart"; import "package:photos/services/remote_assets_service.dart"; +import "package:photos/utils/isolate/isolate_operations.dart"; +import "package:photos/utils/isolate/super_isolate.dart"; import "package:synchronized/synchronized.dart"; class MLComputer extends SuperIsolate { diff --git a/mobile/apps/photos/lib/services/machine_learning/ml_indexing_isolate.dart b/mobile/apps/photos/lib/services/machine_learning/ml_indexing_isolate.dart index e66848bb27..b386674b31 100644 --- a/mobile/apps/photos/lib/services/machine_learning/ml_indexing_isolate.dart +++ b/mobile/apps/photos/lib/services/machine_learning/ml_indexing_isolate.dart @@ -2,14 +2,14 @@ import "dart:async"; import "package:flutter/foundation.dart" show debugPrint; import "package:logging/logging.dart"; -import "package:photos/services/isolate_functions.dart"; -import "package:photos/services/isolate_service.dart"; import 'package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart'; import 'package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart'; import "package:photos/services/machine_learning/ml_models_overview.dart"; import 'package:photos/services/machine_learning/ml_result.dart'; import "package:photos/services/machine_learning/semantic_search/clip/clip_image_encoder.dart"; import "package:photos/services/remote_assets_service.dart"; +import "package:photos/utils/isolate/isolate_operations.dart"; +import "package:photos/utils/isolate/super_isolate.dart"; import "package:photos/utils/ml_util.dart"; import "package:photos/utils/network_util.dart"; import "package:synchronized/synchronized.dart"; diff --git a/mobile/apps/photos/lib/ui/viewer/people/person_face_widget.dart b/mobile/apps/photos/lib/ui/viewer/people/person_face_widget.dart index 8bd069f57e..3e1b138c26 100644 --- a/mobile/apps/photos/lib/ui/viewer/people/person_face_widget.dart +++ b/mobile/apps/photos/lib/ui/viewer/people/person_face_widget.dart @@ -21,6 +21,7 @@ class PersonFaceWidget extends StatefulWidget { final String? clusterID; final bool useFullFile; final VoidCallback? onErrorCallback; + final bool keepAlive; // PersonFaceWidget constructor checks that both personId and clusterID are not null // and that the file is not null @@ -29,6 +30,7 @@ class PersonFaceWidget extends StatefulWidget { this.clusterID, this.useFullFile = true, this.onErrorCallback, + this.keepAlive = false, super.key, }) : assert( personId != null || clusterID != null, @@ -39,12 +41,16 @@ class PersonFaceWidget extends StatefulWidget { State createState() => _PersonFaceWidgetState(); } -class _PersonFaceWidgetState extends State { +class _PersonFaceWidgetState extends State + with AutomaticKeepAliveClientMixin { Future? faceCropFuture; EnteFile? fileForFaceCrop; bool get isPerson => widget.personId != null; + @override + bool get wantKeepAlive => widget.keepAlive; + @override void initState() { super.initState(); @@ -64,6 +70,10 @@ class _PersonFaceWidgetState extends State { @override Widget build(BuildContext context) { + super.build( + context, + ); // Calling super.build for AutomaticKeepAliveClientMixin + return FutureBuilder( future: faceCropFuture, builder: (context, snapshot) { @@ -163,7 +173,7 @@ class _PersonFaceWidgetState extends State { } } if (fileForFaceCrop == null) { - _logger.warning( + _logger.severe( "No suitable file found for face crop for person: ${widget.personId} or cluster: ${widget.clusterID}", ); return null; @@ -176,7 +186,7 @@ class _PersonFaceWidgetState extends State { clusterID: widget.clusterID, ); if (face == null) { - debugPrint( + _logger.severe( "No cover face for person: ${widget.personId} or cluster ${widget.clusterID} and fileID ${fileForFaceCrop.uploadedFileID!}", ); return null; @@ -188,7 +198,13 @@ class _PersonFaceWidgetState extends State { personOrClusterID: personOrClusterId, useTempCache: false, ); - return cropMap?[face.faceID]; + final result = cropMap?[face.faceID]; + if (result == null) { + _logger.severe( + "Null cover face crop for person: ${widget.personId} or cluster ${widget.clusterID} and fileID ${fileForFaceCrop.uploadedFileID!}", + ); + } + return result; } catch (e, s) { _logger.severe( "Error getting cover face for person: ${widget.personId} or cluster ${widget.clusterID}", diff --git a/mobile/apps/photos/lib/ui/viewer/search/result/people_section_all_page.dart b/mobile/apps/photos/lib/ui/viewer/search/result/people_section_all_page.dart index d78eb4b9ba..d711bd9ebd 100644 --- a/mobile/apps/photos/lib/ui/viewer/search/result/people_section_all_page.dart +++ b/mobile/apps/photos/lib/ui/viewer/search/result/people_section_all_page.dart @@ -95,12 +95,14 @@ class SelectablePersonSearchExample extends StatelessWidget { final GenericSearchResult searchResult; final double size; final SelectedPeople selectedPeople; + final bool isDefaultFace; const SelectablePersonSearchExample({ super.key, required this.searchResult, required this.selectedPeople, this.size = 102, + this.isDefaultFace = false, }); void _handleTap(BuildContext context) { @@ -192,7 +194,10 @@ class SelectablePersonSearchExample extends StatelessWidget { searchResult.previewThumbnail()!, shouldShowSyncStatus: false, ) - : FaceSearchResult(searchResult); + : FaceSearchResult( + searchResult, + isDefaultFace: isDefaultFace, + ); } else { child = const NoThumbnailWidget( addBorder: false, @@ -301,8 +306,13 @@ class SelectablePersonSearchExample extends StatelessWidget { class FaceSearchResult extends StatelessWidget { final SearchResult searchResult; + final bool isDefaultFace; - const FaceSearchResult(this.searchResult, {super.key}); + const FaceSearchResult( + this.searchResult, { + super.key, + this.isDefaultFace = false, + }); @override Widget build(BuildContext context) { @@ -313,6 +323,7 @@ class FaceSearchResult extends StatelessWidget { key: params.containsKey(kPersonWidgetKey) ? ValueKey(params[kPersonWidgetKey]) : ValueKey(params[kPersonParamID] ?? params[kClusterParamId]), + keepAlive: isDefaultFace, ); } } @@ -486,6 +497,7 @@ class _PeopleSectionAllWidgetState extends State { searchResult: normalFaces[index], size: itemSize, selectedPeople: widget.selectedPeople!, + isDefaultFace: true, ) : PersonSearchExample( searchResult: normalFaces[index], @@ -525,6 +537,7 @@ class _PeopleSectionAllWidgetState extends State { searchResult: extraFaces[index], size: itemSize, selectedPeople: widget.selectedPeople!, + isDefaultFace: false, ) : PersonSearchExample( searchResult: extraFaces[index], diff --git a/mobile/apps/photos/lib/utils/face/face_thumbnail_cache.dart b/mobile/apps/photos/lib/utils/face/face_thumbnail_cache.dart index 117793173f..448eb7789a 100644 --- a/mobile/apps/photos/lib/utils/face/face_thumbnail_cache.dart +++ b/mobile/apps/photos/lib/utils/face/face_thumbnail_cache.dart @@ -129,7 +129,7 @@ Future?> getCachedFaceCrops( ); faceIdToCrop[face.faceID] = data; } else { - _logger.warning( + _logger.severe( "Cached face crop for faceID ${face.faceID} is empty, deleting file ${faceCropCacheFile.path}", ); await faceCropCacheFile.delete(); @@ -231,7 +231,7 @@ Future?> getCachedFaceCrops( s, ); } else { - _logger.info( + _logger.severe( "Stopped getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()} due to $e", ); } @@ -334,12 +334,14 @@ Future?> _getFaceCrops( if (useFullFile && file.fileType != FileType.video) { final File? ioFile = await getFile(file); if (ioFile == null) { + _logger.severe("Failed to get file for face crop generation"); return null; } imagePath = ioFile.path; } else { final thumbnail = await getThumbnailForUploadedFile(file); if (thumbnail == null) { + _logger.severe("Failed to get thumbnail for face crop generation"); return null; } imagePath = thumbnail.path; diff --git a/mobile/apps/photos/lib/utils/image_ml_util.dart b/mobile/apps/photos/lib/utils/image_ml_util.dart index 0ab1428fea..ab8b4a4e0f 100644 --- a/mobile/apps/photos/lib/utils/image_ml_util.dart +++ b/mobile/apps/photos/lib/utils/image_ml_util.dart @@ -575,7 +575,7 @@ Future> compressFaceThumbnails(Map args) async { } return await Future.wait(compressedBytesList); } catch (e, s) { - _logger.warning( + _logger.severe( 'Failed to compress face thumbnail, using original. Size: ${listPngBytes.map((e) => e.length).toList()} bytes', e, s, diff --git a/mobile/apps/photos/lib/services/isolate_functions.dart b/mobile/apps/photos/lib/utils/isolate/isolate_operations.dart similarity index 100% rename from mobile/apps/photos/lib/services/isolate_functions.dart rename to mobile/apps/photos/lib/utils/isolate/isolate_operations.dart diff --git a/mobile/apps/photos/lib/services/isolate_service.dart b/mobile/apps/photos/lib/utils/isolate/super_isolate.dart similarity index 96% rename from mobile/apps/photos/lib/services/isolate_service.dart rename to mobile/apps/photos/lib/utils/isolate/super_isolate.dart index 4a91c64473..3a83eaef1b 100644 --- a/mobile/apps/photos/lib/services/isolate_service.dart +++ b/mobile/apps/photos/lib/utils/isolate/super_isolate.dart @@ -7,9 +7,10 @@ import "package:flutter/services.dart"; import "package:logging/logging.dart"; import "package:photos/core/error-reporting/isolate_logging.dart"; import "package:photos/models/base/id.dart"; -import "package:photos/services/isolate_functions.dart"; +import "package:photos/utils/isolate/isolate_operations.dart"; import "package:synchronized/synchronized.dart"; +@pragma('vm:entry-point') abstract class SuperIsolate { Logger get logger; @@ -80,6 +81,8 @@ abstract class SuperIsolate { if (rootToken != null) { BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken); } + final logger = Logger('SuperIsolate'); + logger.info('IsolateMain started'); receivePort.listen((message) async { final taskID = message[0] as String; @@ -87,6 +90,7 @@ abstract class SuperIsolate { final function = IsolateOperation.values[functionIndex]; final args = message[2] as Map; final sendPort = message[3] as SendPort; + logger.info("Starting isolate operation $function in isolate"); late final Object data; try { diff --git a/mobile/apps/photos/pubspec.lock b/mobile/apps/photos/pubspec.lock index 58284d5f09..a5d14c3829 100644 --- a/mobile/apps/photos/pubspec.lock +++ b/mobile/apps/photos/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" url: "https://pub.dev" source: hosted - version: "72.0.0" + version: "76.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,7 +21,7 @@ packages: dependency: transitive description: dart source: sdk - version: "0.3.2" + version: "0.3.3" adaptive_theme: dependency: "direct main" description: @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: analyzer - sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" url: "https://pub.dev" source: hosted - version: "6.7.0" + version: "6.11.0" android_intent_plus: dependency: "direct main" description: @@ -130,10 +130,10 @@ packages: dependency: "direct main" description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" battery_info: dependency: "direct main" description: @@ -155,10 +155,10 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" brotli: dependency: transitive description: @@ -268,10 +268,10 @@ packages: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" checked_yaml: dependency: transitive description: @@ -301,10 +301,10 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" code_builder: dependency: transitive description: @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" computer: dependency: "direct main" description: @@ -619,10 +619,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" fast_base58: dependency: "direct main" description: @@ -668,10 +668,10 @@ packages: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" file_saver: dependency: "direct main" description: @@ -1416,18 +1416,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -1536,10 +1536,10 @@ packages: dependency: transitive description: name: macros - sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" url: "https://pub.dev" source: hosted - version: "0.1.2-main.4" + version: "0.1.3-main.0" maps_launcher: dependency: "direct main" description: @@ -1552,10 +1552,10 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: @@ -1645,10 +1645,10 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.16.0" mgrs_dart: dependency: transitive description: @@ -1859,10 +1859,10 @@ packages: dependency: "direct main" description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" path_drawing: dependency: transitive description: @@ -2019,10 +2019,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -2068,10 +2068,10 @@ packages: dependency: transitive description: name: process - sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.3" proj4dart: dependency: transitive description: @@ -2309,7 +2309,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_gen: dependency: transitive description: @@ -2346,10 +2346,10 @@ packages: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" sprintf: dependency: transitive description: @@ -2434,10 +2434,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" step_progress_indicator: dependency: "direct main" description: @@ -2450,10 +2450,10 @@ packages: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" stream_transform: dependency: transitive description: @@ -2466,10 +2466,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" styled_text: dependency: "direct main" description: @@ -2522,34 +2522,34 @@ packages: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test: dependency: "direct dev" description: name: test - sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e" + sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" url: "https://pub.dev" source: hosted - version: "1.25.7" + version: "1.25.15" test_api: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.4" test_core: dependency: transitive description: name: test_core - sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696" + sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa" url: "https://pub.dev" source: hosted - version: "0.6.4" + version: "0.6.8" thermal: dependency: "direct main" description: @@ -2813,10 +2813,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.1" volume_controller: dependency: transitive description: @@ -2877,10 +2877,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.4" webkit_inspection_protocol: dependency: transitive description: @@ -2978,5 +2978,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.24.0"