다운로드 알림 [✕] 닫기 · Android 화면 방향 설정 (가로 고정 · 세로 고정 · 자동)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>main
parent
14b2489938
commit
bca0c04459
|
|
@ -74,6 +74,9 @@ class AppSettings {
|
||||||
/// 탐색기에서 동영상을 열었을 때 (더블클릭 · 연결 프로그램): 'play' 바로 재생 / 'add' 편집 목록에 추가
|
/// 탐색기에서 동영상을 열었을 때 (더블클릭 · 연결 프로그램): 'play' 바로 재생 / 'add' 편집 목록에 추가
|
||||||
String openFileAction = 'play';
|
String openFileAction = 'play';
|
||||||
|
|
||||||
|
/// Android 화면 방향: 'landscape' 가로 고정 (기본) / 'portrait' 세로 고정 / 'auto' 기기 방향 따라
|
||||||
|
String screenOrientation = 'landscape';
|
||||||
|
|
||||||
/// 프로그램이 켜져 있을 때 탐색기에서 연 동영상 재생: 'same' 켜져 있는 창에서 / 'new' 새 재생 창
|
/// 프로그램이 켜져 있을 때 탐색기에서 연 동영상 재생: 'same' 켜져 있는 창에서 / 'new' 새 재생 창
|
||||||
String openFileWindow = 'same';
|
String openFileWindow = 'same';
|
||||||
String homeUrl = 'https://www.youtube.com/';
|
String homeUrl = 'https://www.youtube.com/';
|
||||||
|
|
@ -134,6 +137,7 @@ class AppSettings {
|
||||||
'uiScale': uiScale,
|
'uiScale': uiScale,
|
||||||
'uiScaleDefault': uiScaleDefault,
|
'uiScaleDefault': uiScaleDefault,
|
||||||
'openFileAction': openFileAction,
|
'openFileAction': openFileAction,
|
||||||
|
'screenOrientation': screenOrientation,
|
||||||
'openFileWindow': openFileWindow,
|
'openFileWindow': openFileWindow,
|
||||||
'homeUrl': homeUrl,
|
'homeUrl': homeUrl,
|
||||||
'browserEngine': browserEngine,
|
'browserEngine': browserEngine,
|
||||||
|
|
@ -181,6 +185,9 @@ class AppSettings {
|
||||||
..uiScale = clampUiScale(j['uiScale'] as num?)
|
..uiScale = clampUiScale(j['uiScale'] as num?)
|
||||||
..uiScaleDefault = clampUiScale(j['uiScaleDefault'] as num?)
|
..uiScaleDefault = clampUiScale(j['uiScaleDefault'] as num?)
|
||||||
..openFileAction = j['openFileAction'] == 'add' ? 'add' : 'play'
|
..openFileAction = j['openFileAction'] == 'add' ? 'add' : 'play'
|
||||||
|
..screenOrientation = const ['portrait', 'auto'].contains(j['screenOrientation'])
|
||||||
|
? j['screenOrientation'] as String
|
||||||
|
: 'landscape'
|
||||||
..openFileWindow = j['openFileWindow'] == 'new' ? 'new' : 'same'
|
..openFileWindow = j['openFileWindow'] == 'new' ? 'new' : 'same'
|
||||||
..homeUrl = j['homeUrl'] as String? ?? 'https://www.youtube.com/'
|
..homeUrl = j['homeUrl'] as String? ?? 'https://www.youtube.com/'
|
||||||
..browserEngine = j['browserEngine'] as String? ?? 'edge'
|
..browserEngine = j['browserEngine'] as String? ?? 'edge'
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import 'core/app_update.dart';
|
||||||
import 'core/playlist.dart';
|
import 'core/playlist.dart';
|
||||||
import 'platform/android/android_download_tools.dart';
|
import 'platform/android/android_download_tools.dart';
|
||||||
import 'platform/android/android_keep_alive.dart';
|
import 'platform/android/android_keep_alive.dart';
|
||||||
|
import 'platform/android/android_shell.dart' show applyScreenOrientation;
|
||||||
import 'platform/android/android_storage.dart';
|
import 'platform/android/android_storage.dart';
|
||||||
import 'platform/windows/app_paths.dart';
|
import 'platform/windows/app_paths.dart';
|
||||||
import 'platform/windows/cef_runtime.dart';
|
import 'platform/windows/cef_runtime.dart';
|
||||||
|
|
@ -354,15 +355,15 @@ String lastWorkText(AppController c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Android: 창 하나. 트레이 · 단축키 · 탐색기 연결 · 다운로드 (yt-dlp · aria2) · 업데이트는 없다.
|
/// Android: 창 하나. 트레이 · 단축키 · 탐색기 연결 · 다운로드 (yt-dlp · aria2) · 업데이트는 없다.
|
||||||
/// 데스크톱 화면을 그대로 쓰므로 가로 화면으로 띄운다.
|
/// 데스크톱 화면을 그대로 쓰므로 기본은 가로 화면 (환경 설정 > 화면 방향에서 세로 · 자동으로 바꿀 수 있음).
|
||||||
Future<void> runAndroid(String dataDir) async {
|
Future<void> runAndroid(String dataDir) async {
|
||||||
await SystemChrome.setPreferredOrientations(
|
await applyScreenOrientation('landscape');
|
||||||
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
|
|
||||||
final services = PlatformServices.create();
|
final services = PlatformServices.create();
|
||||||
final logs = Directory(p.join(dataDir, 'Logs'))..createSync(recursive: true);
|
final logs = Directory(p.join(dataDir, 'Logs'))..createSync(recursive: true);
|
||||||
final controller = AppController(services, settingsStore: SettingsStore())..logFile = p.join(logs.path, 'app.log');
|
final controller = AppController(services, settingsStore: SettingsStore())..logFile = p.join(logs.path, 'app.log');
|
||||||
controller.note('── 시작 $appTitle (Android) ──');
|
controller.note('── 시작 $appTitle (Android) ──');
|
||||||
await controller.init();
|
await controller.init();
|
||||||
|
await applyScreenOrientation(controller.settings.screenOrientation);
|
||||||
final bookmarks = BookmarksController();
|
final bookmarks = BookmarksController();
|
||||||
await bookmarks.load();
|
await bookmarks.load();
|
||||||
final navigatorKey = GlobalKey<NavigatorState>();
|
final navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,13 @@ import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
import '../../services/app_shell.dart';
|
import '../../services/app_shell.dart';
|
||||||
|
|
||||||
|
/// Android 화면 방향 적용: 'landscape' 가로 고정 / 'portrait' 세로 고정 / 'auto' 기기 방향 따라 (환경 설정 > 화면)
|
||||||
|
Future<void> applyScreenOrientation(String mode) => SystemChrome.setPreferredOrientations(switch (mode) {
|
||||||
|
'portrait' => [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown],
|
||||||
|
'auto' => const <DeviceOrientation>[], // 빈 목록 = 기기 · 시스템 설정을 따름
|
||||||
|
_ => [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight],
|
||||||
|
});
|
||||||
|
|
||||||
/// Android: 창 · 트레이 · 단축키 · 탐색기 연결은 없음. 종료 · 전체 화면 · 웹 주소 열기 · 폴더 열기 (파일 앱).
|
/// Android: 창 · 트레이 · 단축키 · 탐색기 연결은 없음. 종료 · 전체 화면 · 웹 주소 열기 · 폴더 열기 (파일 앱).
|
||||||
class AndroidShell extends NoopShell {
|
class AndroidShell extends NoopShell {
|
||||||
static const _ch = MethodChannel('jj_mkvmaker/android');
|
static const _ch = MethodChannel('jj_mkvmaker/android');
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
final bar = m.showSnackBar(SnackBar(
|
final bar = m.showSnackBar(SnackBar(
|
||||||
content: Text('다운로드 추가: ${_title.isEmpty ? _url : _title}'),
|
content: Text('다운로드 추가: ${_title.isEmpty ? _url : _title}'),
|
||||||
duration: const Duration(minutes: 10),
|
duration: const Duration(minutes: 10),
|
||||||
|
showCloseIcon: true, // [✕] 로 바로 닫기
|
||||||
action: SnackBarAction(label: '목록 보기', onPressed: () => DownloadsPage.open(nav, d)),
|
action: SnackBarAction(label: '목록 보기', onPressed: () => DownloadsPage.open(nav, d)),
|
||||||
));
|
));
|
||||||
// 받기 준비가 끝나면 (진행률이 나오거나 · 끝 · 실패 · 취소 · 목록에서 지움) 알림을 닫는다
|
// 받기 준비가 끝나면 (진행률이 나오거나 · 끝 · 실패 · 취소 · 목록에서 지움) 알림을 닫는다
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import 'setup_dialog.dart';
|
||||||
import 'update_dialog.dart';
|
import 'update_dialog.dart';
|
||||||
import '../app/settings.dart';
|
import '../app/settings.dart';
|
||||||
import '../platform/windows/cef_runtime.dart';
|
import '../platform/windows/cef_runtime.dart';
|
||||||
|
import '../platform/android/android_shell.dart' show applyScreenOrientation;
|
||||||
import 'android_file_browser.dart';
|
import 'android_file_browser.dart';
|
||||||
import 'app_actions.dart';
|
import 'app_actions.dart';
|
||||||
import 'cef_setup.dart';
|
import 'cef_setup.dart';
|
||||||
|
|
@ -289,6 +290,23 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
_section('화면'),
|
_section('화면'),
|
||||||
|
if (!desk)
|
||||||
|
ListTile(
|
||||||
|
title: const Text('화면 방향'),
|
||||||
|
subtitle: const Text('가로 고정 · 세로 고정, 또는 자동 (기기를 돌리는 대로, 기기의 자동 회전 설정을 따름)'),
|
||||||
|
trailing: DropdownButton<String>(
|
||||||
|
value: s.screenOrientation,
|
||||||
|
items: const [
|
||||||
|
DropdownMenuItem(value: 'landscape', child: Text('가로 고정 (기본)')),
|
||||||
|
DropdownMenuItem(value: 'portrait', child: Text('세로 고정')),
|
||||||
|
DropdownMenuItem(value: 'auto', child: Text('자동')),
|
||||||
|
],
|
||||||
|
onChanged: (v) {
|
||||||
|
c.updateSettings((x) => x.screenOrientation = v!);
|
||||||
|
applyScreenOrientation(v!);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('기본 화면 크기'),
|
title: const Text('기본 화면 크기'),
|
||||||
subtitle: Text('글자 · 버튼 크기입니다. 위쪽 막대의 − · + 로 그때그때 바꿀 수 있고, 가운데 숫자를 누르면 이 크기로 돌아갑니다 '
|
subtitle: Text('글자 · 버튼 크기입니다. 위쪽 막대의 − · + 로 그때그때 바꿀 수 있고, 가운데 숫자를 누르면 이 크기로 돌아갑니다 '
|
||||||
|
|
|
||||||
|
|
@ -260,10 +260,21 @@ void main() {
|
||||||
expect(find.text('목록 보기'), findsOneWidget);
|
expect(find.text('목록 보기'), findsOneWidget);
|
||||||
await tester.pump(const Duration(seconds: 30));
|
await tester.pump(const Duration(seconds: 30));
|
||||||
expect(find.text('목록 보기'), findsOneWidget, reason: '준비 중에는 그대로');
|
expect(find.text('목록 보기'), findsOneWidget, reason: '준비 중에는 그대로');
|
||||||
|
expect(find.byIcon(Icons.close), findsOneWidget); // [✕] 로 바로 닫을 수도 있다
|
||||||
d.tasks.first.progress = 0.1;
|
d.tasks.first.progress = 0.1;
|
||||||
d.refresh();
|
d.refresh();
|
||||||
await settle();
|
await settle();
|
||||||
expect(find.text('목록 보기'), findsNothing);
|
expect(find.text('목록 보기'), findsNothing);
|
||||||
|
|
||||||
|
// [✕] 누르면 바로 닫힘
|
||||||
|
host.onUrl('https://www.youtube.com/watch?v=ghi');
|
||||||
|
await tester.pump();
|
||||||
|
await tester.tap(find.text('다운로드'));
|
||||||
|
await settle();
|
||||||
|
expect(find.text('목록 보기'), findsOneWidget);
|
||||||
|
await tester.tap(find.byIcon(Icons.close));
|
||||||
|
await settle();
|
||||||
|
expect(find.text('목록 보기'), findsNothing);
|
||||||
d.dispose();
|
d.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,16 @@ void main() {
|
||||||
final back = AppSettings.fromJson(s.toJson());
|
final back = AppSettings.fromJson(s.toJson());
|
||||||
expect([back.openFileAction, back.openFileWindow], ['add', 'new']);
|
expect([back.openFileAction, back.openFileWindow], ['add', 'new']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Android 화면 방향: 기본 가로 고정, 세로 · 자동 저장, 모르는 값은 가로', () {
|
||||||
|
final s = AppSettings();
|
||||||
|
expect(s.screenOrientation, 'landscape');
|
||||||
|
for (final m in ['portrait', 'auto', 'landscape']) {
|
||||||
|
s.screenOrientation = m;
|
||||||
|
expect(AppSettings.fromJson(s.toJson()).screenOrientation, m);
|
||||||
|
}
|
||||||
|
expect(AppSettings.fromJson({'screenOrientation': 'sideways'}).screenOrientation, 'landscape');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('중복 실행 방지: 나중 실행은 넘기고, 짧은 시간의 요청은 하나로', () async {
|
test('중복 실행 방지: 나중 실행은 넘기고, 짧은 시간의 요청은 하나로', () async {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue