Browse Source

Handle file deletions outside ente gracefully

Vishnu Mohandas 4 years ago
parent
commit
9b84413a68
4 changed files with 92 additions and 43 deletions
  1. 26 10
      lib/ui/thumbnail_widget.dart
  2. 39 30
      lib/ui/video_widget.dart
  3. 24 2
      lib/ui/zoomable_image.dart
  4. 3 1
      lib/utils/file_sync_util.dart

+ 26 - 10
lib/ui/thumbnail_widget.dart

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

+ 39 - 30
lib/ui/video_widget.dart

@@ -38,38 +38,47 @@ class _VideoWidgetState extends State<VideoWidget> {
   void initState() {
   void initState() {
     super.initState();
     super.initState();
     if (widget.file.localID == null) {
     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());
+      _loadNetworkVideo();
+    } else {
+      widget.file.getAsset().then((asset) async {
+        if (asset == null || !(await asset.exists)) {
+          if (widget.file.uploadedFileID != null) {
+            _loadNetworkVideo();
           }
           }
-        });
-      } 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);
-        });
-      }
+        } 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 {
     } else {
-      widget.file.getAsset().then((asset) {
-        asset.getMediaUrl().then((url) {
-          _setVideoPlayerController(url: url);
-        });
+      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);
       });
       });
     }
     }
   }
   }

+ 24 - 2
lib/ui/zoomable_image.dart

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

+ 3 - 1
lib/utils/file_sync_util.dart

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