diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt index aa7801064..12ce8e4a5 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt @@ -1045,6 +1045,9 @@ abstract class ImageProvider { // used when skipping a move/creation op because the target file already exists val skippedFieldMap: HashMap = hashMapOf("skipped" to true) + // used when deleting instead of moving to bin because the target file no longer exists + val deletedFieldMap: HashMap = hashMapOf("deleted" to true) + fun isMediaUriPermissionGranted(context: Context, uri: Uri, mimeType: String): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val safeUri = StorageUtils.getMediaStoreScopedStorageSafeUri(uri, mimeType) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt index f6eeb0242..c8f38a340 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt @@ -443,18 +443,23 @@ class MediaStoreImageProvider : ImageProvider() { if (effectiveTargetDir != null) { val newFields = if (isCancelledOp()) skippedFieldMap else { val sourceFile = File(sourcePath) - moveSingle( - activity = activity, - sourceFile = sourceFile, - sourceUri = sourceUri, - targetDir = effectiveTargetDir, - targetDirDocFile = targetDirDocFile, - desiredName = desiredName ?: sourceFile.name, - nameConflictStrategy = nameConflictStrategy, - mimeType = mimeType, - copy = copy, - toBin = toBin, - ) + if (!sourceFile.exists() && toBin) { + delete(activity, sourceUri, sourcePath, mimeType = mimeType) + deletedFieldMap + } else { + moveSingle( + activity = activity, + sourceFile = sourceFile, + sourceUri = sourceUri, + targetDir = effectiveTargetDir, + targetDirDocFile = targetDirDocFile, + desiredName = desiredName ?: sourceFile.name, + nameConflictStrategy = nameConflictStrategy, + mimeType = mimeType, + copy = copy, + toBin = toBin, + ) + } } result["newFields"] = newFields result["success"] = true diff --git a/lib/services/common/image_op_events.dart b/lib/services/common/image_op_events.dart index 13863ad3f..60f055a1c 100644 --- a/lib/services/common/image_op_events.dart +++ b/lib/services/common/image_op_events.dart @@ -28,29 +28,29 @@ class ImageOpEvent extends Equatable { @immutable class MoveOpEvent extends ImageOpEvent { final Map newFields; + final bool deleted; @override - List get props => [success, skipped, uri, newFields]; + List get props => [success, skipped, uri, newFields, deleted]; const MoveOpEvent({ - required bool success, - required bool skipped, - required String uri, + required super.success, + required super.skipped, + required super.uri, required this.newFields, - }) : super( - success: success, - skipped: skipped, - uri: uri, - ); + required this.deleted, + }); factory MoveOpEvent.fromMap(Map map) { final newFields = map['newFields'] ?? {}; final skipped = (map['skipped'] ?? false) || (newFields['skipped'] ?? false); + final deleted = (map['deleted'] ?? false) || (newFields['deleted'] ?? false); return MoveOpEvent( success: (map['success'] ?? false) || skipped, skipped: skipped, uri: map['uri'], newFields: newFields, + deleted: deleted, ); } } @@ -63,16 +63,13 @@ class ExportOpEvent extends MoveOpEvent { List get props => [success, skipped, uri, pageId, newFields]; const ExportOpEvent({ - required bool success, - required bool skipped, - required String uri, + required super.success, + required super.skipped, + required super.uri, this.pageId, - required Map newFields, + required super.newFields, }) : super( - success: success, - skipped: skipped, - uri: uri, - newFields: newFields, + deleted: false, ); factory ExportOpEvent.fromMap(Map map) { diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index c88f375d9..07f08265f 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -132,7 +132,9 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { onCancel: () => mediaFileService.cancelFileOp(opId), onDone: (processed) async { final successOps = processed.where((v) => v.success).toSet(); - final movedOps = successOps.where((v) => !v.skipped).toSet(); + + // move + final movedOps = successOps.where((v) => !v.skipped && !v.deleted).toSet(); final movedEntries = movedOps.map((v) => v.uri).map((uri) => entries.firstWhereOrNull((entry) => entry.uri == uri)).whereNotNull().toSet(); await source.updateAfterMove( todoEntries: entries, @@ -140,6 +142,12 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { destinationAlbums: destinationAlbums, movedOps: movedOps, ); + + // delete (when trying to move to bin obsolete entries) + final deletedOps = successOps.where((v) => v.deleted).toSet(); + final deletedUris = deletedOps.map((event) => event.uri).toSet(); + await source.removeEntries(deletedUris, includeTrash: true); + source.resumeMonitoring(); // cleanup diff --git a/test/fake/media_file_service.dart b/test/fake/media_file_service.dart index 71223b179..6ae81b5af 100644 --- a/test/fake/media_file_service.dart +++ b/test/fake/media_file_service.dart @@ -25,6 +25,7 @@ class FakeMediaFileService extends Fake implements MediaFileService { 'path': '${entry.directory}/$newName', 'dateModifiedSecs': FakeMediaStoreService.dateSecs, }, + deleted: false, )); } } diff --git a/test/fake/media_store_service.dart b/test/fake/media_store_service.dart index 813137f95..e6172611e 100644 --- a/test/fake/media_store_service.dart +++ b/test/fake/media_store_service.dart @@ -57,6 +57,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService { 'path': entry.path!.replaceFirst(sourceAlbum, destinationAlbum), 'dateModifiedSecs': FakeMediaStoreService.dateSecs, }, + deleted: false, ); } @@ -73,6 +74,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService { 'path': entry.path!.replaceFirst(oldName, newName), 'dateModifiedSecs': FakeMediaStoreService.dateSecs, }, + deleted: false, ); } }