From d7269a1cba3af7d687edf79ccfbdac954433fc11 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Sat, 10 Apr 2021 23:21:34 +0800 Subject: [PATCH] Show MapView if GPS data exists in exif --- android/app/src/main/AndroidManifest.xml | 4 ++ lib/entity/exif.dart | 6 +++ lib/platform/features.dart | 3 ++ lib/widget/viewer_detail_pane.dart | 69 ++++++++++++++++++++++++ pubspec.lock | 35 ++++++++++++ pubspec.yaml | 2 + 6 files changed, 119 insertions(+) create mode 100644 lib/platform/features.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0ab9fa1e..68ada428 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -46,5 +46,9 @@ + + diff --git a/lib/entity/exif.dart b/lib/entity/exif.dart index f163291a..7cc4e8ef 100644 --- a/lib/entity/exif.dart +++ b/lib/entity/exif.dart @@ -89,6 +89,12 @@ class Exif { /// 0x920a FocalLength Rational get focalLength => data["FocalLength"]; + /// 0x8825 GPS tags + String get gpsLatitudeRef => data["GPSLatitudeRef"]; + List get gpsLatitude => data["GPSLatitude"].cast(); + String get gpsLongitudeRef => data["GPSLongitudeRef"]; + List get gpsLongitude => data["GPSLongitude"].cast(); + static final dateTimeFormat = DateFormat("yyyy:MM:dd HH:mm:ss"); final Map data; diff --git a/lib/platform/features.dart b/lib/platform/features.dart new file mode 100644 index 00000000..8f0fa3f6 --- /dev/null +++ b/lib/platform/features.dart @@ -0,0 +1,3 @@ +import 'dart:io'; + +final isSupportMapView = Platform.isAndroid; diff --git a/lib/widget/viewer_detail_pane.dart b/lib/widget/viewer_detail_pane.dart index 8166058b..1323ba4d 100644 --- a/lib/widget/viewer_detail_pane.dart +++ b/lib/widget/viewer_detail_pane.dart @@ -1,9 +1,12 @@ import 'dart:io'; +import 'package:android_intent/android_intent.dart'; +import 'package:exifdart/exifdart.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:intl/intl.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; @@ -13,6 +16,7 @@ import 'package:nc_photos/entity/exif.dart'; import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/exception_util.dart' as exception_util; import 'package:nc_photos/k.dart' as k; +import 'package:nc_photos/platform/features.dart' as features; import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/theme.dart'; import 'package:nc_photos/use_case/remove.dart'; @@ -135,6 +139,36 @@ class _ViewerDetailPaneState extends State { title: Text(_model), subtitle: cameraSubStr.isNotEmpty ? Text(cameraSubStr) : null, ), + if (features.isSupportMapView && _gps != null) + SizedBox( + height: 256, + child: GoogleMap( + compassEnabled: false, + mapToolbarEnabled: false, + rotateGesturesEnabled: false, + scrollGesturesEnabled: false, + zoomControlsEnabled: false, + zoomGesturesEnabled: false, + tiltGesturesEnabled: false, + myLocationButtonEnabled: false, + buildingsEnabled: false, + // liteModeEnabled: true, + initialCameraPosition: CameraPosition( + target: _gps, + zoom: 16, + ), + markers: { + Marker( + markerId: MarkerId("at"), + position: _gps, + // for some reason, GoogleMap's onTap is not triggered if + // tapped on top of the marker + onTap: _onMapTap, + ), + }, + onTap: (_) => _onMapTap(), + ), + ), ], ); } @@ -207,6 +241,16 @@ class _ViewerDetailPaneState extends State { } } + void _onMapTap() { + if (Platform.isAndroid) { + final intent = AndroidIntent( + action: "action_view", + data: Uri.encodeFull("geo:${_gps.latitude},${_gps.longitude}?z=16"), + ); + intent.launch(); + } + } + void _updateMetadata(int imageWidth, int imageHeight, Exif exif) { if (imageWidth != null && imageHeight != null) { setState(() { @@ -255,6 +299,30 @@ class _ViewerDetailPaneState extends State { _isoSpeedRatings = exif.isoSpeedRatings; }); } + if (exif.gpsLatitudeRef != null && + exif.gpsLatitude != null && + exif.gpsLongitudeRef != null && + exif.gpsLongitude != null) { + final lat = _gpsDmsToDouble(exif.gpsLatitude) * + (exif.gpsLatitudeRef == "S" ? -1 : 1); + final lng = _gpsDmsToDouble(exif.gpsLongitude) * + (exif.gpsLongitudeRef == "W" ? -1 : 1); + _log.fine("GPS: ($lat, $lng)"); + setState(() { + _gps = LatLng(lat, lng); + }); + } + } + + static double _gpsDmsToDouble(List dms) { + double product = dms[0].toDouble(); + if (dms.length > 1) { + product += dms[1].toDouble() / 60; + } + if (dms.length > 2) { + product += dms[2].toDouble() / 3600; + } + return product; } Future _addToAlbum(BuildContext context, Album album) async { @@ -287,6 +355,7 @@ class _ViewerDetailPaneState extends State { String _exposureTime; double _focalLength; int _isoSpeedRatings; + LatLng _gps; static final _log = Logger("widget.viewer_detail_pane._ViewerDetailPaneState"); diff --git a/pubspec.lock b/pubspec.lock index 5f7ef656..e02c3f78 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + android_intent: + dependency: "direct main" + description: + name: android_intent + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" archive: dependency: transitive description: @@ -153,6 +160,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" flutter_staggered_grid_view: dependency: "direct main" description: @@ -165,6 +179,20 @@ packages: description: flutter source: sdk version: "0.0.0" + google_maps_flutter: + dependency: "direct main" + description: + name: google_maps_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" + google_maps_flutter_platform_interface: + dependency: transitive + description: + name: google_maps_flutter_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" hashcodes: dependency: transitive description: @@ -459,6 +487,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.10.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" string_scanner: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index afefa15a..317d4e7d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: flutter_localizations: sdk: flutter + android_intent: ^2.0.0 bloc: ^7.0.0 cached_network_image: ^3.0.0 connectivity: ^3.0.2 @@ -38,6 +39,7 @@ dependencies: ref: 1.0.0 flutter_bloc: ^7.0.0 flutter_staggered_grid_view: ^0.3.3 + google_maps_flutter: ^2.0.3 http: ^0.13.1 idb_shim: ^2.0.0 idb_sqflite: ^1.0.0