import 'package:flutter/widgets.dart'; import '../../app/archive_browse.dart' show ArchiveRead; import '../../core/file_ops.dart' show samePath; import '../../core/shizuku.dart' show isRootPath, isShizuku; import '../../l10n/tr.dart'; import '../archive_ui.dart' show extractArchiveFlow; import 'explorer_controller.dart'; /// 약속 (E5d-5): 읽기 전용인 곳 (펼친 압축 안 · 시스템 폴더 Shizuku · 루트) 을 바꾸려는 일을 막고 까닭을 알린다 (Host). /// 막았으면 true. 압축 안의 것은 꺼내기만 ([extractOne] · [extractFrom] - 86 의 풀기 창 그대로) class ExplorerGuard { ExplorerGuard(this.ctl); final ExplorerController ctl; bool insideArchive(String path) => ctl.arc.inside(path); /// 압축 안을 바꾸려 하면 막고 알린다 (읽기 전용). [self] 면 압축 파일 자신도 (그 안으로 넣는 곳일 때) bool blockArchive(List paths, {bool self = false}) { // X-plore 비교 5-2: 시스템 폴더 (Shizuku) 도 읽기 전용 - 쓰기 · 지우기 · 이름 바꾸기 · 옮기기 · 새 폴더 · 넣기를 막는다 (꺼내기만) if (paths.any(isShizuku)) { ctl.host.snack(paths.any(isRootPath) ? tr('루트 폴더는 읽기 전용입니다 (꺼내기 (복사) 만 할 수 있습니다)') : tr('시스템 폴더 (Shizuku) 는 읽기 전용입니다 (꺼내기 (복사) 만 할 수 있습니다)')); return true; } if (!paths.any((x) => insideArchive(x) || (self && ctl.arc.contains(x)))) return false; ctl.host.snack(tr('압축 안은 읽기 전용입니다 (꺼내기만 할 수 있습니다)')); return true; } /// 관리자 지시 (10/21, 28 지적): 시스템 폴더 · 루트 (shz://) 가 원본이나 받는 곳인 이동은 시작하지 않는다 - /// 복사는 되고 원본 지우기에서 오류로 끝나던 것 (끌어 놓기 · 붙여넣기 · 버튼 · 메뉴 모두 여기를 지남) bool blockShizukuMove(List sources, String? dest, {required bool move}) { if (!move || !(sources.any(isShizuku) || (dest != null && isShizuku(dest)))) return false; ctl.host.snack(tr('읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요')); return true; } /// 압축 안의 파일 하나를 임시 폴더로 꺼낸다 (보기 · 재생). 꺼낸 경로 (못 하면 null - 까닭을 알림) Future extractOne(String path) async { final (out, why) = await ctl.arc.extractOne(path, await ctl.c.services.storage.tempDirectory()); if (why == ArchiveRead.password) ctl.host.snack(tr('비밀번호가 걸린 파일입니다 - [압축 풀기] 로 푸세요')); if (why == ArchiveRead.failed) ctl.host.snack(trf('압축에서 꺼내지 못했습니다: {0}', [ctl.arc.lastError])); return out; } /// 압축 안의 것을 꺼내기 - 86 의 풀기 창 (검사 · 갈 곳 · 같은 이름) 그대로. 꺼낸 폴더를 다시 읽는다 Future extractFrom(BuildContext context, List paths, String? otherDir) async { final arc = ctl.arc.archiveOf(paths.firstWhere(insideArchive))!; final names = [for (final x in paths) if (insideArchive(x) && samePath(ctl.arc.archiveOf(x)!, arc)) ctl.arc.innerName(arc, x)]; final done = await extractArchiveFlow(context, ctl.c, arc, otherDir: otherDir, names: names); if (done != null && ctl.host.alive) await ctl.refreshAll([done]); } }