#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:
parent
4f5664a80a
commit
13bac7937b
6 changed files with 46 additions and 30 deletions
|
@ -1045,6 +1045,9 @@ abstract class ImageProvider {
|
||||||
// used when skipping a move/creation op because the target file already exists
|
// used when skipping a move/creation op because the target file already exists
|
||||||
val skippedFieldMap: HashMap<String, Any?> = hashMapOf("skipped" to true)
|
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 {
|
fun isMediaUriPermissionGranted(context: Context, uri: Uri, mimeType: String): Boolean {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val safeUri = StorageUtils.getMediaStoreScopedStorageSafeUri(uri, mimeType)
|
val safeUri = StorageUtils.getMediaStoreScopedStorageSafeUri(uri, mimeType)
|
||||||
|
|
|
@ -443,6 +443,10 @@ class MediaStoreImageProvider : ImageProvider() {
|
||||||
if (effectiveTargetDir != null) {
|
if (effectiveTargetDir != null) {
|
||||||
val newFields = if (isCancelledOp()) skippedFieldMap else {
|
val newFields = if (isCancelledOp()) skippedFieldMap else {
|
||||||
val sourceFile = File(sourcePath)
|
val sourceFile = File(sourcePath)
|
||||||
|
if (!sourceFile.exists() && toBin) {
|
||||||
|
delete(activity, sourceUri, sourcePath, mimeType = mimeType)
|
||||||
|
deletedFieldMap
|
||||||
|
} else {
|
||||||
moveSingle(
|
moveSingle(
|
||||||
activity = activity,
|
activity = activity,
|
||||||
sourceFile = sourceFile,
|
sourceFile = sourceFile,
|
||||||
|
@ -456,6 +460,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
||||||
toBin = toBin,
|
toBin = toBin,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result["newFields"] = newFields
|
result["newFields"] = newFields
|
||||||
result["success"] = true
|
result["success"] = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,29 +28,29 @@ class ImageOpEvent extends Equatable {
|
||||||
@immutable
|
@immutable
|
||||||
class MoveOpEvent extends ImageOpEvent {
|
class MoveOpEvent extends ImageOpEvent {
|
||||||
final Map newFields;
|
final Map newFields;
|
||||||
|
final bool deleted;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [success, skipped, uri, newFields];
|
List<Object?> get props => [success, skipped, uri, newFields, deleted];
|
||||||
|
|
||||||
const MoveOpEvent({
|
const MoveOpEvent({
|
||||||
required bool success,
|
required super.success,
|
||||||
required bool skipped,
|
required super.skipped,
|
||||||
required String uri,
|
required super.uri,
|
||||||
required this.newFields,
|
required this.newFields,
|
||||||
}) : super(
|
required this.deleted,
|
||||||
success: success,
|
});
|
||||||
skipped: skipped,
|
|
||||||
uri: uri,
|
|
||||||
);
|
|
||||||
|
|
||||||
factory MoveOpEvent.fromMap(Map map) {
|
factory MoveOpEvent.fromMap(Map map) {
|
||||||
final newFields = map['newFields'] ?? {};
|
final newFields = map['newFields'] ?? {};
|
||||||
final skipped = (map['skipped'] ?? false) || (newFields['skipped'] ?? false);
|
final skipped = (map['skipped'] ?? false) || (newFields['skipped'] ?? false);
|
||||||
|
final deleted = (map['deleted'] ?? false) || (newFields['deleted'] ?? false);
|
||||||
return MoveOpEvent(
|
return MoveOpEvent(
|
||||||
success: (map['success'] ?? false) || skipped,
|
success: (map['success'] ?? false) || skipped,
|
||||||
skipped: skipped,
|
skipped: skipped,
|
||||||
uri: map['uri'],
|
uri: map['uri'],
|
||||||
newFields: newFields,
|
newFields: newFields,
|
||||||
|
deleted: deleted,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,16 +63,13 @@ class ExportOpEvent extends MoveOpEvent {
|
||||||
List<Object?> get props => [success, skipped, uri, pageId, newFields];
|
List<Object?> get props => [success, skipped, uri, pageId, newFields];
|
||||||
|
|
||||||
const ExportOpEvent({
|
const ExportOpEvent({
|
||||||
required bool success,
|
required super.success,
|
||||||
required bool skipped,
|
required super.skipped,
|
||||||
required String uri,
|
required super.uri,
|
||||||
this.pageId,
|
this.pageId,
|
||||||
required Map newFields,
|
required super.newFields,
|
||||||
}) : super(
|
}) : super(
|
||||||
success: success,
|
deleted: false,
|
||||||
skipped: skipped,
|
|
||||||
uri: uri,
|
|
||||||
newFields: newFields,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
factory ExportOpEvent.fromMap(Map map) {
|
factory ExportOpEvent.fromMap(Map map) {
|
||||||
|
|
|
@ -132,7 +132,9 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
||||||
onCancel: () => mediaFileService.cancelFileOp(opId),
|
onCancel: () => mediaFileService.cancelFileOp(opId),
|
||||||
onDone: (processed) async {
|
onDone: (processed) async {
|
||||||
final successOps = processed.where((v) => v.success).toSet();
|
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();
|
final movedEntries = movedOps.map((v) => v.uri).map((uri) => entries.firstWhereOrNull((entry) => entry.uri == uri)).whereNotNull().toSet();
|
||||||
await source.updateAfterMove(
|
await source.updateAfterMove(
|
||||||
todoEntries: entries,
|
todoEntries: entries,
|
||||||
|
@ -140,6 +142,12 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin {
|
||||||
destinationAlbums: destinationAlbums,
|
destinationAlbums: destinationAlbums,
|
||||||
movedOps: movedOps,
|
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();
|
source.resumeMonitoring();
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
|
|
|
@ -25,6 +25,7 @@ class FakeMediaFileService extends Fake implements MediaFileService {
|
||||||
'path': '${entry.directory}/$newName',
|
'path': '${entry.directory}/$newName',
|
||||||
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
|
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
|
||||||
},
|
},
|
||||||
|
deleted: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService {
|
||||||
'path': entry.path!.replaceFirst(sourceAlbum, destinationAlbum),
|
'path': entry.path!.replaceFirst(sourceAlbum, destinationAlbum),
|
||||||
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
|
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
|
||||||
},
|
},
|
||||||
|
deleted: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +74,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService {
|
||||||
'path': entry.path!.replaceFirst(oldName, newName),
|
'path': entry.path!.replaceFirst(oldName, newName),
|
||||||
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
|
'dateModifiedSecs': FakeMediaStoreService.dateSecs,
|
||||||
},
|
},
|
||||||
|
deleted: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue