Fix issues on iOS
This commit is contained in:
parent
20f4f6324b
commit
4a892e2ce3
13 changed files with 181 additions and 179 deletions
|
@ -14,11 +14,9 @@ class DatabaseHelper {
|
|||
static final columnGeneratedId = '_id';
|
||||
static final columnUploadedFileId = 'uploaded_file_id';
|
||||
static final columnLocalId = 'local_id';
|
||||
static final columnLocalPath = 'local_path';
|
||||
static final columnRelativePath = 'relative_path';
|
||||
static final columnThumbnailPath = 'thumbnail_path';
|
||||
static final columnPath = 'path';
|
||||
static final columnHash = 'hash';
|
||||
static final columnTitle = 'title';
|
||||
static final columnPathName = 'path_name';
|
||||
static final columnRemotePath = 'remote_path';
|
||||
static final columnIsDeleted = 'is_deleted';
|
||||
static final columnCreateTimestamp = 'create_timestamp';
|
||||
static final columnSyncTimestamp = 'sync_timestamp';
|
||||
|
@ -51,11 +49,9 @@ class DatabaseHelper {
|
|||
$columnGeneratedId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
$columnLocalId TEXT,
|
||||
$columnUploadedFileId INTEGER NOT NULL,
|
||||
$columnLocalPath TEXT NOT NULL,
|
||||
$columnRelativePath TEXT NOT NULL,
|
||||
$columnThumbnailPath TEXT NOT NULL,
|
||||
$columnPath TEXT,
|
||||
$columnHash TEXT NOT NULL,
|
||||
$columnTitle TEXT NOT NULL,
|
||||
$columnPathName TEXT NOT NULL,
|
||||
$columnRemotePath TEXT,
|
||||
$columnIsDeleted INTEGER DEFAULT 0,
|
||||
$columnCreateTimestamp TEXT NOT NULL,
|
||||
$columnSyncTimestamp TEXT
|
||||
|
@ -103,16 +99,20 @@ class DatabaseHelper {
|
|||
return _convertToPhotos(results);
|
||||
}
|
||||
|
||||
Future<int> updatePhoto(Photo photo) async {
|
||||
Future<int> updatePhoto(
|
||||
int generatedId, String remotePath, int syncTimestamp) async {
|
||||
Database db = await instance.database;
|
||||
return await db.update(table, _getRowForPhoto(photo),
|
||||
where: '$columnGeneratedId = ?', whereArgs: [photo.generatedId]);
|
||||
var values = new Map<String, dynamic>();
|
||||
values[columnRemotePath] = remotePath;
|
||||
values[columnSyncTimestamp] = syncTimestamp;
|
||||
return await db.update(table, values,
|
||||
where: '$columnGeneratedId = ?', whereArgs: [generatedId]);
|
||||
}
|
||||
|
||||
Future<Photo> getPhotoByPath(String path) async {
|
||||
Database db = await instance.database;
|
||||
var rows =
|
||||
await db.query(table, where: '$columnPath =?', whereArgs: [path]);
|
||||
await db.query(table, where: '$columnRemotePath =?', whereArgs: [path]);
|
||||
if (rows.length > 0) {
|
||||
return _getPhotofromRow(rows[0]);
|
||||
} else {
|
||||
|
@ -147,11 +147,9 @@ class DatabaseHelper {
|
|||
row[columnLocalId] = photo.localId;
|
||||
row[columnUploadedFileId] =
|
||||
photo.uploadedFileId == null ? -1 : photo.uploadedFileId;
|
||||
row[columnLocalPath] = photo.localPath;
|
||||
row[columnRelativePath] = photo.relativePath;
|
||||
row[columnThumbnailPath] = photo.thumbnailPath;
|
||||
row[columnPath] = photo.path;
|
||||
row[columnHash] = photo.hash;
|
||||
row[columnTitle] = photo.title;
|
||||
row[columnPathName] = photo.pathName;
|
||||
row[columnRemotePath] = photo.remotePath;
|
||||
row[columnCreateTimestamp] = photo.createTimestamp;
|
||||
row[columnSyncTimestamp] = photo.syncTimestamp;
|
||||
return row;
|
||||
|
@ -162,11 +160,9 @@ class DatabaseHelper {
|
|||
photo.generatedId = row[columnGeneratedId];
|
||||
photo.localId = row[columnLocalId];
|
||||
photo.uploadedFileId = row[columnUploadedFileId];
|
||||
photo.localPath = row[columnLocalPath];
|
||||
photo.relativePath = row[columnRelativePath];
|
||||
photo.thumbnailPath = row[columnThumbnailPath];
|
||||
photo.path = row[columnPath];
|
||||
photo.hash = row[columnHash];
|
||||
photo.title = row[columnTitle];
|
||||
photo.pathName = row[columnPathName];
|
||||
photo.remotePath = row[columnRemotePath];
|
||||
photo.createTimestamp = int.parse(row[columnCreateTimestamp]);
|
||||
photo.syncTimestamp = row[columnSyncTimestamp] == null
|
||||
? -1
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/photo_loader.dart';
|
||||
import 'package:myapp/photo_provider.dart';
|
||||
import 'package:myapp/photo_sync_manager.dart';
|
||||
import 'package:myapp/ui/home_widget.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
final provider = PhotoProvider();
|
||||
|
@ -15,20 +12,7 @@ final logger = Logger();
|
|||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
runApp(MyApp());
|
||||
await provider.refreshGalleryList();
|
||||
|
||||
if (provider.list.length > 0) {
|
||||
provider.list[0].assetList.then((assets) {
|
||||
init(assets);
|
||||
});
|
||||
} else {
|
||||
init(List<AssetEntity>());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> init(List<AssetEntity> assets) async {
|
||||
var photoSyncManager = PhotoSyncManager(assets);
|
||||
photoSyncManager.init();
|
||||
provider.refreshGalleryList().then((_) => PhotoSyncManager(provider.list));
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
|
|
|
@ -1,63 +1,81 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
class Photo {
|
||||
int generatedId;
|
||||
int uploadedFileId;
|
||||
String localId;
|
||||
String path;
|
||||
String localPath;
|
||||
String relativePath;
|
||||
String thumbnailPath;
|
||||
String hash;
|
||||
String title;
|
||||
String pathName;
|
||||
String remotePath;
|
||||
int createTimestamp;
|
||||
int syncTimestamp;
|
||||
|
||||
Photo();
|
||||
|
||||
Photo.fromJson(Map<String, dynamic> json)
|
||||
: uploadedFileId = json["fileId"],
|
||||
path = json["path"],
|
||||
hash = json["hash"],
|
||||
thumbnailPath = json["thumbnailPath"],
|
||||
remotePath = json["path"],
|
||||
createTimestamp = json["createTimestamp"],
|
||||
syncTimestamp = json["syncTimestamp"];
|
||||
|
||||
static Future<Photo> fromAsset(AssetEntity asset) async {
|
||||
static Future<Photo> fromAsset(
|
||||
AssetPathEntity pathEntity, AssetEntity asset) async {
|
||||
Photo photo = Photo();
|
||||
photo.uploadedFileId = -1;
|
||||
photo.localId = asset.id;
|
||||
var file = await asset.originFile;
|
||||
photo.localPath = file.path;
|
||||
if (Platform.isAndroid) {
|
||||
photo.relativePath = dirname((asset.relativePath.endsWith("/")
|
||||
? asset.relativePath
|
||||
: asset.relativePath + "/") +
|
||||
asset.title);
|
||||
} else {
|
||||
photo.relativePath = dirname(photo.localPath);
|
||||
}
|
||||
photo.hash = getHash(file);
|
||||
photo.thumbnailPath = photo.localPath;
|
||||
photo.title = asset.title;
|
||||
photo.pathName = pathEntity.name;
|
||||
photo.createTimestamp = asset.createDateTime.microsecondsSinceEpoch;
|
||||
return photo;
|
||||
}
|
||||
|
||||
AssetEntity getAsset() {
|
||||
return AssetEntity(id: localId);
|
||||
Future<Uint8List> getBytes() {
|
||||
final asset = AssetEntity(id: localId);
|
||||
if (extension(title) == ".HEIC") {
|
||||
return asset.originBytes.then((bytes) =>
|
||||
FlutterImageCompress.compressWithList(bytes)
|
||||
.then((result) => Uint8List.fromList(result)));
|
||||
} else {
|
||||
return asset.originBytes;
|
||||
}
|
||||
}
|
||||
|
||||
static String getHash(File file) {
|
||||
return sha256.convert(file.readAsBytesSync()).toString();
|
||||
Future<Uint8List> getOriginalBytes() {
|
||||
return AssetEntity(id: localId).originBytes;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Photo(generatedId: $generatedId, uploadedFileId: $uploadedFileId, localId: $localId, path: $path, localPath: $localPath, relativePath: $relativePath, thumbnailPath: $thumbnailPath, hash: $hash, createTimestamp: $createTimestamp, syncTimestamp: $syncTimestamp)';
|
||||
return 'Photo(generatedId: $generatedId, uploadedFileId: $uploadedFileId, localId: $localId, title: $title, pathName: $pathName, remotePath: $remotePath, createTimestamp: $createTimestamp, syncTimestamp: $syncTimestamp)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
if (identical(this, o)) return true;
|
||||
|
||||
return o is Photo &&
|
||||
o.generatedId == generatedId &&
|
||||
o.uploadedFileId == uploadedFileId &&
|
||||
o.localId == localId &&
|
||||
o.title == title &&
|
||||
o.pathName == pathName &&
|
||||
o.remotePath == remotePath &&
|
||||
o.createTimestamp == createTimestamp &&
|
||||
o.syncTimestamp == syncTimestamp;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return generatedId.hashCode ^
|
||||
uploadedFileId.hashCode ^
|
||||
localId.hashCode ^
|
||||
title.hashCode ^
|
||||
pathName.hashCode ^
|
||||
remotePath.hashCode ^
|
||||
createTimestamp.hashCode ^
|
||||
syncTimestamp.hashCode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,8 +88,10 @@ class PhotoProvider extends ChangeNotifier {
|
|||
if (!result) {
|
||||
print("Did not get permission");
|
||||
}
|
||||
var galleryList =
|
||||
await PhotoManager.getAssetPathList(type: RequestType.image);
|
||||
final filterOptionGroup = FilterOptionGroup();
|
||||
filterOptionGroup.setOption(AssetType.image, FilterOption(needTitle: true));
|
||||
var galleryList = await PhotoManager.getAssetPathList(
|
||||
type: RequestType.image, filterOption: filterOptionGroup);
|
||||
|
||||
galleryList.sort((s1, s2) {
|
||||
return s2.assetCount.compareTo(s1.assetCount);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/db/db_helper.dart';
|
||||
|
@ -16,51 +15,58 @@ import 'package:myapp/core/constants.dart' as Constants;
|
|||
class PhotoSyncManager {
|
||||
final _logger = Logger();
|
||||
final _dio = Dio();
|
||||
final List<AssetEntity> _assets;
|
||||
static final _lastSyncTimestampKey = "last_sync_timestamp_0";
|
||||
static final _lastDBUpdateTimestampKey = "last_db_update_timestamp";
|
||||
|
||||
PhotoSyncManager(this._assets) {
|
||||
_logger.i("PhotoSyncManager init");
|
||||
_assets.sort((first, second) => first.createDateTime.microsecondsSinceEpoch
|
||||
.compareTo(second.createDateTime.microsecondsSinceEpoch));
|
||||
PhotoSyncManager(List<AssetPathEntity> pathEntities) {
|
||||
init(pathEntities);
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
_updateDatabase().then((_) {
|
||||
_syncPhotos().then((_) {
|
||||
_deletePhotos();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> _updateDatabase() async {
|
||||
Future<void> init(List<AssetPathEntity> pathEntities) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
var lastDBUpdateTimestamp = prefs.getInt(_lastDBUpdateTimestampKey);
|
||||
if (lastDBUpdateTimestamp == null) {
|
||||
lastDBUpdateTimestamp = 0;
|
||||
await _initializeDirectories();
|
||||
}
|
||||
var photos = List<Photo>();
|
||||
var bufferLimit = 10;
|
||||
final maxBufferLimit = 1000;
|
||||
for (AssetEntity asset in _assets) {
|
||||
if (asset.createDateTime.microsecondsSinceEpoch > lastDBUpdateTimestamp) {
|
||||
try {
|
||||
photos.add(await Photo.fromAsset(asset));
|
||||
} catch (e) {
|
||||
_logger.e(e);
|
||||
}
|
||||
if (photos.length > bufferLimit) {
|
||||
await _insertPhotosToDB(
|
||||
photos, prefs, asset.createDateTime.microsecondsSinceEpoch);
|
||||
photos.clear();
|
||||
bufferLimit = min(maxBufferLimit, bufferLimit * 2);
|
||||
|
||||
final photos = List<Photo>();
|
||||
for (AssetPathEntity pathEntity in pathEntities) {
|
||||
if (Platform.isIOS || pathEntity.name != "Recent") {
|
||||
// "Recents" contain duplicate information on Android
|
||||
var assetList = await pathEntity.assetList;
|
||||
for (AssetEntity entity in assetList) {
|
||||
if (entity.createDateTime.microsecondsSinceEpoch >
|
||||
lastDBUpdateTimestamp) {
|
||||
try {
|
||||
photos.add(await Photo.fromAsset(pathEntity, entity));
|
||||
} catch (e) {
|
||||
_logger.e(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
photos.sort((first, second) =>
|
||||
first.createTimestamp.compareTo(second.createTimestamp));
|
||||
|
||||
_updateDatabase(photos, prefs, lastDBUpdateTimestamp).then((_) {
|
||||
_syncPhotos().then((_) {
|
||||
_deletePhotos();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> _updateDatabase(final List<Photo> photos,
|
||||
SharedPreferences prefs, int lastDBUpdateTimestamp) async {
|
||||
var photosToBeAdded = List<Photo>();
|
||||
for (Photo photo in photos) {
|
||||
if (photo.createTimestamp > lastDBUpdateTimestamp) {
|
||||
photosToBeAdded.add(photo);
|
||||
}
|
||||
}
|
||||
return await _insertPhotosToDB(
|
||||
photos, prefs, DateTime.now().microsecondsSinceEpoch);
|
||||
photosToBeAdded, prefs, DateTime.now().microsecondsSinceEpoch);
|
||||
}
|
||||
|
||||
_syncPhotos() async {
|
||||
|
@ -89,7 +95,8 @@ class PhotoSyncManager {
|
|||
if (uploadedPhoto == null) {
|
||||
return;
|
||||
}
|
||||
await DatabaseHelper.instance.updatePhoto(uploadedPhoto);
|
||||
await DatabaseHelper.instance.updatePhoto(photo.generatedId,
|
||||
uploadedPhoto.remotePath, uploadedPhoto.syncTimestamp);
|
||||
prefs.setInt(_lastSyncTimestampKey, uploadedPhoto.syncTimestamp);
|
||||
}
|
||||
}
|
||||
|
@ -98,12 +105,12 @@ class PhotoSyncManager {
|
|||
var externalPath = (await getApplicationDocumentsDirectory()).path;
|
||||
var path = externalPath + "/photos/";
|
||||
for (Photo photo in diff) {
|
||||
var localPath = path + basename(photo.path);
|
||||
var localPath = path + basename(photo.remotePath);
|
||||
await _dio
|
||||
.download(Constants.ENDPOINT + "/" + photo.path, localPath)
|
||||
.download(Constants.ENDPOINT + "/" + photo.remotePath, localPath)
|
||||
.catchError(_onError);
|
||||
photo.localPath = localPath;
|
||||
photo.thumbnailPath = localPath;
|
||||
// TODO: Save path
|
||||
photo.pathName = localPath;
|
||||
await DatabaseHelper.instance.insertPhoto(photo);
|
||||
PhotoLoader.instance.reloadPhotos();
|
||||
await prefs.setInt(_lastSyncTimestampKey, photo.syncTimestamp);
|
||||
|
@ -128,8 +135,8 @@ class PhotoSyncManager {
|
|||
|
||||
Future<Photo> _uploadFile(Photo localPhoto) async {
|
||||
var formData = FormData.fromMap({
|
||||
"file": await MultipartFile.fromFile(localPhoto.localPath,
|
||||
filename: basename(localPhoto.localPath)),
|
||||
"file": MultipartFile.fromBytes(await localPhoto.getOriginalBytes()),
|
||||
"filename": localPhoto.title,
|
||||
"user": Constants.USER,
|
||||
});
|
||||
return _dio
|
||||
|
@ -137,8 +144,6 @@ class PhotoSyncManager {
|
|||
.then((response) {
|
||||
_logger.i(response.toString());
|
||||
var photo = Photo.fromJson(response.data);
|
||||
photo.localPath = localPhoto.localPath;
|
||||
photo.localId = localPhoto.localId;
|
||||
return photo;
|
||||
}).catchError(_onError);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ class _AlbumListWidgetState extends State<AlbumListWidget> {
|
|||
List<Album> _getAlbums(List<Photo> photos) {
|
||||
final albumMap = new LinkedHashMap<String, List<Photo>>();
|
||||
for (Photo photo in photos) {
|
||||
final folder = path.basename(photo.relativePath);
|
||||
final folder = path.basename(photo.pathName);
|
||||
if (!albumMap.containsKey(folder)) {
|
||||
albumMap[folder] = new List<Photo>();
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ class _AlbumListWidgetState extends State<AlbumListWidget> {
|
|||
return GestureDetector(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
ImageWidget(album.photos[0], size: 160),
|
||||
ImageWidget(album.photos[0], size: 140),
|
||||
Padding(padding: EdgeInsets.all(2)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
|
|
@ -4,13 +4,10 @@ import 'package:flutter/material.dart';
|
|||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/core/lru_map.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:share_extend/share_extend.dart';
|
||||
import 'extents_page_view.dart';
|
||||
import 'loading_widget.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:myapp/utils/share_util.dart';
|
||||
|
||||
class DetailPage extends StatefulWidget {
|
||||
final List<Photo> photos;
|
||||
|
@ -27,11 +24,6 @@ class _DetailPageState extends State<DetailPage> {
|
|||
int _selectedIndex = 0;
|
||||
final _cachedImages = LRUMap<int, ZoomableImage>(5);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_selectedIndex = widget.selectedIndex;
|
||||
|
@ -77,8 +69,8 @@ class _DetailPageState extends State<DetailPage> {
|
|||
actions: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
onPressed: () {
|
||||
ShareExtend.share(widget.photos[_selectedIndex].localPath, "image");
|
||||
onPressed: () async {
|
||||
share(widget.photos[_selectedIndex]);
|
||||
},
|
||||
)
|
||||
],
|
||||
|
@ -99,26 +91,17 @@ class ZoomableImage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Logger().i("Building " + photo.generatedId.toString());
|
||||
Logger().i("Building " + photo.toString());
|
||||
if (ImageLruCache.getData(photo.generatedId) != null) {
|
||||
return _buildPhotoView(ImageLruCache.getData(photo.generatedId));
|
||||
}
|
||||
var future;
|
||||
if (path.extension(photo.localPath) == '.HEIC') {
|
||||
Logger().i("Decoding HEIC");
|
||||
future = photo.getAsset().originBytes.then((bytes) =>
|
||||
FlutterImageCompress.compressWithList(bytes)
|
||||
.then((result) => Uint8List.fromList(result)));
|
||||
} else {
|
||||
future = AssetEntity(id: photo.localId).originBytes;
|
||||
}
|
||||
return FutureBuilder<Uint8List>(
|
||||
future: future,
|
||||
future: photo.getBytes(),
|
||||
builder: (_, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return _buildPhotoView(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error);
|
||||
return Text(snapshot.error.toString());
|
||||
} else {
|
||||
return loadWidget;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:myapp/db/db_helper.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:myapp/photo_loader.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:share_extend/share_extend.dart';
|
||||
import 'package:myapp/utils/share_util.dart';
|
||||
|
||||
class GalleryAppBarWidget extends StatefulWidget
|
||||
implements PreferredSizeWidget {
|
||||
|
@ -65,11 +64,7 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
}
|
||||
|
||||
void _shareSelectedPhotos(BuildContext context) {
|
||||
var photoPaths = List<String>();
|
||||
for (Photo photo in widget.selectedPhotos) {
|
||||
photoPaths.add(photo.localPath);
|
||||
}
|
||||
ShareExtend.shareMultiple(photoPaths, "image");
|
||||
shareMultiple(widget.selectedPhotos.toList());
|
||||
}
|
||||
|
||||
void _showDeletePhotosSheet(BuildContext context) {
|
||||
|
@ -102,14 +97,14 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
|
|||
|
||||
Future _deleteSelectedPhotos(
|
||||
BuildContext context, bool deleteEverywhere) async {
|
||||
await PhotoManager.editor
|
||||
.deleteWithIds(widget.selectedPhotos.map((p) => p.localId).toList());
|
||||
|
||||
for (Photo photo in widget.selectedPhotos) {
|
||||
deleteEverywhere
|
||||
? await DatabaseHelper.instance.markPhotoForDeletion(photo)
|
||||
: await DatabaseHelper.instance.deletePhoto(photo);
|
||||
File file = File(photo.localPath);
|
||||
await file.delete();
|
||||
}
|
||||
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
photoLoader.reloadPhotos();
|
||||
if (widget.onPhotosDeleted != null) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -33,7 +32,7 @@ class _ImageWidgetState extends State<ImageWidget> {
|
|||
Widget image;
|
||||
|
||||
if (cachedImageData != null) {
|
||||
image = Image.memory(cachedImageData);
|
||||
image = _buildImage(cachedImageData, size);
|
||||
} else {
|
||||
if (widget.photo.localId != null) {
|
||||
image = FutureBuilder<Uint8List>(
|
||||
|
@ -43,10 +42,7 @@ class _ImageWidgetState extends State<ImageWidget> {
|
|||
if (snapshot.hasData) {
|
||||
ImageLruCache.setData(
|
||||
widget.photo.generatedId, size, snapshot.data);
|
||||
Image image = Image.memory(snapshot.data,
|
||||
width: size.toDouble(),
|
||||
height: size.toDouble(),
|
||||
fit: BoxFit.cover);
|
||||
Image image = _buildImage(snapshot.data, size);
|
||||
return image;
|
||||
} else {
|
||||
return loadingWidget;
|
||||
|
@ -54,19 +50,16 @@ class _ImageWidgetState extends State<ImageWidget> {
|
|||
},
|
||||
);
|
||||
} else {
|
||||
image = Image.file(File(widget.photo.localPath),
|
||||
width: size.toDouble(), height: size.toDouble(), fit: BoxFit.cover);
|
||||
// TODO
|
||||
return Text("Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(ImageWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.photo.generatedId != oldWidget.photo.generatedId) {
|
||||
setState(() {});
|
||||
}
|
||||
Image _buildImage(Uint8List data, int size) {
|
||||
return Image.memory(data,
|
||||
width: size.toDouble(), height: size.toDouble(), fit: BoxFit.cover);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:myapp/utils/gallery_items_filter.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
@ -5,11 +7,14 @@ import 'package:path/path.dart';
|
|||
class ImportantItemsFilter implements GalleryItemsFilter {
|
||||
@override
|
||||
bool shouldInclude(Photo photo) {
|
||||
// TODO: Improve logic
|
||||
final String folder = basename(photo.relativePath);
|
||||
return folder == "Camera" ||
|
||||
folder == "DCIM" ||
|
||||
folder == "Download" ||
|
||||
folder == "Screenshot";
|
||||
if (Platform.isAndroid) {
|
||||
final String folder = basename(photo.pathName);
|
||||
return folder == "Camera" ||
|
||||
folder == "DCIM" ||
|
||||
folder == "Download" ||
|
||||
folder == "Screenshot";
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
21
lib/utils/share_util.dart
Normal file
21
lib/utils/share_util.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:path/path.dart';
|
||||
|
||||
Future<void> share(Photo photo) async {
|
||||
final bytes = await photo.getBytes();
|
||||
final ext = extension(photo.title);
|
||||
final shareExt =
|
||||
ext == ".HEIC" ? "jpeg" : ext.substring(1, ext.length).toLowerCase();
|
||||
return Share.file(photo.title, photo.title, bytes, "image/" + shareExt);
|
||||
}
|
||||
|
||||
Future<void> shareMultiple(List<Photo> photos) async {
|
||||
final shareContent = Map<String, Uint8List>();
|
||||
for (Photo photo in photos) {
|
||||
shareContent[photo.title] = await photo.getBytes();
|
||||
}
|
||||
return Share.files("images", shareContent, "*/*");
|
||||
}
|
14
pubspec.lock
14
pubspec.lock
|
@ -78,6 +78,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
esys_flutter_share:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: esys_flutter_share
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -233,13 +240,6 @@ 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.3"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -34,7 +34,7 @@ dependencies:
|
|||
crypto: ^2.1.3
|
||||
toast: ^0.1.5
|
||||
image: ^2.1.4
|
||||
share_extend: "^1.1.2"
|
||||
esys_flutter_share: ^1.0.2
|
||||
draggable_scrollbar: ^0.0.4
|
||||
photo_view: ^0.9.2
|
||||
flutter_image_compress: ^0.6.5+1
|
||||
|
|
Loading…
Add table
Reference in a new issue