#251 #119 clean obsolete Media Store entry when trying to move to bin entry backed by a file that no longer exists

This commit is contained in:
Thibault Deckers 2022-05-22 19:10:41 +09:00
parent 4f5664a80a
commit 13bac7937b
6 changed files with 46 additions and 30 deletions

View file

@ -1045,6 +1045,9 @@ abstract class ImageProvider {
// used when skipping a move/creation op because the target file already exists
val skippedFieldMap: HashMap<String, Any?> = hashMapOf("skipped" to true)
// used when deleting instead of moving to bin because the target file no longer exists
val deletedFieldMap: HashMap<String, Any?> = 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)

View file

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

View file

@ -28,29 +28,29 @@ class ImageOpEvent extends Equatable {
@immutable
class MoveOpEvent extends ImageOpEvent {
final Map newFields;
final bool deleted;
@override
List<Object?> get props => [success, skipped, uri, newFields];
List<Object?> 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<Object?> 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) {

View file

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

View file

@ -25,6 +25,7 @@ class FakeMediaFileService extends Fake implements MediaFileService {
'path': '${entry.directory}/$newName',
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
},
deleted: false,
));
}
}

View file

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