Add some buttons

This commit is contained in:
Vishnu Mohandas 2020-03-30 19:58:46 +05:30
parent 81882999c1
commit f5225cda20
11 changed files with 122 additions and 230 deletions

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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));
}

View file

@ -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) {

View file

@ -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);
}
// 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);
// }
// }
// }
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);
}
}
}
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();
}

View file

@ -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),
);
}
}

View file

@ -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(

View file

@ -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();
}
}

View file

@ -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:

View file

@ -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:

View file

@ -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);
});
}