Vishnu Mohandas пре 5 година
родитељ
комит
f5225cda20

+ 26 - 7
lib/db/db_helper.dart

@@ -53,13 +53,22 @@ class DatabaseHelper {
 
   Future<int> insertPhoto(Photo photo) async {
     Database db = await instance.database;
-    var row = new Map<String, dynamic>();
-    row[columnLocalPath] = photo.localPath;
-    row[columnThumbnailPath] = photo.thumbnailPath;
-    row[columnUrl] = photo.url;
-    row[columnHash] = photo.hash;
-    row[columnSyncTimestamp] = photo.syncTimestamp;
-    return await db.insert(table, row);
+    return await db.insert(table, _getRowForPhoto(photo));
+  }
+
+  Future<List<dynamic>> insertPhotos(List<Photo> photos) async {
+    Database db = await instance.database;
+    var batch = db.batch();
+    int batchCounter = 0;
+    for (Photo photo in photos) {
+      if (batchCounter == 400) {
+        await batch.commit();
+        batch = db.batch();
+      }
+      batch.insert(table, _getRowForPhoto(photo));
+      batchCounter++;
+    }
+    return await batch.commit();
   }
 
   Future<List<Photo>> getAllPhotos() async {
@@ -108,4 +117,14 @@ class DatabaseHelper {
     }
     return photos;
   }
+
+  Map<String, dynamic> _getRowForPhoto(Photo photo) {
+    var row = new Map<String, dynamic>();
+    row[columnLocalPath] = photo.localPath;
+    row[columnThumbnailPath] = photo.thumbnailPath;
+    row[columnUrl] = photo.url;
+    row[columnHash] = photo.hash;
+    row[columnSyncTimestamp] = photo.syncTimestamp;
+    return row;
+  }
 }

+ 11 - 19
lib/main.dart

@@ -1,3 +1,4 @@
+import 'dart:async';
 
 import 'package:flutter/material.dart';
 import 'package:logger/logger.dart';
@@ -7,35 +8,26 @@ import 'package:myapp/photo_provider.dart';
 import 'package:myapp/photo_sync_manager.dart';
 import 'package:myapp/ui/gallery.dart';
 import 'package:myapp/ui/loading_widget.dart';
+import 'package:photo_manager/photo_manager.dart';
 import 'package:provider/provider.dart';
-import 'package:myapp/ui/gallery_page.dart';
 
 final provider = PhotoProvider();
 final logger = Logger();
 
 void main() async {
   WidgetsFlutterBinding.ensureInitialized();
-  await provider.refreshGalleryList();
-  var assets = await provider.list[0].assetList;
-  var photoSyncManager = PhotoSyncManager(assets);
-  photoSyncManager.init();
   runApp(MyApp2());
+  await provider.refreshGalleryList();
+
+  provider.list[0].assetList.then((assets) {
+    var photoSyncManager = PhotoSyncManager(assets);
+    photoSyncManager.init();
+  });
 }
 
-class MyApp extends StatelessWidget {
-  // This widget is the root of your application.
-  @override
-  Widget build(BuildContext context) {
-    logger.i("hello, world");
-    return ChangeNotifierProvider<PhotoProvider>.value(
-      value: provider,
-      child: MaterialApp(
-        title: 'Orma',
-        theme: ThemeData.dark(),
-        home: GalleryPage(path: provider.list[0]),
-      ),
-    );
-  }
+Future<void> init(List<AssetEntity> assets) async {
+  var photoSyncManager = PhotoSyncManager(assets);
+  photoSyncManager.init();
 }
 
 class MyApp2 extends StatelessWidget {

+ 17 - 8
lib/models/photo.dart

@@ -1,7 +1,9 @@
 import 'dart:io';
 
 import 'package:crypto/crypto.dart';
+import 'package:flutter/foundation.dart';
 import 'package:image/image.dart';
+import 'package:path_provider/path_provider.dart';
 import 'package:photo_manager/photo_manager.dart';
 
 class Photo {
@@ -31,20 +33,27 @@ class Photo {
     Photo photo = Photo();
     var file = (await asset.originFile);
     photo.localPath = file.path;
-    photo.thumbnailPath = getThumbnailPath(file.path);
     photo.hash = getHash(file);
+    var externalPath = (await getApplicationDocumentsDirectory()).path;
+    var thumbnailPath = externalPath + "/photos/thumbnails/" + photo.hash + ".thumbnail";
+    var args = Map<String, String>();
+    args["assetPath"] = file.path;
+    args["thumbnailPath"] = thumbnailPath;
+    photo.thumbnailPath = thumbnailPath;
+    await compute(getThumbnailPath, args);
     return 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;
-  }
+Future<void> getThumbnailPath(Map<String, String> args) async {
+  return File(args["thumbnailPath"])..writeAsBytes(_getThumbnail(args["assetPath"]));
+}
+
+List<int> _getThumbnail(String path) {
+  Image image = decodeImage(File(path).readAsBytesSync());
+  return encodePng(copyResize(image, width: 250));
 }

+ 1 - 2
lib/photo_provider.dart

@@ -88,7 +88,7 @@ class PhotoProvider extends ChangeNotifier {
     if (!result) {
       print("Did not get permission");
     }
-    var galleryList = await PhotoManager.getAssetPathList();
+    var galleryList = await PhotoManager.getAssetPathList(type: RequestType.image);
 
     galleryList.sort((s1, s2) {
       return s2.assetCount.compareTo(s1.assetCount);
@@ -96,7 +96,6 @@ class PhotoProvider extends ChangeNotifier {
 
     this.list.clear();
     this.list.addAll(galleryList);
-    print("Final List: " + list.toString());
   }
 
   PathProvider getOrCreatePathProvider(AssetPathEntity pathEntity) {

+ 28 - 16
lib/photo_sync_manager.dart

@@ -1,3 +1,6 @@
+import 'dart:async';
+import 'dart:io';
+
 import 'package:logger/logger.dart';
 import 'package:myapp/db/db_helper.dart';
 import 'package:myapp/photo_loader.dart';
@@ -25,12 +28,9 @@ class PhotoSyncManager {
   }
 
   Future<void> init() async {
-    await _updateDatabase();
-    try {
+    _updateDatabase().then((_) {
       _syncPhotos();
-    } catch (e) {
-      _logger.e(e);
-    }
+    });
   }
 
   Future<bool> _updateDatabase() async {
@@ -38,17 +38,28 @@ class PhotoSyncManager {
     var lastDBUpdateTimestamp = prefs.getInt(_lastDBUpdateTimestampKey);
     if (lastDBUpdateTimestamp == null) {
       lastDBUpdateTimestamp = 0;
+      var externalPath = (await getApplicationDocumentsDirectory()).path;
+      new Directory(externalPath + "/photos/thumbnails")
+          .createSync(recursive: true);
+    }
+    var photos = List<Photo>();
+    for (AssetEntity asset in _assets) {
+      if (asset.createDateTime.millisecondsSinceEpoch > lastDBUpdateTimestamp) {
+        try {
+          photos.add(await Photo.fromAsset(asset));
+        } catch (e) {
+          _logger.e((await asset.originFile).path, e);
+        }
+        if (photos.length > 10) {
+          await DatabaseHelper.instance.insertPhotos(photos);
+          photos.clear();
+          PhotoLoader.instance.reloadPhotos();
+          _logger.i("Inserted " + photos.length.toString() + " photos.");
+          await prefs.setInt(_lastDBUpdateTimestampKey, asset.createDateTime.millisecondsSinceEpoch);
+        }
+      }
     }
-    // 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);
-    //     }
-    //   }
-    // }
+    await DatabaseHelper.instance.insertPhotos(photos);
     PhotoLoader.instance.reloadPhotos();
     return await prefs.setInt(
         _lastDBUpdateTimestampKey, DateTime.now().millisecondsSinceEpoch);
@@ -104,7 +115,8 @@ class PhotoSyncManager {
             .download(_endpoint + photo.url, localPath)
             .catchError(_onError);
         photo.localPath = localPath;
-        photo.thumbnailPath = Photo.getThumbnailPath(localPath);
+        // TODO: Fix me
+        photo.thumbnailPath = localPath;
         await DatabaseHelper.instance.insertPhoto(photo);
         PhotoLoader.instance.reloadPhotos();
       }

+ 21 - 11
lib/ui/detail_page.dart

@@ -2,11 +2,13 @@ import 'dart:io';
 
 import 'package:flutter/material.dart';
 import 'package:myapp/core/lru_map.dart';
+import 'package:myapp/models/photo.dart';
+import 'package:share_extend/share_extend.dart';
 
 class DetailPage extends StatefulWidget {
-  final File file;
+  final Photo photo;
 
-  const DetailPage({Key key, this.file}) : super(key: key);
+  const DetailPage({Key key, this.photo}) : super(key: key);
 
   @override
   _DetailPageState createState() => _DetailPageState();
@@ -16,6 +18,17 @@ class _DetailPageState extends State<DetailPage> {
   @override
   Widget build(BuildContext context) {
     return Scaffold(
+      appBar: AppBar(
+        actions: <Widget>[
+          // action button
+          IconButton(
+            icon: Icon(Icons.share),
+            onPressed: () {
+              ShareExtend.share(widget.photo.localPath, "image");
+            },
+          )
+        ],
+      ),
       body: Center(
         child: Container(
           child: _buildContent(context),
@@ -29,15 +42,12 @@ class _DetailPageState extends State<DetailPage> {
       onVerticalDragUpdate: (details) {
         Navigator.pop(context);
       },
-      child: Hero(
-        tag: 'photo_' + widget.file.path,
-        child: ImageLruCache.getData(widget.file.path) == null
-            ? Image.file(
-                widget.file,
-                filterQuality: FilterQuality.low,
-              )
-            : ImageLruCache.getData(widget.file.path),
-      ),
+      child: ImageLruCache.getData(widget.photo.localPath) == null
+          ? Image.file(
+              File(widget.photo.localPath),
+              filterQuality: FilterQuality.low,
+            )
+          : ImageLruCache.getData(widget.photo.localPath),
     );
   }
 }

+ 8 - 11
lib/ui/gallery.dart

@@ -1,8 +1,8 @@
 import 'dart:io';
-import 'dart:math';
 
 import 'package:flutter/material.dart';
 import 'package:logger/logger.dart';
+import 'package:myapp/models/photo.dart';
 import 'package:myapp/photo_loader.dart';
 import 'package:myapp/ui/image_widget.dart';
 import 'package:provider/provider.dart';
@@ -63,24 +63,21 @@ class _GalleryState extends State<Gallery> {
     var photo = photoLoader.getPhotos()[index];
     return GestureDetector(
       onTap: () async {
-        routeToDetailPage(photo.localPath, context);
+        routeToDetailPage(photo, context);
       },
       onLongPress: () {
-        Toast.show(photo.thumbnailPath, context);
+        Toast.show(photo.localPath, context);
       },
-      child: Hero(
-        child: Padding(
-          padding: const EdgeInsets.all(1.0),
-          child: ImageWidget(path: photo.thumbnailPath),
-        ),
-        tag: 'photo_' + photo.localPath,
+      child: Padding(
+        padding: const EdgeInsets.all(1.0),
+        child: ImageWidget(path: photo.thumbnailPath),
       ),
     );
   }
 
-  void routeToDetailPage(String path, BuildContext context) async {
+  void routeToDetailPage(Photo photo, BuildContext context) async {
     final page = DetailPage(
-      file: File(path),
+      photo: photo,
     );
     Navigator.of(context).push(
       MaterialPageRoute(

+ 0 - 117
lib/ui/gallery_page.dart

@@ -1,117 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:myapp/photo_provider.dart';
-import 'package:myapp/ui/image_widget.dart';
-import 'package:photo_manager/photo_manager.dart';
-import 'package:myapp/ui/change_notifier_builder.dart';
-import 'package:myapp/ui/loading_widget.dart';
-import 'package:myapp/ui/detail_page.dart';
-import 'package:provider/provider.dart';
-
-class GalleryPage extends StatefulWidget {
-  final AssetPathEntity path;
-
-  const GalleryPage({Key key, this.path}) : super(key: key);
-
-  @override
-  _GalleryPageState createState() => _GalleryPageState();
-}
-
-class _GalleryPageState extends State<GalleryPage> {
-  AssetPathEntity get path => widget.path;
-
-  PathProvider get provider =>
-      Provider.of<PhotoProvider>(context).getOrCreatePathProvider(path);
-
-  @override
-  void initState() {
-    super.initState();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return ChangeNotifierBuilder(
-      value: provider,
-      builder: (_, __) {
-        var length = path.assetCount;
-        return Scaffold(
-          appBar: AppBar(
-            title: Text("Orma"),
-          ),
-          body: buildRefreshIndicator(length),
-        );
-      },
-    );
-  }
-
-  Widget buildRefreshIndicator(int length) {
-    if (!provider.isInit) {
-      provider.onRefresh();
-      return Center(
-        child: Text("loading"),
-      );
-    }
-    return RefreshIndicator(
-      onRefresh: _onRefresh,
-      child: Scrollbar(
-        child: GridView.builder(
-          itemBuilder: _buildItem,
-          itemCount: provider.showItemCount,
-          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
-            crossAxisCount: 4,
-          ),
-        ),
-      ),
-    );
-  }
-
-  Widget _buildItem(BuildContext context, int index) {
-    final list = provider.list;
-    if (list.length == index) {
-      onLoadMore();
-      return loadWidget;
-    }
-
-    if (index > list.length) {
-      return Container();
-    }
-
-    final entity = list[index];
-    return GestureDetector(
-      onTap: () async {
-        routeToDetailPage(entity);
-      },
-      child: ImageWidget(
-        key: ValueKey(entity),
-        path: "",
-      ),
-    );
-  }
-
-  void routeToDetailPage(AssetEntity entity) async {
-    final originFile = await entity.originFile;
-    final page = DetailPage(
-      file: originFile,
-    );
-    Navigator.of(context).push(
-      MaterialPageRoute(
-        builder: (BuildContext context) {
-          return page;
-        },
-      ),
-    );
-  }
-
-  Future<void> onLoadMore() async {
-    if (!mounted) {
-      return;
-    }
-    await provider.onLoadMore();
-  }
-
-  Future<void> _onRefresh() async {
-    if (!mounted) {
-      return;
-    }
-    await provider.onRefresh();
-  }
-}

+ 8 - 8
pubspec.lock

@@ -170,13 +170,6 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.1"
-  pedantic:
-    dependency: transitive
-    description:
-      name: pedantic
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0+1"
   petitparser:
     dependency: transitive
     description:
@@ -219,6 +212,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.5"
+  share_extend:
+    dependency: "direct main"
+    description:
+      name: share_extend
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.1.2"
   shared_preferences:
     dependency: "direct main"
     description:
@@ -314,7 +314,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.11"
+    version: "0.2.15"
   toast:
     dependency: "direct main"
     description:

+ 1 - 0
pubspec.yaml

@@ -34,6 +34,7 @@ dependencies:
   crypto: ^2.1.3
   toast: ^0.1.5
   image: ^2.1.4
+  share_extend: "^1.1.2"
 
 dev_dependencies:
   flutter_test:

+ 0 - 30
test/widget_test.dart

@@ -1,30 +0,0 @@
-// This is a basic Flutter widget test.
-//
-// To perform an interaction with a widget in your test, use the WidgetTester
-// utility that Flutter provides. For example, you can send tap and scroll
-// gestures. You can also use WidgetTester to find child widgets in the widget
-// tree, read text, and verify that the values of widget properties are correct.
-
-import 'package:flutter/material.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import 'package:myapp/main.dart';
-
-void main() {
-  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
-    // Build our app and trigger a frame.
-    await tester.pumpWidget(MyApp());
-
-    // Verify that our counter starts at 0.
-    expect(find.text('0'), findsOneWidget);
-    expect(find.text('1'), findsNothing);
-
-    // Tap the '+' icon and trigger a frame.
-    await tester.tap(find.byIcon(Icons.add));
-    await tester.pump();
-
-    // Verify that our counter has incremented.
-    expect(find.text('0'), findsNothing);
-    expect(find.text('1'), findsOneWidget);
-  });
-}