JJ_MKVMaker/lib/ui/folder_picker.dart

122 lines
4.8 KiB
Dart

import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import '../core/vfs.dart';
import '../core/webdav.dart';
import '../l10n/tr.dart';
import 'android_file_browser.dart';
/// 폴더 고르기. Android 는 앱 안 화면 (내장 저장소 · SD 카드 · USB 를 실제 경로로 고를 수 있음), PC 는 Windows 폴더 선택 창
Future<String?> pickFolder(BuildContext context, String title, [String? initial]) => Platform.isAndroid
? showAndroidFolderBrowser(context, title: title, initialDirectory: initial)
: FilePicker.getDirectoryPath(dialogTitle: title, initialDirectory: initial);
/// 124: 이 기기 폴더 또는 WebDAV 서버의 폴더 고르기 (서버가 없으면 바로 이 기기 폴더).
/// 환경 설정에서 추가한 서버가 실시간 동기화 · 모니터링의 고르는 곳에도 바로 보이게.
Future<String?> pickFolderOrDav(BuildContext context, String title, [String? initial]) async {
final servers = DavRegistry.servers;
if (servers.isEmpty) return pickFolder(context, title, initial);
final where = await showDialog<String>(
context: context,
builder: (ctx) => SimpleDialog(
title: Text(title),
children: [
SimpleDialogOption(
onPressed: () => Navigator.pop(ctx, ''),
child: ListTile(leading: const Icon(Icons.folder_outlined), title: Text(tr('이 기기의 폴더'))),
),
for (final s in servers)
SimpleDialogOption(
onPressed: () => Navigator.pop(ctx, s.id),
child: ListTile(leading: const Icon(Icons.cloud_outlined), title: Text(s.label), subtitle: Text(s.url)),
),
],
),
);
if (where == null || !context.mounted) return null;
if (where.isEmpty) return pickFolder(context, title, initial != null && !isDav(initial) ? initial : null);
final start = initial != null && isDav(initial) && DavPath.parse(initial).server == where ? initial : '$davScheme$where/';
return showDialog<String>(context: context, builder: (_) => _DavFolderDialog(title: title, start: start));
}
/// WebDAV 서버 안의 폴더를 들어가며 고른다
class _DavFolderDialog extends StatefulWidget {
const _DavFolderDialog({required this.title, required this.start});
final String title;
final String start;
@override
State<_DavFolderDialog> createState() => _DavFolderDialogState();
}
class _DavFolderDialogState extends State<_DavFolderDialog> {
late String _dir = widget.start;
List<String>? _folders;
String? _error;
@override
void initState() {
super.initState();
_open(_dir);
}
Future<void> _open(String dir) async {
setState(() {
_dir = dir;
_folders = null;
_error = null;
});
try {
final list = await vList(dir, strict: true);
final names = [for (final e in list) if (e.isDir) e.path]..sort((a, b) => vBasename(a).toLowerCase().compareTo(vBasename(b).toLowerCase()));
if (mounted && _dir == dir) setState(() => _folders = names);
} catch (e) {
if (mounted && _dir == dir) setState(() => _error = '$e');
}
}
@override
Widget build(BuildContext context) {
final atRoot = DavPath.parse(_dir).rel.replaceAll('/', '').isEmpty;
return AlertDialog(
title: Text(widget.title),
content: SizedBox(
width: 460,
height: 380,
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Text(vDisplay(_dir) + DavPath.parse(_dir).rel, maxLines: 2, overflow: TextOverflow.ellipsis),
const Divider(),
Expanded(
child: _error != null
? Center(child: Text(_error!, style: const TextStyle(color: Colors.redAccent)))
: _folders == null
? const Center(child: CircularProgressIndicator())
: ListView(children: [
if (!atRoot)
ListTile(
dense: true,
leading: const Icon(Icons.arrow_upward),
title: const Text('..'),
onTap: () => _open(vDirname(_dir)),
),
for (final f in _folders!)
ListTile(
dense: true,
leading: const Icon(Icons.folder_outlined),
title: Text(vBasename(f)),
onTap: () => _open(f),
),
]),
),
]),
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: Text(tr('취소'))),
FilledButton(onPressed: _error != null ? null : () => Navigator.pop(context, _dir), child: Text(tr('이 폴더 고르기'))),
],
);
}
}