rc2: 시스템 폴더 · 루트 (shz://) 가 원본이나 받는 곳인 이동은 시작 전에 막음 (28 지적, 관리자 지시 10/21)

- 예전: 복사는 되고 원본 지우기에서 오류로 끝났다 (손실 없음)
- 지금: "읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요" 를 알리고 시작하지 않음. 이동 버튼 (_transfer) · 이동으로 담기 (_toClipboard) ·
  붙여넣기 · 끌어 놓기 · 메뉴가 모두 지나는 _runTransfer 에서 원본 · 받는 곳 둘 다 봄
- 시험: shizuku_test 의 탐색기 시험에 [이동] → 안내 · 담지 않음 · 복사 안 함. shizuku · root 11개 통과

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rc2
RyuJongJin 2026-10-21 19:09:40 +09:00
parent 06a6b1e8aa
commit d4266a7236
5 changed files with 22 additions and 0 deletions

View File

@ -2392,6 +2392,7 @@
"읽기 전용 · 보기와 받기만 됩니다": "Read-only · view and download only", "읽기 전용 · 보기와 받기만 됩니다": "Read-only · view and download only",
"읽기 전용 공유입니다 (보기 · 받기만)": "This share is read-only (view and download only)", "읽기 전용 공유입니다 (보기 · 받기만)": "This share is read-only (view and download only)",
"읽기 전용 매체입니다": "Read-only media", "읽기 전용 매체입니다": "Read-only media",
"읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요": "It is read-only, so it cannot be moved - use Copy",
"읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "Read-only: files can be viewed and downloaded, but not uploaded, deleted or renamed. Anything outside this folder and links cannot be opened.", "읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "Read-only: files can be viewed and downloaded, but not uploaded, deleted or renamed. Anything outside this folder and links cannot be opened.",
"읽는 중 · 폴더 {0}개 · {1}": "Reading · {0} folders · {1}", "읽는 중 · 폴더 {0}개 · {1}": "Reading · {0} folders · {1}",
"읽을 수 없습니다": "Cannot read", "읽을 수 없습니다": "Cannot read",

View File

@ -2392,6 +2392,7 @@
"읽기 전용 · 보기와 받기만 됩니다": "読み取り専用 · 表示とダウンロードのみ", "읽기 전용 · 보기와 받기만 됩니다": "読み取り専用 · 表示とダウンロードのみ",
"읽기 전용 공유입니다 (보기 · 받기만)": "読み取り専用の共有です (表示 · ダウンロードのみ)", "읽기 전용 공유입니다 (보기 · 받기만)": "読み取り専用の共有です (表示 · ダウンロードのみ)",
"읽기 전용 매체입니다": "読み取り専用メディアです", "읽기 전용 매체입니다": "読み取り専用メディアです",
"읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요": "読み取り専用のため移動できません - コピーを使ってください",
"읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "読み取り専用です: 表示とダウンロードのみで、アップロード · 削除 · 名前の変更はできません。このフォルダーの外とリンクは開けません。", "읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "読み取り専用です: 表示とダウンロードのみで、アップロード · 削除 · 名前の変更はできません。このフォルダーの外とリンクは開けません。",
"읽는 중 · 폴더 {0}개 · {1}": "読み込み中 · フォルダー {0} 件 · {1}", "읽는 중 · 폴더 {0}개 · {1}": "読み込み中 · フォルダー {0} 件 · {1}",
"읽을 수 없습니다": "読み込めません", "읽을 수 없습니다": "読み込めません",

View File

@ -2392,6 +2392,7 @@
"읽기 전용 · 보기와 받기만 됩니다": "只读 · 仅可查看和下载", "읽기 전용 · 보기와 받기만 됩니다": "只读 · 仅可查看和下载",
"읽기 전용 공유입니다 (보기 · 받기만)": "这是只读共享 (仅可查看和下载)", "읽기 전용 공유입니다 (보기 · 받기만)": "这是只读共享 (仅可查看和下载)",
"읽기 전용 매체입니다": "只读介质", "읽기 전용 매체입니다": "只读介质",
"읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요": "此处为只读,无法移动 - 请使用复制",
"읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "只读:只能查看和下载,不能上传、删除或重命名。无法打开此文件夹之外的内容和链接。", "읽기 전용입니다: 보기와 받기만 되고, 올리기 · 지우기 · 이름 바꾸기는 되지 않습니다. 이 폴더 밖과 링크는 열리지 않습니다.": "只读:只能查看和下载,不能上传、删除或重命名。无法打开此文件夹之外的内容和链接。",
"읽는 중 · 폴더 {0}개 · {1}": "正在读取 · {0} 个文件夹 · {1}", "읽는 중 · 폴더 {0}개 · {1}": "正在读取 · {0} 个文件夹 · {1}",
"읽을 수 없습니다": "无法读取", "읽을 수 없습니다": "无法读取",

View File

@ -2596,6 +2596,14 @@ class _ExplorerPageState extends State<ExplorerPage> {
if (done != null && mounted) await _refreshAll([done]); if (done != null && mounted) await _refreshAll([done]);
} }
/// 관리자 지시 (10/21, 28 지적): 시스템 폴더 · 루트 (shz://) 가 원본이나 받는 곳인 이동은 시작하지 않는다 -
/// 복사는 되고 원본 지우기에서 오류로 끝나던 것 (끌어 놓기 · 붙여넣기 · 버튼 · 메뉴 모두 여기를 지남). 막았으면 true
bool _blockShizukuMove(List<String> sources, String? dest, {required bool move}) {
if (!move || !(sources.any(isShizuku) || (dest != null && isShizuku(dest)))) return false;
_snack(tr('읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요'));
return true;
}
/// 압축 안을 바꾸려 하면 막고 알린다 (읽기 전용). 막았으면 true /// 압축 안을 바꾸려 하면 막고 알린다 (읽기 전용). 막았으면 true
/// [self] 면 압축 파일 자신도 (그 안으로 넣는 곳일 때) /// [self] 면 압축 파일 자신도 (그 안으로 넣는 곳일 때)
bool _blockArchive(List<String> paths, {bool self = false}) { bool _blockArchive(List<String> paths, {bool self = false}) {
@ -3266,6 +3274,7 @@ class _ExplorerPageState extends State<ExplorerPage> {
/// 다른 창의 지금 폴더로 복사 · 이동 (진행 창 · 취소) /// 다른 창의 지금 폴더로 복사 · 이동 (진행 창 · 취소)
/// 복사 · 이동 버튼: 두 창이면 다른 창의 지금 폴더로, 그 밖의 배치는 담아 두기 (붙여넣기로) /// 복사 · 이동 버튼: 두 창이면 다른 창의 지금 폴더로, 그 밖의 배치는 담아 두기 (붙여넣기로)
Future<void> _transfer(_Pane pane, List<String> sources, {required bool move}) async { Future<void> _transfer(_Pane pane, List<String> sources, {required bool move}) async {
if (_blockShizukuMove(sources, null, move: move)) return;
// X-plore 비교 f: 압축 안의 것 - 복사는 꺼내기 (86 의 풀기 검사 그대로), 이동은 막음 // X-plore 비교 f: 압축 안의 것 - 복사는 꺼내기 (86 의 풀기 검사 그대로), 이동은 막음
if (sources.any(_insideArchive)) { if (sources.any(_insideArchive)) {
if (move) { if (move) {
@ -3316,6 +3325,7 @@ class _ExplorerPageState extends State<ExplorerPage> {
} }
void _toClipboard(List<String> sources, {required bool move}) { void _toClipboard(List<String> sources, {required bool move}) {
if (_blockShizukuMove(sources, null, move: move)) return;
if (sources.isEmpty) { if (sources.isEmpty) {
_snack(_pickFirst(move)); _snack(_pickFirst(move));
return; return;
@ -3401,6 +3411,7 @@ class _ExplorerPageState extends State<ExplorerPage> {
_snack(_pickFirst(move)); _snack(_pickFirst(move));
return false; return false;
} }
if (_blockShizukuMove(sources, dest, move: move)) return false;
// X-plore 비교 f: 압축 안으로는 넣지 않고 (읽기 전용), 압축 안의 것을 붙여넣으면 꺼내기로 // X-plore 비교 f: 압축 안으로는 넣지 않고 (읽기 전용), 압축 안의 것을 붙여넣으면 꺼내기로
if (_blockArchive([dest], self: true)) return false; if (_blockArchive([dest], self: true)) return false;
if (sources.any(_insideArchive)) { if (sources.any(_insideArchive)) {

View File

@ -165,6 +165,14 @@ void main() {
await t.tap(find.text('이름 변경').first); await t.tap(find.text('이름 변경').first);
await wait(t, () => false); await wait(t, () => false);
expect(find.byType(TextField), findsNothing, reason: '이름 바꾸기 창도 열리지 않음'); expect(find.byType(TextField), findsNothing, reason: '이름 바꾸기 창도 열리지 않음');
// 28 지적 · 관리자 지시 (10/21): 이동은 시작 전에 막음 (예전에는 복사는 되고 원본 지우기에서 오류)
ScaffoldMessenger.of(t.element(find.byType(ExplorerPage))).clearSnackBars();
await wait(t, () => false);
await t.tap(find.text('이동').first);
await wait(t, () => find.textContaining('옮길 수 없습니다').evaluate().isNotEmpty);
expect(find.text('읽기 전용이라 옮길 수 없습니다 - 복사를 쓰세요'), findsOneWidget);
expect(find.textContaining('담았습니다'), findsNothing, reason: '이동으로 담지도 않음');
expect(fake.calls.where((x) => x.startsWith('copy:')), isEmpty);
// 다시 열기: 경고 없이 (이번 실행에서 이미 봄) // 다시 열기: 경고 없이 (이번 실행에서 이미 봄)
await t.pumpWidget(const SizedBox()); await t.pumpWidget(const SizedBox());
await t.runAsync(() => t.pumpWidget(MaterialApp(home: ExplorerPage(key: UniqueKey(), c: ctl())))); await t.runAsync(() => t.pumpWidget(MaterialApp(home: ExplorerPage(key: UniqueKey(), c: ctl()))));