diff --git a/lib/db/photo_db.dart b/lib/db/photo_db.dart index d02ad9796..c8d72dac8 100644 --- a/lib/db/photo_db.dart +++ b/lib/db/photo_db.dart @@ -20,6 +20,8 @@ class PhotoDB { static final columnLocalId = 'local_id'; static final columnTitle = 'title'; static final columnDeviceFolder = 'device_folder'; + static final columnLatitude = 'latitude'; + static final columnLongitude = 'longitude'; static final columnRemoteFolderId = 'remote_folder_id'; static final columnRemotePath = 'remote_path'; static final columnThumbnailPath = 'thumbnail_path'; @@ -57,6 +59,8 @@ class PhotoDB { $columnUploadedFileId INTEGER NOT NULL, $columnTitle TEXT NOT NULL, $columnDeviceFolder TEXT NOT NULL, + $columnLatitude REAL, + $columnLongitude REAL, $columnRemoteFolderId INTEGER DEFAULT -1, $columnRemotePath TEXT, $columnThumbnailPath TEXT, @@ -283,6 +287,8 @@ class PhotoDB { photo.uploadedFileId == null ? -1 : photo.uploadedFileId; row[columnTitle] = photo.title; row[columnDeviceFolder] = photo.deviceFolder; + row[columnLatitude] = photo.latitude; + row[columnLongitude] = photo.longitude; row[columnRemoteFolderId] = photo.remoteFolderId; row[columnRemotePath] = photo.remotePath; row[columnThumbnailPath] = photo.thumbnailPath; @@ -298,6 +304,8 @@ class PhotoDB { photo.uploadedFileId = row[columnUploadedFileId]; photo.title = row[columnTitle]; photo.deviceFolder = row[columnDeviceFolder]; + photo.latitude = row[columnLatitude]; + photo.longitude = row[columnLongitude]; photo.remoteFolderId = row[columnRemoteFolderId]; photo.remotePath = row[columnRemotePath]; photo.thumbnailPath = row[columnThumbnailPath]; diff --git a/lib/models/photo.dart b/lib/models/photo.dart index be16c684d..79a0fc165 100644 --- a/lib/models/photo.dart +++ b/lib/models/photo.dart @@ -18,6 +18,8 @@ class Photo { String thumbnailPath; int createTimestamp; int updateTimestamp; + double latitude; + double longitude; Photo(); Photo.fromJson(Map json) @@ -37,6 +39,9 @@ class Photo { photo.localId = asset.id; photo.title = asset.title; photo.deviceFolder = pathEntity.name; + final location = await asset.latlngAsync(); + photo.latitude = location.latitude; + photo.longitude = location.longitude; photo.createTimestamp = asset.createDateTime.microsecondsSinceEpoch; if (photo.createTimestamp == 0) { try { diff --git a/lib/ui/location_search_results_page.dart b/lib/ui/location_search_results_page.dart new file mode 100644 index 000000000..f94f17894 --- /dev/null +++ b/lib/ui/location_search_results_page.dart @@ -0,0 +1,73 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:latlong/latlong.dart'; +import 'package:photos/models/photo.dart'; +import 'package:photos/photo_repository.dart'; +import 'package:photos/ui/gallery.dart'; +import 'package:photos/ui/loading_widget.dart'; + +class LocationSearchResultsPage extends StatefulWidget { + final LatLng location; + final String name; + + LocationSearchResultsPage(this.location, this.name, {Key key}) + : super(key: key); + + @override + _LocationSearchResultsPageState createState() => + _LocationSearchResultsPageState(); +} + +class _LocationSearchResultsPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.name), + ), + body: Container( + child: _getBody(), + ), + ); + } + + FutureBuilder> _getBody() { + return FutureBuilder>( + future: _getResult(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return Gallery( + snapshot.data, + Set(), + ); + } else { + return Center(child: loadWidget); + } + }, + ); + } + + Future> _getResult() async { + final photos = PhotoRepository.instance.photos; + final args = Map(); + args['photos'] = photos; + args['location'] = widget.location; + args['maxDistance'] = 5000; + return await compute(_filterPhotos, args); + } + + static List _filterPhotos(Map args) { + List photos = args['photos']; + LatLng location = args['location']; + int maxDistance = args['maxDistance']; + final result = List(); + for (final photo in photos) { + final distance = Distance().as(LengthUnit.Meter, location, + new LatLng(photo.latitude, photo.longitude)); + if (distance < maxDistance) { + result.add(photo); + } + } + return result; + } +} diff --git a/lib/ui/search_page.dart b/lib/ui/search_page.dart index 51535d795..06b74aaac 100644 --- a/lib/ui/search_page.dart +++ b/lib/ui/search_page.dart @@ -1,12 +1,18 @@ +import 'dart:developer'; + import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; +import 'package:latlong/latlong.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/face_search_manager.dart'; import 'package:photos/models/face.dart'; +import 'package:photos/models/photo.dart'; +import 'package:photos/photo_repository.dart'; import 'package:photos/ui/circular_network_image_widget.dart'; import 'package:photos/ui/face_search_results_page.dart'; import 'package:photos/ui/loading_widget.dart'; +import 'package:photos/ui/location_search_results_page.dart'; class SearchPage extends StatefulWidget { @override @@ -55,8 +61,14 @@ class _SearchPageState extends State { return LocationSearchResultWidget(suggestion['name']); }, onSuggestionSelected: (suggestion) { - // Navigator.of(context).push(MaterialPageRoute( - // builder: (context) => ProductPage(product: suggestion))); + double latitude = suggestion['geometry']['location']['lat']; + double longitude = suggestion['geometry']['location']['lng']; + Navigator.pop(context); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => LocationSearchResultsPage( + new LatLng(latitude, longitude), + suggestion['name'], + ))); }, ), actions: [ @@ -108,9 +120,8 @@ class _SearchPageState extends State { } void _routeToSearchResults(Face face, BuildContext context) { - final page = FaceSearchResultsPage( - face: face, - ); + final page = FaceSearchResultsPage(face); + Navigator.pop(context); Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { diff --git a/pubspec.lock b/pubspec.lock index fec2a426f..d5cde34dc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + ansicolor: + dependency: transitive + description: + name: ansicolor + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" archive: dependency: "direct main" description: @@ -64,6 +71,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.5" + console_log_handler: + dependency: transitive + description: + name: console_log_handler + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.6" convert: dependency: transitive description: @@ -212,6 +226,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + latlong: + dependency: "direct main" + description: + name: latlong + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.1" like_button: dependency: "direct main" description: @@ -483,6 +504,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.4.1" + validate: + dependency: transitive + description: + name: validate + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 22d5a9232..299db01a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: logging: ^0.11.4 flutter_image_compress: ^0.6.5+1 flutter_typeahead: ^1.8.1 + latlong: ^0.6.1 dev_dependencies: flutter_test: