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