Handle file deletions outside ente gracefully

This commit is contained in:
Vishnu Mohandas 2020-11-17 11:32:14 +05:30
parent 19208e8d2f
commit 9b84413a68
4 changed files with 93 additions and 44 deletions

View file

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:photos/core/cache/image_cache.dart';
import 'package:photos/core/cache/thumbnail_cache.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/models/file.dart';
import 'package:logging/logging.dart';
import 'package:photos/core/constants.dart';
import 'package:photos/models/file_type.dart';
import 'package:photos/repositories/file_repository.dart';
import 'package:photos/utils/file_util.dart';
class ThumbnailWidget extends StatefulWidget {
@ -26,14 +28,21 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
color: Colors.grey[800],
);
File _file;
bool _hasLoadedThumbnail = false;
bool _isLoadingThumbnail = false;
bool _encounteredErrorLoadingThumbnail = false;
ImageProvider _imageProvider;
@override
void initState() {
_file = widget.file;
super.initState();
}
@override
Widget build(BuildContext context) {
if (widget.file.localID == null) {
if (_file.localID == null) {
_loadNetworkImage();
} else {
_loadLocalImage(context);
@ -48,7 +57,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
var content;
if (image != null) {
if (widget.file.fileType == FileType.video) {
if (_file.fileType == FileType.video) {
content = Stack(
children: [
image,
@ -79,14 +88,21 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
!_isLoadingThumbnail) {
_isLoadingThumbnail = true;
final cachedSmallThumbnail =
ThumbnailLruCache.get(widget.file, THUMBNAIL_SMALL_SIZE);
ThumbnailLruCache.get(_file, THUMBNAIL_SMALL_SIZE);
if (cachedSmallThumbnail != null) {
_imageProvider = Image.memory(cachedSmallThumbnail).image;
_hasLoadedThumbnail = true;
} else {
widget.file.getAsset().then((asset) async {
if (asset == null) {
await deleteFiles([widget.file]);
_file.getAsset().then((asset) async {
if (asset == null || !(await asset.exists)) {
if (_file.uploadedFileID != null) {
_file.localID = null;
FilesDB.instance.update(_file);
_loadNetworkImage();
} else {
FilesDB.instance.deleteLocalFile(_file.localID);
FileRepository.instance.reloadFiles();
}
return;
}
asset
@ -107,7 +123,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
}
});
}
ThumbnailLruCache.put(widget.file, THUMBNAIL_SMALL_SIZE, data);
ThumbnailLruCache.put(_file, THUMBNAIL_SMALL_SIZE, data);
});
}).catchError((e) {
_logger.warning("Could not load image: ", e);
@ -122,13 +138,13 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
!_encounteredErrorLoadingThumbnail &&
!_isLoadingThumbnail) {
_isLoadingThumbnail = true;
final cachedThumbnail = ThumbnailFileLruCache.get(widget.file);
final cachedThumbnail = ThumbnailFileLruCache.get(_file);
if (cachedThumbnail != null) {
_imageProvider = Image.file(cachedThumbnail).image;
_hasLoadedThumbnail = true;
return;
}
getThumbnailFromServer(widget.file).then((file) {
getThumbnailFromServer(_file).then((file) {
final imageProvider = Image.file(file).image;
if (mounted) {
precacheImage(imageProvider, context).then((value) {
@ -139,7 +155,7 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
});
}
}).catchError((e) {
_logger.severe("Could not load image " + widget.file.toString());
_logger.severe("Could not load image " + _file.toString());
_encounteredErrorLoadingThumbnail = true;
});
}

View file

@ -38,38 +38,47 @@ class _VideoWidgetState extends State<VideoWidget> {
void initState() {
super.initState();
if (widget.file.localID == null) {
if (!widget.file.isEncrypted) {
_setVideoPlayerController(url: widget.file.getStreamUrl());
_videoPlayerController.addListener(() {
if (_videoPlayerController.value.hasError) {
_logger.warning(_videoPlayerController.value.errorDescription);
showToast(
"The video has not been processed yet. Downloading the original one...",
toastLength: Toast.LENGTH_SHORT);
_setVideoPlayerController(url: widget.file.getDownloadUrl());
}
});
} else {
getFileFromServer(
widget.file,
progressCallback: (count, total) {
setState(() {
_progress = count / total;
if (_progress == 1) {
showToast("Decrypting video...",
toastLength: Toast.LENGTH_SHORT);
}
});
},
).then((file) {
_setVideoPlayerController(file: file);
});
}
_loadNetworkVideo();
} else {
widget.file.getAsset().then((asset) {
asset.getMediaUrl().then((url) {
_setVideoPlayerController(url: url);
});
widget.file.getAsset().then((asset) async {
if (asset == null || !(await asset.exists)) {
if (widget.file.uploadedFileID != null) {
_loadNetworkVideo();
}
} else {
asset.getMediaUrl().then((url) {
_setVideoPlayerController(url: url);
});
}
});
}
}
void _loadNetworkVideo() {
if (!widget.file.isEncrypted) {
_setVideoPlayerController(url: widget.file.getStreamUrl());
_videoPlayerController.addListener(() {
if (_videoPlayerController.value.hasError) {
_logger.warning(_videoPlayerController.value.errorDescription);
showToast(
"The video has not been processed yet. Downloading the original one...",
toastLength: Toast.LENGTH_SHORT);
_setVideoPlayerController(url: widget.file.getDownloadUrl());
}
});
} else {
getFileFromServer(
widget.file,
progressCallback: (count, total) {
setState(() {
_progress = count / total;
if (_progress == 1) {
showToast("Decrypting video...", toastLength: Toast.LENGTH_SHORT);
}
});
},
).then((file) {
_setVideoPlayerController(file: file);
});
}
}

View file

@ -6,7 +6,9 @@ import 'package:logging/logging.dart';
import 'package:photos/core/cache/image_cache.dart';
import 'package:photos/core/cache/thumbnail_cache.dart';
import 'package:photos/core/cache/thumbnail_cache_manager.dart';
import 'package:photos/db/files_db.dart';
import 'package:photos/models/file.dart';
import 'package:photos/repositories/file_repository.dart';
import 'package:photos/ui/loading_widget.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photos/core/constants.dart';
@ -44,6 +46,7 @@ class _ZoomableImageState extends State<ZoomableImage>
@override
void initState() {
_photo = widget.photo;
_scaleStateChangedCallback = (value) {
if (widget.shouldDisableScroll != null) {
widget.shouldDisableScroll(value != PhotoViewScaleState.initial);
@ -54,7 +57,6 @@ class _ZoomableImageState extends State<ZoomableImage>
@override
Widget build(BuildContext context) {
_photo = widget.photo;
if (_photo.localID == null) {
_loadNetworkImage();
} else {
@ -136,9 +138,17 @@ class _ZoomableImageState extends State<ZoomableImage>
_onLargeThumbnailLoaded(Image.memory(cachedThumbnail).image, context);
} else {
_photo.getAsset().then((asset) {
if (asset == null) {
// Deleted file
return;
}
asset
.thumbDataWithSize(THUMBNAIL_LARGE_SIZE, THUMBNAIL_LARGE_SIZE)
.then((data) {
if (data == null) {
// Deleted file
return;
}
_onLargeThumbnailLoaded(Image.memory(data).image, context);
ThumbnailLruCache.put(_photo, THUMBNAIL_LARGE_SIZE, data);
});
@ -152,7 +162,19 @@ class _ZoomableImageState extends State<ZoomableImage>
if (cachedFile != null) {
_onFinalImageLoaded(Image.file(cachedFile).image, context);
} else {
_photo.getAsset().then((asset) {
_photo.getAsset().then((asset) async {
if (asset == null || !(await asset.exists)) {
_logger.info("File was deleted " + _photo.toString());
if (_photo.uploadedFileID != null) {
_photo.localID = null;
FilesDB.instance.update(_photo);
_loadNetworkImage();
} else {
FilesDB.instance.deleteLocalFile(_photo.localID);
FileRepository.instance.reloadFiles();
}
return;
}
asset.file.then((file) {
if (mounted) {
_onFinalImageLoaded(Image.file(file).image, context);

View file

@ -4,6 +4,8 @@ import 'package:logging/logging.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:photos/models/file.dart';
final _logger = Logger("FileSyncUtil");
Future<List<File>> getDeviceFiles(int fromTime, int toTime) async {
final pathEntities = await _getGalleryList(fromTime, toTime);
final files = List<File>();
@ -58,7 +60,7 @@ Future _addToPhotos(
files.add(file);
}
} catch (e) {
Logger("FileSyncUtil").severe(e);
_logger.severe(e);
}
}
}