diff --git a/lib/core/lru_map.dart b/lib/core/lru_map.dart index 999d73035..55ebaba05 100644 --- a/lib/core/lru_map.dart +++ b/lib/core/lru_map.dart @@ -1,5 +1,4 @@ import 'dart:collection'; -import 'dart:typed_data'; import 'package:flutter/material.dart'; diff --git a/lib/db/db_helper.dart b/lib/db/db_helper.dart index e1f327f7c..84cd8b98d 100644 --- a/lib/db/db_helper.dart +++ b/lib/db/db_helper.dart @@ -12,6 +12,7 @@ class DatabaseHelper { static final table = 'photos'; static final columnLocalPath = 'local_path'; + static final columnThumbnailPath = 'thumbnail_path'; static final columnUrl = 'url'; static final columnHash = 'hash'; static final columnSyncTimestamp = 'sync_timestamp'; @@ -42,6 +43,7 @@ class DatabaseHelper { await db.execute(''' CREATE TABLE $table ( $columnLocalPath TEXT NOT NULL, + $columnThumbnailPath TEXT NOT NULL, $columnUrl TEXT, $columnHash TEXT NOT NULL, $columnSyncTimestamp TEXT @@ -53,6 +55,7 @@ class DatabaseHelper { Database db = await instance.database; var row = new Map(); row[columnLocalPath] = photo.localPath; + row[columnThumbnailPath] = photo.thumbnailPath; row[columnUrl] = photo.url; row[columnHash] = photo.hash; row[columnSyncTimestamp] = photo.syncTimestamp; diff --git a/lib/models/photo.dart b/lib/models/photo.dart index a0ec197cb..ee0bb37e2 100644 --- a/lib/models/photo.dart +++ b/lib/models/photo.dart @@ -1,11 +1,13 @@ import 'dart:io'; import 'package:crypto/crypto.dart'; +import 'package:image/image.dart'; import 'package:photo_manager/photo_manager.dart'; class Photo { String url; String localPath; + String thumbnailPath; String hash; int syncTimestamp; @@ -18,6 +20,7 @@ class Photo { Photo.fromRow(Map row) : localPath = row["local_path"], + thumbnailPath = row["thumbnail_path"], url = row["url"], hash = row["hash"], syncTimestamp = row["sync_timestamp"] == null @@ -28,6 +31,7 @@ class Photo { Photo photo = Photo(); var file = (await asset.originFile); photo.localPath = file.path; + photo.thumbnailPath = getThumbnailPath(file.path); photo.hash = getHash(file); return photo; } @@ -35,4 +39,12 @@ class Photo { static String getHash(File file) { return sha256.convert(file.readAsBytesSync()).toString(); } + + static String getThumbnailPath(String path) { + Image image = decodeImage(File(path).readAsBytesSync()); + Image thumbnail = copyResize(image, width: 150); + String thumbnailPath = path + ".thumbnail"; + File(thumbnailPath)..writeAsBytesSync(encodePng(thumbnail)); + return thumbnailPath; + } } diff --git a/lib/photo_sync_manager.dart b/lib/photo_sync_manager.dart index 8dd84d17f..fdc88cf58 100644 --- a/lib/photo_sync_manager.dart +++ b/lib/photo_sync_manager.dart @@ -39,11 +39,17 @@ class PhotoSyncManager { if (lastDBUpdateTimestamp == null) { lastDBUpdateTimestamp = 0; } - for (AssetEntity asset in _assets) { - if (asset.createDateTime.millisecondsSinceEpoch > lastDBUpdateTimestamp) { - await insertPhotoToDB(await Photo.fromAsset(asset)); - } - } + // for (AssetEntity asset in _assets) { + // if (asset.createDateTime.millisecondsSinceEpoch > lastDBUpdateTimestamp) { + // try { + // var photo = await Photo.fromAsset(asset); + // await DatabaseHelper.instance.insertPhoto(photo); + // } catch (e) { + // _logger.e(e); + // } + // } + // } + PhotoLoader.instance.reloadPhotos(); return await prefs.setInt( _lastDBUpdateTimestampKey, DateTime.now().millisecondsSinceEpoch); } @@ -98,7 +104,9 @@ class PhotoSyncManager { .download(_endpoint + photo.url, localPath) .catchError(_onError); photo.localPath = localPath; - await insertPhotoToDB(photo); + photo.thumbnailPath = Photo.getThumbnailPath(localPath); + await DatabaseHelper.instance.insertPhoto(photo); + PhotoLoader.instance.reloadPhotos(); } await prefs.setInt(_lastSyncTimestampKey, photo.syncTimestamp); } @@ -131,12 +139,6 @@ class PhotoSyncManager { return photo; } - Future insertPhotoToDB(Photo photo) async { - _logger.i("Inserting to DB"); - await DatabaseHelper.instance.insertPhoto(photo); - PhotoLoader.instance.reloadPhotos(); - } - void _onError(error) { _logger.e(error); } diff --git a/lib/ui/gallery.dart b/lib/ui/gallery.dart index 9b49a264b..c05ca9b33 100644 --- a/lib/ui/gallery.dart +++ b/lib/ui/gallery.dart @@ -1,10 +1,12 @@ import 'dart:io'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:myapp/photo_loader.dart'; import 'package:myapp/ui/image_widget.dart'; import 'package:provider/provider.dart'; +import 'package:toast/toast.dart'; import 'change_notifier_builder.dart'; import 'detail_page.dart'; @@ -18,44 +20,67 @@ class Gallery extends StatefulWidget { class _GalleryState extends State { Logger _logger = Logger(); - + + int _crossAxisCount = 4; + PhotoLoader get photoLoader => Provider.of(context); - + @override Widget build(BuildContext context) { - _logger.i("Build"); - return ChangeNotifierBuilder( - value: photoLoader, - builder: (_, __) { - return GridView.builder( - itemBuilder: _buildItem, - itemCount: photoLoader.getPhotos().length, - gridDelegate: - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4), - ); - }, - ); - } - - Widget _buildItem(BuildContext context, int index) { - var file = File(photoLoader.getPhotos()[index].localPath); + _logger.i("Build with _crossAxisCount: " + _crossAxisCount.toString()); return GestureDetector( - onTap: () async { - routeToDetailPage(file, context); + onScaleUpdate: (ScaleUpdateDetails details) { + _logger.i("Scale update: " + details.horizontalScale.toString()); + setState(() { + if (details.horizontalScale < 1) { + _crossAxisCount = 8; + } else if (details.horizontalScale < 2) { + _crossAxisCount = 5; + } else if (details.horizontalScale < 4) { + _crossAxisCount = 4; + } else if (details.horizontalScale < 8) { + _crossAxisCount = 2; + } else { + _crossAxisCount = 1; + } + }); }, - child: Hero( - child: Padding( - padding: const EdgeInsets.all(1.0), - child: ImageWidget(path: file.path), - ), - tag: 'photo_' + file.path, + child: ChangeNotifierBuilder( + value: photoLoader, + builder: (_, __) { + return GridView.builder( + itemBuilder: _buildItem, + itemCount: photoLoader.getPhotos().length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: _crossAxisCount, + )); + }, ), ); } - void routeToDetailPage(File file, BuildContext context) async { + Widget _buildItem(BuildContext context, int index) { + var photo = photoLoader.getPhotos()[index]; + return GestureDetector( + onTap: () async { + routeToDetailPage(photo.localPath, context); + }, + onLongPress: () { + Toast.show(photo.thumbnailPath, context); + }, + child: Hero( + child: Padding( + padding: const EdgeInsets.all(1.0), + child: ImageWidget(path: photo.thumbnailPath), + ), + tag: 'photo_' + photo.localPath, + ), + ); + } + + void routeToDetailPage(String path, BuildContext context) async { final page = DetailPage( - file: file, + file: File(path), ); Navigator.of(context).push( MaterialPageRoute( diff --git a/lib/ui/image_widget.dart b/lib/ui/image_widget.dart index cd18ac999..4b3ff22a0 100644 --- a/lib/ui/image_widget.dart +++ b/lib/ui/image_widget.dart @@ -31,7 +31,6 @@ class _ImageWidgetState extends State { Widget image; if (cachedImage != null) { - _logger.i("Cache hit for " + path); image = cachedImage; } else { image = FutureBuilder( diff --git a/pubspec.lock b/pubspec.lock index 3356f6930..422497b3f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -94,7 +94,7 @@ packages: source: hosted version: "3.1.3" image: - dependency: transitive + dependency: "direct main" description: name: image url: "https://pub.dartlang.org" @@ -315,6 +315,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.11" + toast: + dependency: "direct main" + description: + name: toast + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index dd966ebcb..56407ce8e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,6 +32,8 @@ dependencies: dio: ^3.0.9 local_image_provider: ^1.0.0 crypto: ^2.1.3 + toast: ^0.1.5 + image: ^2.1.4 dev_dependencies: flutter_test: