Delete local files

This commit is contained in:
Ming Ming 2022-05-06 02:34:30 +08:00
parent d33e3af806
commit c38ace893d
5 changed files with 123 additions and 1 deletions

View file

@ -86,7 +86,7 @@ class LocalUriFile with EquatableMixin implements LocalFile {
final DateTime? dateTaken;
}
typedef LocalFileOnDeleteFailureListener = void Function(
typedef LocalFileOnFailureListener = void Function(
LocalFile file, Object? error, StackTrace? stackTrace);
class LocalFileRepo {
@ -95,10 +95,23 @@ class LocalFileRepo {
/// See [LocalFileDataSource.listDir]
Future<List<LocalFile>> listDir(String path) => dataSrc.listDir(path);
/// See [LocalFileDataSource.deleteFiles]
Future<void> deleteFiles(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
}) =>
dataSrc.deleteFiles(files, onFailure: onFailure);
final LocalFileDataSource dataSrc;
}
abstract class LocalFileDataSource {
/// List all files under [path]
Future<List<LocalFile>> listDir(String path);
/// Delete files
Future<void> deleteFiles(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
});
}

View file

@ -1,7 +1,12 @@
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/entity/file_util.dart' as file_util;
import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/iterable_extension.dart';
import 'package:nc_photos/mobile/android/android_info.dart';
import 'package:nc_photos/mobile/android/k.dart' as android;
import 'package:nc_photos/object_extension.dart';
import 'package:nc_photos/stream_extension.dart';
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
class LocalFileMediaStoreDataSource implements LocalFileDataSource {
@ -17,6 +22,63 @@ class LocalFileMediaStoreDataSource implements LocalFileDataSource {
.toList();
}
@override
deleteFiles(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
}) async {
_log.info("[deleteFiles] ${files.map((f) => f.logTag).toReadableString()}");
final uriFiles = files
.where((f) {
if (f is! LocalUriFile) {
_log.warning(
"[deleteFiles] Can't remove file not returned by this data source: $f");
onFailure?.call(f, ArgumentError("File not supported"), null);
return false;
} else {
return true;
}
})
.cast<LocalUriFile>()
.toList();
if (AndroidInfo().sdkInt >= AndroidVersion.R) {
await _deleteFiles30(uriFiles, onFailure);
} else {
await _deleteFiles0(uriFiles, onFailure);
}
}
Future<void> _deleteFiles30(
List<LocalUriFile> files, LocalFileOnFailureListener? onFailure) async {
assert(AndroidInfo().sdkInt >= AndroidVersion.R);
int? resultCode;
final resultFuture = MediaStore.stream
.whereType<MediaStoreDeleteRequestResultEvent>()
.first
.then((ev) => resultCode = ev.resultCode);
await MediaStore.deleteFiles(files.map((f) => f.uri).toList());
await resultFuture;
if (resultCode != android.resultOk) {
_log.warning("[_deleteFiles30] result != OK: $resultCode");
for (final f in files) {
onFailure?.call(f, null, null);
}
}
}
Future<void> _deleteFiles0(
List<LocalUriFile> files, LocalFileOnFailureListener? onFailure) async {
assert(AndroidInfo().sdkInt < AndroidVersion.R);
final failedUris =
await MediaStore.deleteFiles(files.map((f) => f.uri).toList());
final failedFilesIt = failedUris!
.map((uri) => files.firstWhereOrNull((f) => f.uri == uri))
.whereNotNull();
for (final f in failedFilesIt) {
onFailure?.call(f, null, null);
}
}
static LocalFile _toLocalFile(MediaStoreQueryResult r) => LocalUriFile(
uri: r.uri,
displayName: r.displayName,

View file

@ -6,6 +6,7 @@ import 'package:logging/logging.dart';
import 'package:nc_photos/account.dart';
import 'package:nc_photos/entity/album.dart';
import 'package:nc_photos/entity/file.dart';
import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/entity/share.dart';
import 'package:nc_photos/pref.dart';
@ -148,6 +149,12 @@ class PrefUpdatedEvent {
final dynamic value;
}
class LocalFileDeletedEvent {
const LocalFileDeletedEvent(this.files);
final List<LocalFile> files;
}
extension FilePropertyUpdatedEventExtension on FilePropertyUpdatedEvent {
bool hasAnyProperties(List<int> properties) =>
properties.any((p) => this.properties & p != 0);

View file

@ -0,0 +1,8 @@
/// Standard activity result: operation canceled.
const resultCanceled = 0;
/// Standard activity result: operation succeeded.
const resultOk = -1;
/// Start of user-defined activity results.
const resultFirstUser = 1;

View file

@ -0,0 +1,32 @@
import 'package:event_bus/event_bus.dart';
import 'package:kiwi/kiwi.dart';
import 'package:logging/logging.dart';
import 'package:nc_photos/di_container.dart';
import 'package:nc_photos/entity/local_file.dart';
import 'package:nc_photos/event/event.dart';
class DeleteLocal {
DeleteLocal(this._c) : assert(require(_c));
static bool require(DiContainer c) =>
DiContainer.has(c, DiType.localFileRepo);
Future<void> call(
List<LocalFile> files, {
LocalFileOnFailureListener? onFailure,
}) async {
final deleted = List.of(files);
await _c.localFileRepo.deleteFiles(files, onFailure: (f, e, stackTrace) {
deleted.removeWhere((d) => d.compareIdentity(f));
onFailure?.call(f, e, stackTrace);
});
if (deleted.isNotEmpty) {
_log.info("[call] Deleted ${deleted.length} files successfully");
KiwiContainer().resolve<EventBus>().fire(LocalFileDeletedEvent(deleted));
}
}
final DiContainer _c;
static final _log = Logger("use_case.delete_local.DeleteLocal");
}