폴더 열기 · 결과 폴더 버튼 · 웹 브라우저 버튼 위치
- Android [폴더 열기]: 다른 파일 관리자 (X-plore 등) 가 열려 폴더로 가지 않던 것 → 삼성 내 파일 (그 폴더로 바로) · 시스템 파일 앱 순으로 - MKV 화면: MKV 만들기 옆에 [결과 폴더] (보고 있는 동영상의 jj_mkv 폴더 열기, 아직 없으면 알림) - 웹 브라우저 버튼: MKV 만들기 옆 → 위쪽 왼쪽 공통 버튼 (MKV 화면 다음, 뒤로 앞). 모든 화면 같은 자리 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>main
parent
4cf2ac29ee
commit
7a34bedbb6
|
|
@ -59,13 +59,27 @@ class MainActivity : FlutterActivity() {
|
||||||
val root = Environment.getExternalStorageDirectory().path
|
val root = Environment.getExternalStorageDirectory().path
|
||||||
if (!path.startsWith(root)) return false
|
if (!path.startsWith(root)) return false
|
||||||
val rel = path.removePrefix(root).trim('/')
|
val rel = path.removePrefix(root).trim('/')
|
||||||
|
// 1. 삼성 "내 파일": 그 폴더로 바로 연다
|
||||||
|
try {
|
||||||
|
startActivity(
|
||||||
|
Intent("samsung.myfiles.intent.action.LAUNCH_MY_FILES")
|
||||||
|
.setPackage("com.sec.android.app.myfiles")
|
||||||
|
.putExtra("samsung.myfiles.intent.extra.START_PATH", path)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
}
|
||||||
|
// 2. 시스템 "파일" 앱 (Google / AOSP). 다른 파일 관리자 (X-plore 등) 는 이 주소의 폴더로 가지 않아 앱을 정해 연다
|
||||||
val uri = android.provider.DocumentsContract.buildDocumentUri(
|
val uri = android.provider.DocumentsContract.buildDocumentUri(
|
||||||
"com.android.externalstorage.documents", "primary:$rel"
|
"com.android.externalstorage.documents", "primary:$rel"
|
||||||
)
|
)
|
||||||
for (type in listOf(android.provider.DocumentsContract.Document.MIME_TYPE_DIR, "resource/folder")) {
|
for (pkg in listOf("com.google.android.documentsui", "com.android.documentsui", null)) {
|
||||||
try {
|
try {
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(Intent.ACTION_VIEW).setDataAndType(uri, type)
|
Intent(Intent.ACTION_VIEW)
|
||||||
|
.setDataAndType(uri, android.provider.DocumentsContract.Document.MIME_TYPE_DIR)
|
||||||
|
.apply { if (pkg != null) setPackage(pkg) }
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,12 @@ class AppScope extends InheritedWidget {
|
||||||
class AppNavButtons extends StatelessWidget {
|
class AppNavButtons extends StatelessWidget {
|
||||||
/// 다운로드 목록 화면 자신 (그 버튼을 "지금 여기" 로 표시)
|
/// 다운로드 목록 화면 자신 (그 버튼을 "지금 여기" 로 표시)
|
||||||
final bool onDownloadsPage;
|
final bool onDownloadsPage;
|
||||||
const AppNavButtons({super.key, this.onDownloadsPage = false});
|
|
||||||
|
|
||||||
static const double width = 4 * 40;
|
/// 웹 브라우저 화면 자신
|
||||||
|
final bool onBrowserPage;
|
||||||
|
const AppNavButtons({super.key, this.onDownloadsPage = false, this.onBrowserPage = false});
|
||||||
|
|
||||||
|
static const double width = 5 * 40;
|
||||||
|
|
||||||
/// MKV 화면 (맨 처음 화면) 까지 돌아가기
|
/// MKV 화면 (맨 처음 화면) 까지 돌아가기
|
||||||
static void toMkv(BuildContext context) => Navigator.of(context).popUntil((r) => r.isFirst);
|
static void toMkv(BuildContext context) => Navigator.of(context).popUntil((r) => r.isFirst);
|
||||||
|
|
@ -91,6 +94,19 @@ class AppNavButtons extends StatelessWidget {
|
||||||
atRoot ? null : () => toMkv(context),
|
atRoot ? null : () => toMkv(context),
|
||||||
here: atRoot,
|
here: atRoot,
|
||||||
),
|
),
|
||||||
|
// 웹 브라우저: MKV 화면 버튼 다음 (모든 화면 같은 자리)
|
||||||
|
btn(
|
||||||
|
Icon(Icons.public, color: onBrowserPage ? JjColors.accent : null),
|
||||||
|
onBrowserPage ? '웹 브라우저 (지금 여기)' : '웹 브라우저',
|
||||||
|
scope?.bookmarks == null || onBrowserPage
|
||||||
|
? null
|
||||||
|
: () => Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute<void>(
|
||||||
|
builder: (_) =>
|
||||||
|
BrowserPage(c: scope!.controller, downloads: scope.downloads, bookmarks: scope.bookmarks!))),
|
||||||
|
here: onBrowserPage,
|
||||||
|
),
|
||||||
btn(const Icon(Icons.arrow_back), '뒤로', atRoot ? null : () => Navigator.maybePop(context)),
|
btn(const Icon(Icons.arrow_back), '뒤로', atRoot ? null : () => Navigator.maybePop(context)),
|
||||||
btn(
|
btn(
|
||||||
Icon(Icons.download_for_offline_outlined, color: onDownloadsPage ? JjColors.accent : null),
|
Icon(Icons.download_for_offline_outlined, color: onDownloadsPage ? JjColors.accent : null),
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,7 @@ class _BrowserPageState extends State<BrowserPage> {
|
||||||
color: JjColors.panel,
|
color: JjColors.panel,
|
||||||
padding: const EdgeInsets.only(left: 8, right: appBarRightPadding),
|
padding: const EdgeInsets.only(left: 8, right: appBarRightPadding),
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
const AppNavButtons(),
|
const AppNavButtons(onBrowserPage: true),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Container(width: 1, height: 24, color: JjColors.border),
|
Container(width: 1, height: 24, color: JjColors.border),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import '../core/models.dart';
|
||||||
import '../core/output_paths.dart';
|
import '../core/output_paths.dart';
|
||||||
import 'ai_dialog.dart';
|
import 'ai_dialog.dart';
|
||||||
import 'app_actions.dart';
|
import 'app_actions.dart';
|
||||||
import 'browser_page.dart';
|
|
||||||
import 'downloads_page.dart';
|
import 'downloads_page.dart';
|
||||||
import 'player_page.dart';
|
import 'player_page.dart';
|
||||||
import 'subtitle_editor_page.dart';
|
import 'subtitle_editor_page.dart';
|
||||||
|
|
@ -95,6 +94,21 @@ class _TopBar extends StatelessWidget {
|
||||||
const _TopBar({required this.c, this.downloads, this.onExit, this.bookmarks});
|
const _TopBar({required this.c, this.downloads, this.onExit, this.bookmarks});
|
||||||
|
|
||||||
/// 고른 동영상이 없을 때: 목록 전체를 만들지 묻는다
|
/// 고른 동영상이 없을 때: 목록 전체를 만들지 묻는다
|
||||||
|
/// 보고 있는 동영상의 결과 폴더 (jj_mkv) 를 탐색기 · 파일 앱으로. 아직 없으면 알린다.
|
||||||
|
Future<void> _openOutput(BuildContext context) async {
|
||||||
|
final v = c.selected;
|
||||||
|
if (v == null) return;
|
||||||
|
final dir = outputDirFor(v.path);
|
||||||
|
if (!await Directory(dir).exists()) {
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.maybeOf(context)
|
||||||
|
?.showSnackBar(SnackBar(content: Text('아직 만든 MKV · 자막이 없습니다: $dir')));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await c.services.shell.revealFile(dir);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _confirmBuildAll(BuildContext context) async {
|
Future<void> _confirmBuildAll(BuildContext context) async {
|
||||||
final todo = c.videos.where((v) => v.status != JobStatus.done).length;
|
final todo = c.videos.where((v) => v.status != JobStatus.done).length;
|
||||||
final ok = await showDialog<bool>(
|
final ok = await showDialog<bool>(
|
||||||
|
|
@ -139,7 +153,7 @@ class _TopBar extends StatelessWidget {
|
||||||
(c.busy ? (jobText ? 350 : 60) : 0) +
|
(c.busy ? (jobText ? 350 : 60) : 0) +
|
||||||
(hasPlayer ? (rightText ? 201 : 48) : 0) +
|
(hasPlayer ? (rightText ? 201 : 48) : 0) +
|
||||||
(mkvText ? 166 + (c.busy && jobText ? 60 : 0) : 48) +
|
(mkvText ? 166 + (c.busy && jobText ? 60 : 0) : 48) +
|
||||||
(bookmarks != null ? (rightText ? 138 : 48) : 0) +
|
(rightText ? 166 : 48) + // 결과 폴더
|
||||||
(dlBox ? 162 : 0) +
|
(dlBox ? 162 : 0) +
|
||||||
(usage ? 220 : 0) + 96 + 92 +
|
(usage ? 220 : 0) + 96 + 92 +
|
||||||
(c.checked.isEmpty ? 0 : 3 * 26) +
|
(c.checked.isEmpty ? 0 : 3 * 26) +
|
||||||
|
|
@ -241,18 +255,13 @@ class _TopBar extends StatelessWidget {
|
||||||
icon: Icon(c.busy ? Icons.playlist_add : Icons.play_arrow, size: 20),
|
icon: Icon(c.busy ? Icons.playlist_add : Icons.play_arrow, size: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (bookmarks != null) ...[
|
// 만든 MKV 가 있는 폴더 (jj_mkv). 웹 브라우저 버튼은 왼쪽 공통 버튼으로 옮김
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
action(
|
action(Icons.folder_special_outlined, '결과 폴더', c.selected == null ? null : () => _openOutput(context),
|
||||||
Icons.public,
|
text: rightText,
|
||||||
'브라우저',
|
tip: c.selected == null
|
||||||
() => Navigator.push(
|
? '결과 폴더 열기 (동영상을 고르세요)'
|
||||||
context,
|
: '결과 폴더 열기: ${outputDirFor(c.selected!.path)}'),
|
||||||
MaterialPageRoute<void>(
|
|
||||||
builder: (_) => BrowserPage(c: c, downloads: downloads, bookmarks: bookmarks!))),
|
|
||||||
text: rightText,
|
|
||||||
tip: '웹 브라우저'),
|
|
||||||
],
|
|
||||||
if (dlBox) ...[
|
if (dlBox) ...[
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
_DownloadBox(d: downloads!),
|
_DownloadBox(d: downloads!),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue