117 lines
4.1 KiB
Dart
117 lines
4.1 KiB
Dart
import 'dart:io';
|
|
|
|
import '../core/app_trash.dart';
|
|
import '../core/file_ops.dart' show FileOps;
|
|
import '../platform/windows/recycle_bin.dart';
|
|
|
|
/// 약속 (E5b): 지우기 · 되돌리기. 휴지통은 recycle_bin (Windows) · app_trash (Android) 를 부르기만 한다.
|
|
/// - [deletePaths]: 입력 = 경로들 · 영구인지 · 앱 휴지통 (있으면 그리로), 출력 = [DeleteReport] (실제로 어떻게 됐는지 하나씩).
|
|
/// 하나가 실패해도 나머지는 계속한다. 휴지통으로 못 보낸 것은 몰래 영구 삭제하지 않고 보고에 남긴다
|
|
/// (앱 휴지통 [DeleteReport.notTrashed] · 긴 경로 [DeleteReport.tooLong] - 영구 삭제는 부르는 쪽이 물은 뒤 [deleteForever]).
|
|
/// - [deleteForever]: 영구 삭제, 출력 = 못 지운 것 (경로, 예외).
|
|
/// - [undoTrash] · [undoRecycle]: 방금 휴지통으로 보낸 것을 원래 자리로, 출력 = 되살린 것 · 못 되살린 것 (이유).
|
|
/// - 다시 쓰는 곳: 탐색기 · 다운로드 화면 지우기 · 보관함 · 정리.
|
|
class DeleteReport {
|
|
DeleteReport(this.since);
|
|
|
|
/// 이 지우기를 시작한 때 (Windows 휴지통 되돌리기용 - 정보 파일 시각이 초 단위라 조금 앞)
|
|
final DateTime since;
|
|
|
|
/// Windows 휴지통으로 보낸 것 (되돌릴 수 있음)
|
|
final sent = <String>[];
|
|
|
|
/// 앱 휴지통으로 보낸 것 · 그 경로 (되돌릴 수 있음)
|
|
final trashed = <TrashItem>[];
|
|
final undoable = <String>[];
|
|
|
|
/// 앱 휴지통으로 옮기지 못한 것 (경로, 이유) - 다른 저장 장치 · 권한 등
|
|
final notTrashed = <(String, String)>[];
|
|
|
|
/// 경로가 너무 길어 휴지통이 받지 않은 것
|
|
final tooLong = <String>[];
|
|
|
|
/// 지우지 못한 것 (경로, 이유)
|
|
final failed = <(String, String)>[];
|
|
|
|
/// 휴지통으로 보낸 수 · Windows 가 묻고 영구 삭제한 수 · 취소한 수
|
|
var recycled = 0, nuked = 0, kept = 0;
|
|
}
|
|
|
|
/// 사람이 읽을 실패 이유 (OS 메시지가 있으면 그것)
|
|
String deleteFailReason(Object e) => e is FileSystemException ? (e.osError?.message ?? e.message) : '$e';
|
|
|
|
Future<DeleteReport> deletePaths(List<String> paths, {required bool permanent, AppTrash? trash, DateTime? now}) async {
|
|
final r = DeleteReport((now ?? DateTime.now()).subtract(const Duration(seconds: 2)));
|
|
for (final x in paths) {
|
|
if (!permanent && trash != null) {
|
|
try {
|
|
r.trashed.add(await trash.moveToTrash(x));
|
|
r.undoable.add(x);
|
|
r.recycled++;
|
|
} on TrashMoveException catch (e) {
|
|
r.notTrashed.add((x, '${e.cause}'));
|
|
}
|
|
continue;
|
|
}
|
|
try {
|
|
if (!permanent) {
|
|
switch (recycle(x)) {
|
|
case RecycleResult.recycled:
|
|
r.recycled++;
|
|
r.sent.add(x);
|
|
case RecycleResult.deletedPermanently:
|
|
r.nuked++;
|
|
case RecycleResult.cancelled:
|
|
r.kept++;
|
|
}
|
|
} else {
|
|
await FileOps().delete([x]);
|
|
}
|
|
} on LongPathException {
|
|
r.tooLong.add(x);
|
|
} catch (e) {
|
|
r.failed.add((x, deleteFailReason(e)));
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/// 영구 삭제 - 못 지운 것 (경로, 예외)
|
|
Future<List<(String, Object)>> deleteForever(Iterable<String> paths) async {
|
|
final failed = <(String, Object)>[];
|
|
for (final x in paths) {
|
|
try {
|
|
await FileOps().delete([x]);
|
|
} catch (e) {
|
|
failed.add((x, e));
|
|
}
|
|
}
|
|
return failed;
|
|
}
|
|
|
|
/// 앱 휴지통에서 되살림 - 되살린 경로 (못 되살린 것은 빠짐)
|
|
Future<List<String>> undoTrash(AppTrash trash, List<TrashItem> items) async {
|
|
final back = <String>[];
|
|
for (final i in items) {
|
|
try {
|
|
back.add(await trash.restore(i));
|
|
} catch (_) {}
|
|
}
|
|
return back;
|
|
}
|
|
|
|
/// Windows 휴지통에서 되살림 - 되살린 수 · 못 되살린 것 (경로, 이유)
|
|
({int back, List<(String, String)> failed}) undoRecycle(List<String> paths, DateTime since) {
|
|
final failed = <(String, String)>[];
|
|
var back = 0;
|
|
for (final x in paths) {
|
|
try {
|
|
restoreRecycled(x, since: since);
|
|
back++;
|
|
} catch (e) {
|
|
failed.add((x, e is FileSystemException ? e.message : '$e'));
|
|
}
|
|
}
|
|
return (back: back, failed: failed);
|
|
}
|