diff --git a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java index fd457a4fe..c3ee24434 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channelhandlers/MetadataHandler.java @@ -183,6 +183,8 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler { } } result.success(metadataMap); + } catch (ImageProcessingException e) { + result.error("getCatalogMetadata-imageprocessing", "failed to get metadata for path=" + path + " (" + e.getMessage() + ")", null); } catch (FileNotFoundException e) { result.error("getCatalogMetadata-filenotfound", "failed to get metadata for path=" + path + " (" + e.getMessage() + ")", null); } catch (Exception e) { diff --git a/lib/main.dart b/lib/main.dart index ea0d18241..23555ceda 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,6 @@ import 'package:aves/model/image_decode_service.dart'; import 'package:aves/model/image_entry.dart'; -import 'package:aves/model/metadata_storage_service.dart'; +import 'package:aves/model/metadata_db.dart'; import 'package:aves/widgets/album/all_collection_page.dart'; import 'package:aves/widgets/common/fake_app_bar.dart'; import 'package:flutter/material.dart'; @@ -50,6 +50,7 @@ class _HomePageState extends State { onDone: () { debugPrint('mediastore stream done'); setState(() {}); + catalogEntries(); }, onError: (error) => debugPrint('mediastore stream error=$error'), ); @@ -65,4 +66,22 @@ class _HomePageState extends State { resizeToAvoidBottomInset: false, ); } + + catalogEntries() async { + debugPrint('$runtimeType catalogEntries cataloging start'); + await Future.forEach(entries, (entry) async { + await entry.catalog(); + }); + debugPrint('$runtimeType catalogEntries cataloging complete'); + + // sort with more accurate date + entries.sort((a,b) => b.bestDate.compareTo(a.bestDate)); + setState(() {}); + + debugPrint('$runtimeType catalogEntries locating start'); + await Future.forEach(entries, (entry) async { + await entry.locate(); + }); + debugPrint('$runtimeType catalogEntries locating done'); + } } diff --git a/lib/model/catalog_metadata.dart b/lib/model/catalog_metadata.dart deleted file mode 100644 index d4258ee7a..000000000 --- a/lib/model/catalog_metadata.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:geocoder/model.dart'; - -class CatalogMetadata { - final int contentId, dateMillis; - final String xmpSubjects; - final double latitude, longitude; - Address address; - - CatalogMetadata({this.contentId, this.dateMillis, this.xmpSubjects, double latitude, double longitude}) - // Geocoder throws an IllegalArgumentException when a coordinate has a funky values like 1.7056881853375E7 - : this.latitude = latitude == null || latitude < -90.0 || latitude > 90.0 ? null : latitude, - this.longitude = longitude == null || longitude < -180.0 || longitude > 180.0 ? null : longitude; - - factory CatalogMetadata.fromMap(Map map) { - return CatalogMetadata( - contentId: map['contentId'], - dateMillis: map['dateMillis'], - xmpSubjects: map['xmpSubjects'], - latitude: map['latitude'], - longitude: map['longitude'], - ); - } - - Map toMap() => { - 'contentId': contentId, - 'dateMillis': dateMillis, - 'xmpSubjects': xmpSubjects, - 'latitude': latitude, - 'longitude': longitude, - }; - - @override - String toString() { - return 'CatalogMetadata{contentId=$contentId, dateMillis=$dateMillis, latitude=$latitude, longitude=$longitude, xmpSubjects=$xmpSubjects}'; - } -} diff --git a/lib/model/image_decode_service.dart b/lib/model/image_decode_service.dart index 387059459..d2be1a3a6 100644 --- a/lib/model/image_decode_service.dart +++ b/lib/model/image_decode_service.dart @@ -16,7 +16,7 @@ class ImageDecodeService { } static Future getImageBytes(ImageEntry entry, int width, int height) async { - debugPrint('getImageBytes with path=${entry.path} contentId=${entry.contentId}'); +// debugPrint('getImageBytes with path=${entry.path} contentId=${entry.contentId}'); if (width > 0 && height > 0) { try { final result = await platform.invokeMethod('getImageBytes', { diff --git a/lib/model/image_entry.dart b/lib/model/image_entry.dart index d81137d92..14bcb8788 100644 --- a/lib/model/image_entry.dart +++ b/lib/model/image_entry.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/catalog_metadata.dart'; +import 'package:aves/model/image_metadata.dart'; import 'package:aves/model/metadata_service.dart'; import 'package:flutter/material.dart'; import 'package:geocoder/geocoder.dart'; diff --git a/lib/model/image_metadata.dart b/lib/model/image_metadata.dart new file mode 100644 index 000000000..22db0a610 --- /dev/null +++ b/lib/model/image_metadata.dart @@ -0,0 +1,67 @@ +import 'package:geocoder/model.dart'; + +class CatalogMetadata { + final int contentId, dateMillis; + final String xmpSubjects; + final double latitude, longitude; + Address address; + + CatalogMetadata({ + this.contentId, + this.dateMillis, + this.xmpSubjects, + double latitude, + double longitude, + }) + // Geocoder throws an IllegalArgumentException when a coordinate has a funky values like 1.7056881853375E7 + : this.latitude = latitude == null || latitude < -90.0 || latitude > 90.0 ? null : latitude, + this.longitude = longitude == null || longitude < -180.0 || longitude > 180.0 ? null : longitude; + + factory CatalogMetadata.fromMap(Map map) { + return CatalogMetadata( + contentId: map['contentId'], + dateMillis: map['dateMillis'], + xmpSubjects: map['xmpSubjects'], + latitude: map['latitude'], + longitude: map['longitude'], + ); + } + + Map toMap() => { + 'contentId': contentId, + 'dateMillis': dateMillis, + 'xmpSubjects': xmpSubjects, + 'latitude': latitude, + 'longitude': longitude, + }; + + @override + String toString() { + return 'CatalogMetadata{contentId=$contentId, dateMillis=$dateMillis, latitude=$latitude, longitude=$longitude, xmpSubjects=$xmpSubjects}'; + } +} + +class OverlayMetadata { + final String aperture, exposureTime, focalLength, iso; + + OverlayMetadata({ + String aperture, + this.exposureTime, + this.focalLength, + this.iso, + }) : this.aperture = aperture.replaceFirst('f', 'ƒ'); + + factory OverlayMetadata.fromMap(Map map) { + return OverlayMetadata( + aperture: map['aperture'], + exposureTime: map['exposureTime'], + focalLength: map['focalLength'], + iso: map['iso'], + ); + } + + @override + String toString() { + return 'OverlayMetadata{aperture=$aperture, exposureTime=$exposureTime, focalLength=$focalLength, iso=$iso}'; + } +} diff --git a/lib/model/metadata_storage_service.dart b/lib/model/metadata_db.dart similarity index 93% rename from lib/model/metadata_storage_service.dart rename to lib/model/metadata_db.dart index 9ab0af866..497ced091 100644 --- a/lib/model/metadata_storage_service.dart +++ b/lib/model/metadata_db.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/catalog_metadata.dart'; +import 'package:aves/model/image_metadata.dart'; import 'package:flutter/foundation.dart'; import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; @@ -52,7 +52,7 @@ class MetadataDb { } insert(CatalogMetadata metadata) async { - debugPrint('$runtimeType insert metadata=$metadata'); +// debugPrint('$runtimeType insert metadata=$metadata'); final db = await _database; await db.insert( table, diff --git a/lib/model/metadata_service.dart b/lib/model/metadata_service.dart index 1689cbdda..73cb8b2f7 100644 --- a/lib/model/metadata_service.dart +++ b/lib/model/metadata_service.dart @@ -1,5 +1,5 @@ -import 'package:aves/model/catalog_metadata.dart'; -import 'package:aves/model/metadata_storage_service.dart'; +import 'package:aves/model/image_metadata.dart'; +import 'package:aves/model/metadata_db.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -40,16 +40,16 @@ class MetadataService { return null; } - // return map with string descriptions for: 'aperture' 'exposureTime' 'focalLength' 'iso' - static Future getOverlayMetadata(String path) async { + static Future getOverlayMetadata(String path) async { try { + // return map with string descriptions for: 'aperture' 'exposureTime' 'focalLength' 'iso' final result = await platform.invokeMethod('getOverlayMetadata', { 'path': path, - }); - return result as Map; + }) as Map; + return OverlayMetadata.fromMap(result); } on PlatformException catch (e) { debugPrint('getOverlayMetadata failed with exception=${e.message}'); } - return Map(); + return null; } } diff --git a/lib/model/android_app_service.dart b/lib/utils/android_app_service.dart similarity index 100% rename from lib/model/android_app_service.dart rename to lib/utils/android_app_service.dart diff --git a/lib/widgets/debug_page.dart b/lib/widgets/debug_page.dart index 7f19a39f8..020cdea5f 100644 --- a/lib/widgets/debug_page.dart +++ b/lib/widgets/debug_page.dart @@ -1,5 +1,5 @@ -import 'package:aves/model/catalog_metadata.dart'; -import 'package:aves/model/metadata_storage_service.dart'; +import 'package:aves/model/image_metadata.dart'; +import 'package:aves/model/metadata_db.dart'; import 'package:flutter/material.dart'; class DebugPage extends StatefulWidget { diff --git a/lib/widgets/fullscreen/image_page.dart b/lib/widgets/fullscreen/image_page.dart index 4c1ae145f..37081081d 100644 --- a/lib/widgets/fullscreen/image_page.dart +++ b/lib/widgets/fullscreen/image_page.dart @@ -1,8 +1,8 @@ import 'dart:io'; import 'dart:math'; -import 'package:aves/model/android_app_service.dart'; import 'package:aves/model/image_entry.dart'; +import 'package:aves/utils/android_app_service.dart'; import 'package:aves/widgets/fullscreen/info/info_page.dart'; import 'package:aves/widgets/fullscreen/overlay_bottom.dart'; import 'package:aves/widgets/fullscreen/overlay_top.dart'; diff --git a/lib/widgets/fullscreen/overlay_bottom.dart b/lib/widgets/fullscreen/overlay_bottom.dart index 06577e39a..f4d6fab53 100644 --- a/lib/widgets/fullscreen/overlay_bottom.dart +++ b/lib/widgets/fullscreen/overlay_bottom.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'dart:ui'; import 'package:aves/model/image_entry.dart'; +import 'package:aves/model/image_metadata.dart'; import 'package:aves/model/metadata_service.dart'; import 'package:aves/widgets/common/blurred.dart'; import 'package:flutter/material.dart'; @@ -25,9 +26,9 @@ class FullscreenBottomOverlay extends StatefulWidget { } class _FullscreenBottomOverlayState extends State { - Future _detailLoader; + Future _detailLoader; ImageEntry _lastEntry; - Map _lastDetails; + OverlayMetadata _lastDetails; ImageEntry get entry => widget.entries[widget.index]; @@ -63,7 +64,7 @@ class _FullscreenBottomOverlayState extends State { padding: innerPadding, child: FutureBuilder( future: _detailLoader, - builder: (futureContext, AsyncSnapshot snapshot) { + builder: (futureContext, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done && !snapshot.hasError) { _lastDetails = snapshot.data; _lastEntry = entry; @@ -87,7 +88,7 @@ class _FullscreenBottomOverlayState extends State { class _FullscreenBottomOverlayContent extends StatelessWidget { final ImageEntry entry; - final Map details; + final OverlayMetadata details; final String position; final double maxWidth; @@ -129,7 +130,7 @@ class _FullscreenBottomOverlayContent extends StatelessWidget { ], ), ), - if (details != null && details.isNotEmpty) ...[ + if (details != null) ...[ SizedBox(height: 4), SizedBox( width: subRowWidth, @@ -137,10 +138,10 @@ class _FullscreenBottomOverlayContent extends StatelessWidget { children: [ Icon(Icons.camera, size: 16), SizedBox(width: 8), - Expanded(child: Text((details['aperture'] as String).replaceAll('f', 'ƒ'))), - Expanded(child: Text(details['exposureTime'])), - Expanded(child: Text(details['focalLength'])), - Expanded(child: Text(details['iso'])), + Expanded(child: Text(details.aperture)), + Expanded(child: Text(details.exposureTime)), + Expanded(child: Text(details.focalLength)), + Expanded(child: Text(details.iso)), ], ), ),