설정 A 분야 8: 웹 브라우저 (BrowserSettings) - 키 10
- 이미 옮긴 분야 뒤의 같은 이름 (s.tor.homeUrl) 은 바꾸지 않음 - browser_page 의 _CefView 자기 필드 cefWhenElevated 는 그대로 (같은 이름이라 잘못 바뀐 것을 되돌림) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>modular
parent
6beeb22e43
commit
7c6320165a
|
|
@ -33,7 +33,7 @@ void main() {
|
||||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||||
c.settings
|
c.settings
|
||||||
..webViewDataDir = dataDir
|
..webViewDataDir = dataDir
|
||||||
..homeUrl = url;
|
..browser.homeUrl = url;
|
||||||
final bm = BookmarksController(p.join(dataDir, 'bm.json'));
|
final bm = BookmarksController(p.join(dataDir, 'bm.json'));
|
||||||
final d = DownloadManager(backends: const [], settings: () => c.settings, readClipboard: () async => null);
|
final d = DownloadManager(backends: const [], settings: () => c.settings, readClipboard: () async => null);
|
||||||
final navKey = GlobalKey<NavigatorState>();
|
final navKey = GlobalKey<NavigatorState>();
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ void main() {
|
||||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||||
c.settings
|
c.settings
|
||||||
..webViewDataDir = p.join(data, 'webview')
|
..webViewDataDir = p.join(data, 'webview')
|
||||||
..browserEngine = 'chrome'
|
..browser.engine = 'chrome'
|
||||||
// 207: 이 PC 처럼 관리자 권한으로 실행 중이면 설정을 켠 때만 Chrome 엔진이 돈다 (--do-not-de-elevate)
|
// 207: 이 PC 처럼 관리자 권한으로 실행 중이면 설정을 켠 때만 Chrome 엔진이 돈다 (--do-not-de-elevate)
|
||||||
..cefWhenElevated = true;
|
..browser.cefWhenElevated = true;
|
||||||
final answered = <String>[];
|
final answered = <String>[];
|
||||||
final logs = <String>[];
|
final logs = <String>[];
|
||||||
final g = NetGuard.instance
|
final g = NetGuard.instance
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ void main() {
|
||||||
c.settings
|
c.settings
|
||||||
..webViewDataDir = dataDir
|
..webViewDataDir = dataDir
|
||||||
..uiLanguage = 'ko'
|
..uiLanguage = 'ko'
|
||||||
..webTranslate = true;
|
..browser.translate = true;
|
||||||
final bm = BookmarksController(p.join(dataDir, 'bm.json'));
|
final bm = BookmarksController(p.join(dataDir, 'bm.json'));
|
||||||
await tester.pumpWidget(MaterialApp(home: BrowserPage(c: c, bookmarks: bm, initialUrl: url)));
|
await tester.pumpWidget(MaterialApp(home: BrowserPage(c: c, bookmarks: bm, initialUrl: url)));
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ void main() {
|
||||||
// 3. JavaScript 끄기 → 페이지를 다시 읽고, 페이지 스크립트는 돌지 않는다
|
// 3. JavaScript 끄기 → 페이지를 다시 읽고, 페이지 스크립트는 돌지 않는다
|
||||||
expect(scriptRuns, greaterThan(0));
|
expect(scriptRuns, greaterThan(0));
|
||||||
final loads = pageLoads, runs = scriptRuns;
|
final loads = pageLoads, runs = scriptRuns;
|
||||||
await c.updateSettings((s) => s.webJavaScript = false);
|
await c.updateSettings((s) => s.browser.javaScript = false);
|
||||||
await waitServer(() => pageLoads > loads);
|
await waitServer(() => pageLoads > loads);
|
||||||
await tester.runAsync(() => Future<void>.delayed(const Duration(seconds: 3)));
|
await tester.runAsync(() => Future<void>.delayed(const Duration(seconds: 3)));
|
||||||
// ignore: avoid_print
|
// ignore: avoid_print
|
||||||
|
|
@ -148,7 +148,7 @@ void main() {
|
||||||
|
|
||||||
// 5. 다시 켜기 → 다시 읽고 페이지 스크립트가 돈다, 번역도 다시
|
// 5. 다시 켜기 → 다시 읽고 페이지 스크립트가 돈다, 번역도 다시
|
||||||
final runs2 = scriptRuns;
|
final runs2 = scriptRuns;
|
||||||
await c.updateSettings((s) => s.webJavaScript = true);
|
await c.updateSettings((s) => s.browser.javaScript = true);
|
||||||
await waitServer(() => scriptRuns > runs2);
|
await waitServer(() => scriptRuns > runs2);
|
||||||
expect(scriptRuns, greaterThan(runs2));
|
expect(scriptRuns, greaterThan(runs2));
|
||||||
d = await waitFor((d) => d['late']!.isNotEmpty && hangul.hasMatch(d['h']!), 'JavaScript 다시 켬');
|
d = await waitFor((d) => d['late']!.isNotEmpty && hangul.hasMatch(d['h']!), 'JavaScript 다시 켬');
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import '../services/app_shell.dart' show appIconOf;
|
||||||
import '../core/remote_idle.dart';
|
import '../core/remote_idle.dart';
|
||||||
import 'settings/records.dart';
|
import 'settings/records.dart';
|
||||||
import 'settings/settings_group.dart';
|
import 'settings/settings_group.dart';
|
||||||
|
import 'settings/browser_settings.dart';
|
||||||
import 'settings/download_settings.dart';
|
import 'settings/download_settings.dart';
|
||||||
import 'settings/reader_settings.dart';
|
import 'settings/reader_settings.dart';
|
||||||
import 'settings/ai_image_settings.dart';
|
import 'settings/ai_image_settings.dart';
|
||||||
|
|
@ -26,6 +27,7 @@ import 'settings/explorer_settings.dart';
|
||||||
export 'settings/records.dart';
|
export 'settings/records.dart';
|
||||||
export 'settings/settings_group.dart';
|
export 'settings/settings_group.dart';
|
||||||
export 'settings/settings_store.dart';
|
export 'settings/settings_store.dart';
|
||||||
|
export 'settings/browser_settings.dart';
|
||||||
export 'settings/download_settings.dart';
|
export 'settings/download_settings.dart';
|
||||||
export 'settings/reader_settings.dart';
|
export 'settings/reader_settings.dart';
|
||||||
export 'settings/ai_image_settings.dart';
|
export 'settings/ai_image_settings.dart';
|
||||||
|
|
@ -46,14 +48,8 @@ class AppSettings {
|
||||||
/// 73: 파일 탐색기 · Rsync 즐겨찾기 (폴더, Rsync 는 짝)
|
/// 73: 파일 탐색기 · Rsync 즐겨찾기 (폴더, Rsync 는 짝)
|
||||||
List<FolderBookmark> folderBookmarks = [];
|
List<FolderBookmark> folderBookmarks = [];
|
||||||
|
|
||||||
/// 웹 브라우저에서 YouTube 광고 자동 건너뛰기 (건너뛰기 버튼 누르기 · 건너뛸 수 없는 광고는 빨리 감기 · 소리 끄기)
|
|
||||||
bool youtubeAdSkip = true;
|
|
||||||
|
|
||||||
/// YouTube 페이지의 광고 배너 · 광고 영역 숨기기
|
|
||||||
bool youtubeAdHide = true;
|
|
||||||
|
|
||||||
/// 웹 브라우저 위에 다른 화면이 올라오거나 다른 화면으로 가면 페이지의 동영상 · 소리를 멈춤 (끄면 계속 들림)
|
|
||||||
bool webPauseOnLeave = true;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -92,7 +88,7 @@ class AppSettings {
|
||||||
/// 폰 세로 (좁은 화면) MKV 목록 크기: 'auto' 목록은 내용만큼 · 자세히 칸에 최소 높이 (처음 값) · 'small' · 'large' (관리자 판단 10/17)
|
/// 폰 세로 (좁은 화면) MKV 목록 크기: 'auto' 목록은 내용만큼 · 자세히 칸에 최소 높이 (처음 값) · 'small' · 'large' (관리자 판단 10/17)
|
||||||
String mkvCompactList = 'auto';
|
String mkvCompactList = 'auto';
|
||||||
|
|
||||||
/// 처음 화면: 'home' = 지금처럼 MKV 화면 (기본), 'browser' = 웹 브라우저 ([homeUrl]), 'files' = 파일 탐색기
|
/// 처음 화면: 'home' = 지금처럼 MKV 화면 (기본), 'browser' = 웹 브라우저 ([browser.homeUrl]), 'files' = 파일 탐색기
|
||||||
String startScreen = 'home';
|
String startScreen = 'home';
|
||||||
|
|
||||||
/// 화면 (글자 · 버튼) 크기 배율. [uiScale] 은 지금 크기 (위쪽 막대의 − · + 로 바꿈),
|
/// 화면 (글자 · 버튼) 크기 배율. [uiScale] 은 지금 크기 (위쪽 막대의 − · + 로 바꿈),
|
||||||
|
|
@ -127,22 +123,11 @@ class AppSettings {
|
||||||
|
|
||||||
/// 프로그램이 켜져 있을 때 탐색기에서 연 동영상 재생: 'same' 켜져 있는 창에서 / 'new' 새 재생 창
|
/// 프로그램이 켜져 있을 때 탐색기에서 연 동영상 재생: 'same' 켜져 있는 창에서 / 'new' 새 재생 창
|
||||||
String openFileWindow = 'same';
|
String openFileWindow = 'same';
|
||||||
String homeUrl = 'https://www.youtube.com/';
|
|
||||||
|
|
||||||
/// 앱 안 브라우저 엔진: 'edge' (WebView2, 기본). 'chrome' 은 내려받아 연결 (준비 중)
|
|
||||||
String browserEngine = 'edge';
|
|
||||||
|
|
||||||
/// 207: 관리자 권한으로 실행 중일 때도 내장 Chrome 엔진을 쓸지 (처음 꺼짐 - 관리자 판단: 보안이 먼저, CEF 는 샌드박스가 꺼져 있다)
|
|
||||||
bool cefWhenElevated = false;
|
|
||||||
|
|
||||||
/// "외부 브라우저로 열기" 에 쓸 브라우저: 'system' · 'chrome' · 'firefox' · 'edge' · 'whale'
|
|
||||||
String externalBrowser = 'system';
|
|
||||||
|
|
||||||
/// 앱 안 브라우저: 다른 언어로 된 웹 페이지를 화면 언어 ([uiLanguage]) 로 자동 번역 (Google 번역)
|
|
||||||
bool webTranslate = false;
|
|
||||||
|
|
||||||
/// 앱 안 브라우저: 페이지의 JavaScript 실행 (끄면 스크립트 없이 글 · 그림만)
|
|
||||||
bool webJavaScript = true;
|
|
||||||
|
|
||||||
// ── 네트워크 · DNS (사용자 요청 10/10: "DNS 주소를 강제로 내가 설정한 주소로", 기본 8.8.8.8 · 보조 8.8.4.4 - 관리자 판단) ──
|
// ── 네트워크 · DNS (사용자 요청 10/10: "DNS 주소를 강제로 내가 설정한 주소로", 기본 8.8.8.8 · 보조 8.8.4.4 - 관리자 판단) ──
|
||||||
|
|
||||||
|
|
@ -253,8 +238,6 @@ class AppSettings {
|
||||||
/// 66: 좌우로 밀 때 끝에서 처음으로 (끄면 끝에서 멈춤)
|
/// 66: 좌우로 밀 때 끝에서 처음으로 (끄면 끝에서 멈춤)
|
||||||
bool swipeWrap = true;
|
bool swipeWrap = true;
|
||||||
|
|
||||||
/// 66-2: 웹 브라우저에서 화면 옮기기 (위쪽 막대를 좌우로) 안내를 한 번 보였는지
|
|
||||||
bool browserSwipeHinted = false;
|
|
||||||
|
|
||||||
/// 저장 공간 정리 창에서 미리 체크할 묶음 (작업 임시 파일 · 받다 만 다운로드)
|
/// 저장 공간 정리 창에서 미리 체크할 묶음 (작업 임시 파일 · 받다 만 다운로드)
|
||||||
List<String> cleanupPrechecked = ['work', 'download'];
|
List<String> cleanupPrechecked = ['work', 'download'];
|
||||||
|
|
@ -403,8 +386,11 @@ class AppSettings {
|
||||||
/// 다운로드 · YouTube (yt-dlp · aria2)
|
/// 다운로드 · YouTube (yt-dlp · aria2)
|
||||||
final download = DownloadSettings();
|
final download = DownloadSettings();
|
||||||
|
|
||||||
|
/// 웹 브라우저 (앱 안 · 외부)
|
||||||
|
final browser = BrowserSettings();
|
||||||
|
|
||||||
/// 설정 분야들 (설정 A) - 저마다 자기 키를 읽고 쓴다. toJson · fromJson 은 이 목록을 돌며 합치기만 한다
|
/// 설정 분야들 (설정 A) - 저마다 자기 키를 읽고 쓴다. toJson · fromJson 은 이 목록을 돌며 합치기만 한다
|
||||||
List<SettingsGroup> get groups => [explorer, network, tor, copy, aiImage, reader, download];
|
List<SettingsGroup> get groups => [explorer, network, tor, copy, aiImage, reader, download, browser];
|
||||||
|
|
||||||
/// [plainSecrets]: 안전 저장소를 쓸 수 없을 때만 (예전 파일에 있던 비밀 값을 잃지 않게) 예전처럼 파일에 둔다
|
/// [plainSecrets]: 안전 저장소를 쓸 수 없을 때만 (예전 파일에 있던 비밀 값을 잃지 않게) 예전처럼 파일에 둔다
|
||||||
Map<String, Object?> toJson({bool plainSecrets = false}) {
|
Map<String, Object?> toJson({bool plainSecrets = false}) {
|
||||||
|
|
@ -414,9 +400,6 @@ class AppSettings {
|
||||||
'mkvOutputRoot': mkvOutputRoot,
|
'mkvOutputRoot': mkvOutputRoot,
|
||||||
'moveTargets': [for (final t in moveTargets) t.toJson()],
|
'moveTargets': [for (final t in moveTargets) t.toJson()],
|
||||||
'folderBookmarks': [for (final b in folderBookmarks) b.toJson()],
|
'folderBookmarks': [for (final b in folderBookmarks) b.toJson()],
|
||||||
'youtubeAdSkip': youtubeAdSkip,
|
|
||||||
'youtubeAdHide': youtubeAdHide,
|
|
||||||
'webPauseOnLeave': webPauseOnLeave,
|
|
||||||
'minimizeToTray': minimizeToTray,
|
'minimizeToTray': minimizeToTray,
|
||||||
'closeAction': closeAction,
|
'closeAction': closeAction,
|
||||||
'showHotkey': showHotkey,
|
'showHotkey': showHotkey,
|
||||||
|
|
@ -448,12 +431,6 @@ class AppSettings {
|
||||||
'appIcon': appIcon,
|
'appIcon': appIcon,
|
||||||
'uiLanguagesAdded': uiLanguagesAdded,
|
'uiLanguagesAdded': uiLanguagesAdded,
|
||||||
'openFileWindow': openFileWindow,
|
'openFileWindow': openFileWindow,
|
||||||
'homeUrl': homeUrl,
|
|
||||||
'browserEngine': browserEngine,
|
|
||||||
'cefWhenElevated': cefWhenElevated,
|
|
||||||
'externalBrowser': externalBrowser,
|
|
||||||
'webTranslate': webTranslate,
|
|
||||||
'webJavaScript': webJavaScript,
|
|
||||||
'zipLevel': zipLevel,
|
'zipLevel': zipLevel,
|
||||||
'wifiShareMinutes': wifiShareMinutes,
|
'wifiShareMinutes': wifiShareMinutes,
|
||||||
'wifiShareTailscale': wifiShareTailscale,
|
'wifiShareTailscale': wifiShareTailscale,
|
||||||
|
|
@ -472,7 +449,6 @@ class AppSettings {
|
||||||
'imageExts': imageExts,
|
'imageExts': imageExts,
|
||||||
'startMenuShortcut': startMenuShortcut,
|
'startMenuShortcut': startMenuShortcut,
|
||||||
'swipeWrap': swipeWrap,
|
'swipeWrap': swipeWrap,
|
||||||
'browserSwipeHinted': browserSwipeHinted,
|
|
||||||
'cleanupPrechecked': cleanupPrechecked,
|
'cleanupPrechecked': cleanupPrechecked,
|
||||||
'migrationNotice': migrationNotice,
|
'migrationNotice': migrationNotice,
|
||||||
'allowInnerToOuter': allowInnerToOuter,
|
'allowInnerToOuter': allowInnerToOuter,
|
||||||
|
|
@ -542,9 +518,6 @@ class AppSettings {
|
||||||
// 예전 판의 이동 폴더 하나 → "이동" 버튼으로
|
// 예전 판의 이동 폴더 하나 → "이동" 버튼으로
|
||||||
if (j['moveTargets'] == null && j['moveTargetDir'] is String) MoveTarget('이동', j['moveTargetDir'] as String),
|
if (j['moveTargets'] == null && j['moveTargetDir'] is String) MoveTarget('이동', j['moveTargetDir'] as String),
|
||||||
]
|
]
|
||||||
..youtubeAdSkip = j['youtubeAdSkip'] as bool? ?? true
|
|
||||||
..youtubeAdHide = j['youtubeAdHide'] as bool? ?? true
|
|
||||||
..webPauseOnLeave = j['webPauseOnLeave'] as bool? ?? true
|
|
||||||
..minimizeToTray = j['minimizeToTray'] as bool? ?? true
|
..minimizeToTray = j['minimizeToTray'] as bool? ?? true
|
||||||
..closeAction = const ['background', 'quit', 'ask'].contains(j['closeAction']) ? j['closeAction'] as String : 'background'
|
..closeAction = const ['background', 'quit', 'ask'].contains(j['closeAction']) ? j['closeAction'] as String : 'background'
|
||||||
..showHotkey = j['showHotkey'] as String? ?? 'Ctrl+Shift+X'
|
..showHotkey = j['showHotkey'] as String? ?? 'Ctrl+Shift+X'
|
||||||
|
|
@ -567,12 +540,6 @@ class AppSettings {
|
||||||
? j['screenOrientation'] as String
|
? j['screenOrientation'] as String
|
||||||
: 'auto'
|
: 'auto'
|
||||||
..openFileWindow = j['openFileWindow'] == 'new' ? 'new' : 'same'
|
..openFileWindow = j['openFileWindow'] == 'new' ? 'new' : 'same'
|
||||||
..homeUrl = j['homeUrl'] as String? ?? 'https://www.youtube.com/'
|
|
||||||
..browserEngine = j['browserEngine'] as String? ?? 'edge'
|
|
||||||
..cefWhenElevated = j['cefWhenElevated'] == true
|
|
||||||
..externalBrowser = j['externalBrowser'] as String? ?? 'system'
|
|
||||||
..webTranslate = j['webTranslate'] as bool? ?? false
|
|
||||||
..webJavaScript = j['webJavaScript'] as bool? ?? true
|
|
||||||
..vaultEnabled = j['vaultEnabled'] == true
|
..vaultEnabled = j['vaultEnabled'] == true
|
||||||
..shizukuEnabled = j['shizukuEnabled'] == true
|
..shizukuEnabled = j['shizukuEnabled'] == true
|
||||||
..rootEnabled = j['rootEnabled'] == true
|
..rootEnabled = j['rootEnabled'] == true
|
||||||
|
|
@ -602,7 +569,6 @@ class AppSettings {
|
||||||
: [...defaultImageExtensions]
|
: [...defaultImageExtensions]
|
||||||
..startMenuShortcut = j['startMenuShortcut'] != false
|
..startMenuShortcut = j['startMenuShortcut'] != false
|
||||||
..swipeWrap = j['swipeWrap'] != false
|
..swipeWrap = j['swipeWrap'] != false
|
||||||
..browserSwipeHinted = j['browserSwipeHinted'] == true
|
|
||||||
..cleanupPrechecked = j['cleanupPrechecked'] is List
|
..cleanupPrechecked = j['cleanupPrechecked'] is List
|
||||||
? [for (final x in j['cleanupPrechecked'] as List) '$x']
|
? [for (final x in j['cleanupPrechecked'] as List) '$x']
|
||||||
: ['work', 'download']
|
: ['work', 'download']
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
|
||||||
|
import 'settings_group.dart';
|
||||||
|
|
||||||
|
/// 약속 (설정 A): 환경 설정 > 웹 브라우저 (앱 안 · 외부) - 자기 키 (이름 · 처음 값 · 읽기 · 쓰기) 를 스스로 갖는다.
|
||||||
|
/// 설정 하나를 더할 때는 이 파일만 고친다. 저장 파일의 키 이름은 예전 그대로 (평평한 키)
|
||||||
|
class BrowserSettings implements SettingsGroup {
|
||||||
|
/// 웹 브라우저에서 YouTube 광고 자동 건너뛰기 (건너뛰기 버튼 누르기 · 건너뛸 수 없는 광고는 빨리 감기 · 소리 끄기)
|
||||||
|
bool adSkip = true;
|
||||||
|
|
||||||
|
/// YouTube 페이지의 광고 배너 · 광고 영역 숨기기
|
||||||
|
bool adHide = true;
|
||||||
|
|
||||||
|
/// 웹 브라우저 위에 다른 화면이 올라오거나 다른 화면으로 가면 페이지의 동영상 · 소리를 멈춤 (끄면 계속 들림)
|
||||||
|
bool pauseOnLeave = true;
|
||||||
|
|
||||||
|
String homeUrl = 'https://www.youtube.com/';
|
||||||
|
|
||||||
|
/// 앱 안 브라우저 엔진: 'edge' (WebView2, 기본). 'chrome' 은 내려받아 연결 (준비 중)
|
||||||
|
String engine = 'edge';
|
||||||
|
|
||||||
|
/// 207: 관리자 권한으로 실행 중일 때도 내장 Chrome 엔진을 쓸지 (처음 꺼짐 - 관리자 판단: 보안이 먼저, CEF 는 샌드박스가 꺼져 있다)
|
||||||
|
bool cefWhenElevated = false;
|
||||||
|
|
||||||
|
/// "외부 브라우저로 열기" 에 쓸 브라우저: 'system' · 'chrome' · 'firefox' · 'edge' · 'whale'
|
||||||
|
String external = 'system';
|
||||||
|
|
||||||
|
/// 앱 안 브라우저: 다른 언어로 된 웹 페이지를 화면 언어 ([uiLanguage]) 로 자동 번역 (Google 번역)
|
||||||
|
bool translate = false;
|
||||||
|
|
||||||
|
/// 앱 안 브라우저: 페이지의 JavaScript 실행 (끄면 스크립트 없이 글 · 그림만)
|
||||||
|
bool javaScript = true;
|
||||||
|
|
||||||
|
/// 66-2: 웹 브라우저에서 화면 옮기기 (위쪽 막대를 좌우로) 안내를 한 번 보였는지
|
||||||
|
bool swipeHinted = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void write(Map<String, Object?> j, {required bool plainSecrets}) {
|
||||||
|
j['youtubeAdSkip'] = adSkip;
|
||||||
|
j['youtubeAdHide'] = adHide;
|
||||||
|
j['webPauseOnLeave'] = pauseOnLeave;
|
||||||
|
j['homeUrl'] = homeUrl;
|
||||||
|
j['browserEngine'] = engine;
|
||||||
|
j['cefWhenElevated'] = cefWhenElevated;
|
||||||
|
j['externalBrowser'] = external;
|
||||||
|
j['webTranslate'] = translate;
|
||||||
|
j['webJavaScript'] = javaScript;
|
||||||
|
j['browserSwipeHinted'] = swipeHinted;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void read(Map<String, dynamic> j) {
|
||||||
|
adSkip = j['youtubeAdSkip'] as bool? ?? true;
|
||||||
|
adHide = j['youtubeAdHide'] as bool? ?? true;
|
||||||
|
pauseOnLeave = j['webPauseOnLeave'] as bool? ?? true;
|
||||||
|
homeUrl = j['homeUrl'] as String? ?? 'https://www.youtube.com/';
|
||||||
|
engine = j['browserEngine'] as String? ?? 'edge';
|
||||||
|
cefWhenElevated = j['cefWhenElevated'] == true;
|
||||||
|
external = j['externalBrowser'] as String? ?? 'system';
|
||||||
|
translate = j['webTranslate'] as bool? ?? false;
|
||||||
|
javaScript = j['webJavaScript'] as bool? ?? true;
|
||||||
|
swipeHinted = j['browserSwipeHinted'] == true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -486,7 +486,7 @@ Future<void> setupNetwork(AppController c, String dataDir, {bool second = false}
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
WebProxy.log = c.note;
|
WebProxy.log = c.note;
|
||||||
// 환경 설정 "다른 화면으로 가면 소리 멈춤" 이 꺼져 있으면 일반 브라우저가 뒤에 있어도 DNS 문을 열어 둔다
|
// 환경 설정 "다른 화면으로 가면 소리 멈춤" 이 꺼져 있으면 일반 브라우저가 뒤에 있어도 DNS 문을 열어 둔다
|
||||||
WebProxy.keepPlayingWhenHidden = () => !c.settings.webPauseOnLeave;
|
WebProxy.keepPlayingWhenHidden = () => !c.settings.browser.pauseOnLeave;
|
||||||
try {
|
try {
|
||||||
await WebProxy.apply();
|
await WebProxy.apply();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -499,10 +499,10 @@ Future<void> setupNetwork(AppController c, String dataDir, {bool second = false}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
var last = c.settings.network.config.key;
|
var last = c.settings.network.config.key;
|
||||||
var lastPause = c.settings.webPauseOnLeave;
|
var lastPause = c.settings.browser.pauseOnLeave;
|
||||||
c.addListener(() async {
|
c.addListener(() async {
|
||||||
if (Platform.isAndroid && c.settings.webPauseOnLeave != lastPause) {
|
if (Platform.isAndroid && c.settings.browser.pauseOnLeave != lastPause) {
|
||||||
lastPause = c.settings.webPauseOnLeave;
|
lastPause = c.settings.browser.pauseOnLeave;
|
||||||
await WebProxy.apply();
|
await WebProxy.apply();
|
||||||
}
|
}
|
||||||
final now = c.settings.network.config.key;
|
final now = c.settings.network.config.key;
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
/// 207: Chrome 엔진 [다시 시도] 때마다 새로
|
/// 207: Chrome 엔진 [다시 시도] 때마다 새로
|
||||||
int _cefTry = 0;
|
int _cefTry = 0;
|
||||||
|
|
||||||
late String _url = widget.initialUrl ?? BrowserPage._lastUrl ?? widget.c.settings.homeUrl;
|
late String _url = widget.initialUrl ?? BrowserPage._lastUrl ?? widget.c.settings.browser.homeUrl;
|
||||||
String _title = '';
|
String _title = '';
|
||||||
double _progress = 1;
|
double _progress = 1;
|
||||||
|
|
||||||
|
|
@ -355,7 +355,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
static final _translator = WebPageTranslator();
|
static final _translator = WebPageTranslator();
|
||||||
|
|
||||||
/// 이 화면에서 번역 중인지. 처음엔 설정을 따르고, 도구 막대의 [번역] 버튼으로 바꾼다.
|
/// 이 화면에서 번역 중인지. 처음엔 설정을 따르고, 도구 막대의 [번역] 버튼으로 바꾼다.
|
||||||
late bool _trOn = widget.c.settings.webTranslate;
|
late bool _trOn = widget.c.settings.browser.translate;
|
||||||
|
|
||||||
/// 버튼으로 직접 켰다: 이미 화면 언어로 보이는 페이지도 번역한다
|
/// 버튼으로 직접 켰다: 이미 화면 언어로 보이는 페이지도 번역한다
|
||||||
bool _trForce = false;
|
bool _trForce = false;
|
||||||
|
|
@ -374,12 +374,12 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_applyCookieExport();
|
_applyCookieExport();
|
||||||
final s = widget.c.settings;
|
final s = widget.c.settings;
|
||||||
if (s.webJavaScript != _lastJs) {
|
if (s.browser.javaScript != _lastJs) {
|
||||||
_lastJs = s.webJavaScript;
|
_lastJs = s.browser.javaScript;
|
||||||
_nav?.setJavaScript(_lastJs);
|
_nav?.setJavaScript(_lastJs);
|
||||||
}
|
}
|
||||||
if (s.webTranslate != _lastTranslate) {
|
if (s.browser.translate != _lastTranslate) {
|
||||||
_lastTranslate = s.webTranslate;
|
_lastTranslate = s.browser.translate;
|
||||||
_setTranslate(_lastTranslate, force: false);
|
_setTranslate(_lastTranslate, force: false);
|
||||||
} else if (s.uiLanguage != _lastLang && _trOn) {
|
} else if (s.uiLanguage != _lastLang && _trOn) {
|
||||||
_translateNow(); // 다른 언어로 다시 번역 (스크립트가 앞의 번역을 되돌린 뒤 번역)
|
_translateNow(); // 다른 언어로 다시 번역 (스크립트가 앞의 번역을 되돌린 뒤 번역)
|
||||||
|
|
@ -448,17 +448,17 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
super.initState();
|
super.initState();
|
||||||
BrowserPage._open++;
|
BrowserPage._open++;
|
||||||
final s = widget.c.settings;
|
final s = widget.c.settings;
|
||||||
_lastTranslate = s.webTranslate;
|
_lastTranslate = s.browser.translate;
|
||||||
_lastJs = s.webJavaScript;
|
_lastJs = s.browser.javaScript;
|
||||||
_lastLang = s.uiLanguage;
|
_lastLang = s.uiLanguage;
|
||||||
_applyCookieExport();
|
_applyCookieExport();
|
||||||
widget.c.addListener(_onSettings);
|
widget.c.addListener(_onSettings);
|
||||||
if (_trOn) _translateNow();
|
if (_trOn) _translateNow();
|
||||||
// 66-2: 브라우저 가운데는 페이지가 좌우로 밀리므로 화면 옮기기는 위쪽 막대에서 - 처음 한 번만 알린다.
|
// 66-2: 브라우저 가운데는 페이지가 좌우로 밀리므로 화면 옮기기는 위쪽 막대에서 - 처음 한 번만 알린다.
|
||||||
// 알림 (SnackBar) 은 다른 알림 (다운로드 등) 을 뒤로 밀어 기다리게 하므로 위쪽 막대 아래의 한 줄로
|
// 알림 (SnackBar) 은 다른 알림 (다운로드 등) 을 뒤로 밀어 기다리게 하므로 위쪽 막대 아래의 한 줄로
|
||||||
if (s.swipeNav && !s.browserSwipeHinted) {
|
if (s.swipeNav && !s.browser.swipeHinted) {
|
||||||
_swipeHint = true;
|
_swipeHint = true;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => widget.c.updateSettings((x) => x.browserSwipeHinted = true));
|
WidgetsBinding.instance.addPostFrameCallback((_) => widget.c.updateSettings((x) => x.browser.swipeHinted = true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -580,7 +580,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
void didPushNext() {
|
void didPushNext() {
|
||||||
_visible = false;
|
_visible = false;
|
||||||
// 환경 설정 > 웹 브라우저 > "다른 화면으로 가면 페이지 소리 멈춤" (끄면 다른 화면에서도 계속 들림)
|
// 환경 설정 > 웹 브라우저 > "다른 화면으로 가면 페이지 소리 멈춤" (끄면 다른 화면에서도 계속 들림)
|
||||||
if (widget.c.settings.webPauseOnLeave) _nav?.pauseMedia();
|
if (widget.c.settings.browser.pauseOnLeave) _nav?.pauseMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 설정 화면 등에서 돌아왔다: 바뀐 광고 설정을 지금 페이지에 적용
|
/// 설정 화면 등에서 돌아왔다: 바뀐 광고 설정을 지금 페이지에 적용
|
||||||
|
|
@ -595,13 +595,13 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
void _applyAdSettings() {
|
void _applyAdSettings() {
|
||||||
if (!(Uri.tryParse(_url)?.host ?? '').endsWith('youtube.com')) return;
|
if (!(Uri.tryParse(_url)?.host ?? '').endsWith('youtube.com')) return;
|
||||||
final s = widget.c.settings;
|
final s = widget.c.settings;
|
||||||
_nav?.runScript(youtubeAdScript(skip: s.youtubeAdSkip, hide: s.youtubeAdHide, badge: tr('광고 건너뛰는 중…')));
|
_nav?.runScript(youtubeAdScript(skip: s.browser.adSkip, hide: s.browser.adHide, badge: tr('광고 건너뛰는 중…')));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// MKV 화면 등으로 가며 브라우저 화면이 닫힌다: 살려 둔 웹뷰에서 소리만 나지 않게 멈춘다
|
/// MKV 화면 등으로 가며 브라우저 화면이 닫힌다: 살려 둔 웹뷰에서 소리만 나지 않게 멈춘다
|
||||||
@override
|
@override
|
||||||
void didPop() {
|
void didPop() {
|
||||||
if (widget.c.settings.webPauseOnLeave) _nav?.pauseMedia();
|
if (widget.c.settings.browser.pauseOnLeave) _nav?.pauseMedia();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -670,7 +670,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _openExternal(String url) =>
|
void _openExternal(String url) =>
|
||||||
widget.c.services.shell.openUrl(url, browser: widget.c.settings.externalBrowser);
|
widget.c.services.shell.openUrl(url, browser: widget.c.settings.browser.external);
|
||||||
|
|
||||||
// ───────── 즐겨찾기 ─────────
|
// ───────── 즐겨찾기 ─────────
|
||||||
|
|
||||||
|
|
@ -774,26 +774,26 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _withDnsBlock(widget.viewBuilder?.call(_host, _url) ??
|
child: _withDnsBlock(widget.viewBuilder?.call(_host, _url) ??
|
||||||
// 환경 설정에서 Chrome 을 고르고 엔진이 이번 실행에 준비되어 있으면 Chrome, 아니면 Edge
|
// 환경 설정에서 Chrome 을 고르고 엔진이 이번 실행에 준비되어 있으면 Chrome, 아니면 Edge
|
||||||
(widget.c.settings.browserEngine == 'chrome' && CefRuntime.readyThisRun
|
(widget.c.settings.browser.engine == 'chrome' && CefRuntime.readyThisRun
|
||||||
// 207: 관리자 권한이면 처음 값으로 Chrome 엔진을 시작하지 않는다 (보안) - 이유와 고를 것
|
// 207: 관리자 권한이면 처음 값으로 Chrome 엔진을 시작하지 않는다 (보안) - 이유와 고를 것
|
||||||
? Elevation.elevated && !widget.c.settings.cefWhenElevated
|
? Elevation.elevated && !widget.c.settings.browser.cefWhenElevated
|
||||||
? _CefElevatedBlocked(c: widget.c)
|
? _CefElevatedBlocked(c: widget.c)
|
||||||
// Chrome 엔진은 JavaScript 설정을 만든 뒤에 바꿀 수 없어 바뀌면 새로 만든다
|
// Chrome 엔진은 JavaScript 설정을 만든 뒤에 바꿀 수 없어 바뀌면 새로 만든다
|
||||||
: _CefView(
|
: _CefView(
|
||||||
key: ValueKey('${widget.c.settings.webJavaScript}|$_cefTry'),
|
key: ValueKey('${widget.c.settings.browser.javaScript}|$_cefTry'),
|
||||||
host: _host,
|
host: _host,
|
||||||
initialUrl: _url,
|
initialUrl: _url,
|
||||||
dataDir: widget.c.settings.webViewDataDir,
|
dataDir: widget.c.settings.webViewDataDir,
|
||||||
javaScript: widget.c.settings.webJavaScript,
|
javaScript: widget.c.settings.browser.javaScript,
|
||||||
// 207: 시작하지 못하면 사용자가 바로 고를 수 있게
|
// 207: 시작하지 못하면 사용자가 바로 고를 수 있게
|
||||||
onUseEdge: () => widget.c.updateSettings((x) => x.browserEngine = 'edge'),
|
onUseEdge: () => widget.c.updateSettings((x) => x.browser.engine = 'edge'),
|
||||||
onRetry: () => setState(() => _cefTry++),
|
onRetry: () => setState(() => _cefTry++),
|
||||||
cefWhenElevated: widget.c.settings.cefWhenElevated)
|
cefWhenElevated: widget.c.settings.browser.cefWhenElevated)
|
||||||
: _EdgeView(
|
: _EdgeView(
|
||||||
host: _host,
|
host: _host,
|
||||||
initialUrl: _url,
|
initialUrl: _url,
|
||||||
dataDir: widget.c.settings.webViewDataDir,
|
dataDir: widget.c.settings.webViewDataDir,
|
||||||
javaScript: widget.c.settings.webJavaScript))),
|
javaScript: widget.c.settings.browser.javaScript))),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|
@ -852,7 +852,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
_progress < 1
|
_progress < 1
|
||||||
? btn(Icons.close, tr('중지'), () => _nav?.stop())
|
? btn(Icons.close, tr('중지'), () => _nav?.stop())
|
||||||
: btn(Icons.refresh, keyHint(tr('새로고침'), 'F5'), () => _nav?.reload()),
|
: btn(Icons.refresh, keyHint(tr('새로고침'), 'F5'), () => _nav?.reload()),
|
||||||
if (!compact) btn(Icons.home_outlined, tr('홈'), () => _go(widget.c.settings.homeUrl)),
|
if (!compact) btn(Icons.home_outlined, tr('홈'), () => _go(widget.c.settings.browser.homeUrl)),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
|
|
@ -940,7 +940,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
||||||
tooltip: tr('더 보기'),
|
tooltip: tr('더 보기'),
|
||||||
icon: const Icon(Icons.more_vert, size: 20),
|
icon: const Icon(Icons.more_vert, size: 20),
|
||||||
onSelected: (v) => switch (v) {
|
onSelected: (v) => switch (v) {
|
||||||
'home' => _go(widget.c.settings.homeUrl),
|
'home' => _go(widget.c.settings.browser.homeUrl),
|
||||||
'translate' => _setTranslate(!_trOn, force: true),
|
'translate' => _setTranslate(!_trOn, force: true),
|
||||||
'external' => _openExternal(_url),
|
'external' => _openExternal(_url),
|
||||||
'work' => _toggleWork(),
|
'work' => _toggleWork(),
|
||||||
|
|
@ -1349,7 +1349,7 @@ class _CefElevatedBlocked extends StatelessWidget {
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Wrap(spacing: 12, runSpacing: 8, alignment: WrapAlignment.center, children: [
|
Wrap(spacing: 12, runSpacing: 8, alignment: WrapAlignment.center, children: [
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () => c.updateSettings((x) => x.browserEngine = 'edge'),
|
onPressed: () => c.updateSettings((x) => x.browser.engine = 'edge'),
|
||||||
child: Text(tr('Edge 엔진으로 바꾸기')),
|
child: Text(tr('Edge 엔진으로 바꾸기')),
|
||||||
),
|
),
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ Future<bool> chooseChromeEngine(BuildContext context, AppController c) async {
|
||||||
);
|
);
|
||||||
if (installed != true || !context.mounted) return false;
|
if (installed != true || !context.mounted) return false;
|
||||||
}
|
}
|
||||||
await c.updateSettings((s) => s.browserEngine = 'chrome');
|
await c.updateSettings((s) => s.browser.engine = 'chrome');
|
||||||
if (!context.mounted) return true;
|
if (!context.mounted) return true;
|
||||||
if (!CefRuntime.readyThisRun) {
|
if (!CefRuntime.readyThisRun) {
|
||||||
final restart = await showDialog<bool>(
|
final restart = await showDialog<bool>(
|
||||||
|
|
@ -147,7 +147,7 @@ Future<bool> confirmCefWhenElevated(BuildContext context, AppController c) async
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (ok != true) return false;
|
if (ok != true) return false;
|
||||||
await c.updateSettings((x) => x.cefWhenElevated = true);
|
await c.updateSettings((x) => x.browser.cefWhenElevated = true);
|
||||||
c.note(tr('관리자 권한일 때도 Chrome 엔진 쓰기를 켰습니다 (권장하지 않음).'));
|
c.note(tr('관리자 권한일 때도 Chrome 엔진 쓰기를 켰습니다 (권장하지 않음).'));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ class _DnsSettingsState extends State<DnsSettings> {
|
||||||
if (!desk) tr('· Android 의 yt-dlp 새 버전 확인 (다운로드 도구 안에서 따로 연결)'),
|
if (!desk) tr('· Android 의 yt-dlp 새 버전 확인 (다운로드 도구 안에서 따로 연결)'),
|
||||||
].join('\n')),
|
].join('\n')),
|
||||||
),
|
),
|
||||||
if (desk && s.browserEngine == 'chrome' && CefRuntime.readyThisRun)
|
if (desk && s.browser.engine == 'chrome' && CefRuntime.readyThisRun)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||||
child: Text(tr('Chrome 엔진은 앱을 다시 시작하면 바뀐 설정을 씁니다.'), style: TextStyle(fontSize: 12, color: JjColors.textDim)),
|
child: Text(tr('Chrome 엔진은 앱을 다시 시작하면 바뀐 설정을 씁니다.'), style: TextStyle(fontSize: 12, color: JjColors.textDim)),
|
||||||
|
|
|
||||||
|
|
@ -32,29 +32,29 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final s = c.settings;
|
final s = c.settings;
|
||||||
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [
|
return Column(crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [
|
||||||
settingsTextTile(tr('홈 주소'), s.homeUrl, tr('웹 브라우저의 시작 · 🏠 주소'),
|
settingsTextTile(tr('홈 주소'), s.browser.homeUrl, tr('웹 브라우저의 시작 · 🏠 주소'),
|
||||||
(v) => c.updateSettings((x) => x.homeUrl = v.trim().isEmpty ? 'https://www.youtube.com/' : v.trim())),
|
(v) => c.updateSettings((x) => x.browser.homeUrl = v.trim().isEmpty ? 'https://www.youtube.com/' : v.trim())),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: s.youtubeAdSkip,
|
value: s.browser.adSkip,
|
||||||
onChanged: (v) => c.updateSettings((x) => x.youtubeAdSkip = v),
|
onChanged: (v) => c.updateSettings((x) => x.browser.adSkip = v),
|
||||||
title: Text(tr('YouTube 광고 자동 건너뛰기')),
|
title: Text(tr('YouTube 광고 자동 건너뛰기')),
|
||||||
subtitle: Text(tr('앱 안 웹 브라우저: [건너뛰기] 를 대신 누르고, 건너뛸 수 없는 광고는 소리를 끄고 빨리 넘깁니다 ' '(넘기는 동안 화면 위쪽에 "광고 건너뛰는 중…")')),
|
subtitle: Text(tr('앱 안 웹 브라우저: [건너뛰기] 를 대신 누르고, 건너뛸 수 없는 광고는 소리를 끄고 빨리 넘깁니다 ' '(넘기는 동안 화면 위쪽에 "광고 건너뛰는 중…")')),
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: s.youtubeAdHide,
|
value: s.browser.adHide,
|
||||||
onChanged: (v) => c.updateSettings((x) => x.youtubeAdHide = v),
|
onChanged: (v) => c.updateSettings((x) => x.browser.adHide = v),
|
||||||
title: Text(tr('YouTube 광고 배너 숨기기')),
|
title: Text(tr('YouTube 광고 배너 숨기기')),
|
||||||
subtitle: Text(tr('목록 · 영상 옆 · 영상 위에 나오는 광고 영역을 감춥니다')),
|
subtitle: Text(tr('목록 · 영상 옆 · 영상 위에 나오는 광고 영역을 감춥니다')),
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: s.webPauseOnLeave,
|
value: s.browser.pauseOnLeave,
|
||||||
onChanged: (v) => c.updateSettings((x) => x.webPauseOnLeave = v),
|
onChanged: (v) => c.updateSettings((x) => x.browser.pauseOnLeave = v),
|
||||||
title: Text(tr('다른 화면으로 가면 페이지 소리 멈춤')),
|
title: Text(tr('다른 화면으로 가면 페이지 소리 멈춤')),
|
||||||
subtitle: Text(tr('끄면 환경 설정 · 다운로드 목록 · 다른 화면을 보는 동안에도 웹 페이지의 소리가 계속 들립니다')),
|
subtitle: Text(tr('끄면 환경 설정 · 다운로드 목록 · 다른 화면을 보는 동안에도 웹 페이지의 소리가 계속 들립니다')),
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: s.webTranslate,
|
value: s.browser.translate,
|
||||||
onChanged: (v) => c.updateSettings((x) => x.webTranslate = v),
|
onChanged: (v) => c.updateSettings((x) => x.browser.translate = v),
|
||||||
title: Text(tr('웹 페이지 자동 번역')),
|
title: Text(tr('웹 페이지 자동 번역')),
|
||||||
subtitle: Text(trf(
|
subtitle: Text(trf(
|
||||||
'다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. '
|
'다른 언어로 된 페이지를 화면 언어 ({0}) 로 번역합니다. 페이지의 글을 Google 번역으로 보냅니다. '
|
||||||
|
|
@ -62,8 +62,8 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
[I18nController.nativeName(s.uiLanguage)])),
|
[I18nController.nativeName(s.uiLanguage)])),
|
||||||
),
|
),
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: s.webJavaScript,
|
value: s.browser.javaScript,
|
||||||
onChanged: (v) => c.updateSettings((x) => x.webJavaScript = v),
|
onChanged: (v) => c.updateSettings((x) => x.browser.javaScript = v),
|
||||||
title: Text(tr('JavaScript 사용')),
|
title: Text(tr('JavaScript 사용')),
|
||||||
subtitle: Text(desk
|
subtitle: Text(desk
|
||||||
? tr('끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). '
|
? tr('끄면 페이지의 스크립트를 실행하지 않습니다 (광고 · 추적이 줄지만 YouTube 처럼 스크립트로 만든 사이트는 보이지 않음). '
|
||||||
|
|
@ -75,10 +75,10 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
SettingTile(
|
SettingTile(
|
||||||
title: Text(tr('브라우저 엔진 (앱 안)')),
|
title: Text(tr('브라우저 엔진 (앱 안)')),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
trf('Edge: Windows 에 들어 있어 따로 설치하지 않습니다. 여기서 YouTube 에 로그인하면 다운로드에도 그 로그인이 쓰입니다.\n' 'Chrome: 고르면 약 {0}MB 를 내려받고 다시 시작한 뒤 쓸 수 있습니다 ' '(Google 로그인은 막힐 수 있음).' '{1}' '{2}', [CefRuntime.approxDownloadMb, CefRuntime.installed ? trf(' 지금 설치됨 ({0}MB)', [CefRuntime.installedMb()]) : '', s.browserEngine == 'chrome' && !CefRuntime.readyThisRun ? tr(' · 다시 시작해야 Chrome 으로 바뀝니다') : ''])),
|
trf('Edge: Windows 에 들어 있어 따로 설치하지 않습니다. 여기서 YouTube 에 로그인하면 다운로드에도 그 로그인이 쓰입니다.\n' 'Chrome: 고르면 약 {0}MB 를 내려받고 다시 시작한 뒤 쓸 수 있습니다 ' '(Google 로그인은 막힐 수 있음).' '{1}' '{2}', [CefRuntime.approxDownloadMb, CefRuntime.installed ? trf(' 지금 설치됨 ({0}MB)', [CefRuntime.installedMb()]) : '', s.browser.engine == 'chrome' && !CefRuntime.readyThisRun ? tr(' · 다시 시작해야 Chrome 으로 바뀝니다') : ''])),
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
trailing: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
if (s.browserEngine == 'edge' && CefRuntime.installed && !CefRuntime.readyThisRun)
|
if (s.browser.engine == 'edge' && CefRuntime.installed && !CefRuntime.readyThisRun)
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: tr('내려받은 Chrome 엔진 지우기'),
|
tooltip: tr('내려받은 Chrome 엔진 지우기'),
|
||||||
icon: Icon(Icons.delete_outline, color: JjColors.textDim),
|
icon: Icon(Icons.delete_outline, color: JjColors.textDim),
|
||||||
|
|
@ -89,7 +89,7 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
DropdownButton<String>(
|
DropdownButton<String>(
|
||||||
value: s.browserEngine,
|
value: s.browser.engine,
|
||||||
items: [
|
items: [
|
||||||
DropdownMenuItem(value: 'edge', child: Text(tr('Edge (내장)'))),
|
DropdownMenuItem(value: 'edge', child: Text(tr('Edge (내장)'))),
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
|
|
@ -101,7 +101,7 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
await chooseChromeEngine(context, c);
|
await chooseChromeEngine(context, c);
|
||||||
if (mounted) setState(() {});
|
if (mounted) setState(() {});
|
||||||
} else {
|
} else {
|
||||||
await c.updateSettings((x) => x.browserEngine = 'edge');
|
await c.updateSettings((x) => x.browser.engine = 'edge');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
@ -109,12 +109,12 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
),
|
),
|
||||||
// 207: 관리자 권한일 때도 Chrome 엔진 쓰기 (처음 꺼짐, 켤 때 경고)
|
// 207: 관리자 권한일 때도 Chrome 엔진 쓰기 (처음 꺼짐, 켤 때 경고)
|
||||||
SwitchListTile(
|
SwitchListTile(
|
||||||
value: s.cefWhenElevated,
|
value: s.browser.cefWhenElevated,
|
||||||
onChanged: (v) async {
|
onChanged: (v) async {
|
||||||
if (v) {
|
if (v) {
|
||||||
await confirmCefWhenElevated(context, c);
|
await confirmCefWhenElevated(context, c);
|
||||||
} else {
|
} else {
|
||||||
await c.updateSettings((x) => x.cefWhenElevated = false);
|
await c.updateSettings((x) => x.browser.cefWhenElevated = false);
|
||||||
}
|
}
|
||||||
if (mounted) setState(() {});
|
if (mounted) setState(() {});
|
||||||
},
|
},
|
||||||
|
|
@ -127,10 +127,10 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
title: Text(tr('외부 브라우저')),
|
title: Text(tr('외부 브라우저')),
|
||||||
subtitle: Text(tr('"외부 브라우저로 열기" 에 쓸 브라우저 (이 PC 에 설치된 것)')),
|
subtitle: Text(tr('"외부 브라우저로 열기" 에 쓸 브라우저 (이 PC 에 설치된 것)')),
|
||||||
trailing: DropdownButton<String>(
|
trailing: DropdownButton<String>(
|
||||||
value: s.externalBrowser,
|
value: s.browser.external,
|
||||||
items: [
|
items: [
|
||||||
DropdownMenuItem(value: 'system', child: Text(tr('Windows 기본 브라우저'))),
|
DropdownMenuItem(value: 'system', child: Text(tr('Windows 기본 브라우저'))),
|
||||||
for (final b in {...c.services.shell.installedBrowsers(), if (s.externalBrowser != 'system') s.externalBrowser})
|
for (final b in {...c.services.shell.installedBrowsers(), if (s.browser.external != 'system') s.browser.external})
|
||||||
DropdownMenuItem(value: b, child: Text(switch (b) {
|
DropdownMenuItem(value: b, child: Text(switch (b) {
|
||||||
'chrome' => 'Chrome',
|
'chrome' => 'Chrome',
|
||||||
'firefox' => 'Firefox',
|
'firefox' => 'Firefox',
|
||||||
|
|
@ -139,7 +139,7 @@ class _BrowserSettingsSectionState extends State<BrowserSettingsSection> {
|
||||||
_ => b,
|
_ => b,
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
onChanged: (v) => c.updateSettings((x) => x.externalBrowser = v!),
|
onChanged: (v) => c.updateSettings((x) => x.browser.external = v!),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -633,7 +633,7 @@ void main() {
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.textContaining('위쪽 막대를 좌우로 밀면'), findsOneWidget);
|
expect(find.textContaining('위쪽 막대를 좌우로 밀면'), findsOneWidget);
|
||||||
expect(find.byType(SnackBar), findsNothing, reason: '알림 (다운로드 등) 을 뒤로 밀지 않게 알림이 아니라 한 줄');
|
expect(find.byType(SnackBar), findsNothing, reason: '알림 (다운로드 등) 을 뒤로 밀지 않게 알림이 아니라 한 줄');
|
||||||
expect(c.settings.browserSwipeHinted, isTrue);
|
expect(c.settings.browser.swipeHinted, isTrue);
|
||||||
await tester.tap(find.byTooltip('안내 닫기'));
|
await tester.tap(find.byTooltip('안내 닫기'));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.textContaining('위쪽 막대를 좌우로 밀면'), findsNothing);
|
expect(find.textContaining('위쪽 막대를 좌우로 밀면'), findsNothing);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ void main() {
|
||||||
dir = Directory.systemTemp.createTempSync('jj_cef_elev_');
|
dir = Directory.systemTemp.createTempSync('jj_cef_elev_');
|
||||||
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
||||||
c.settings
|
c.settings
|
||||||
..browserEngine = 'chrome'
|
..browser.engine = 'chrome'
|
||||||
..webViewDataDir = p.join(dir.path, 'webview');
|
..webViewDataDir = p.join(dir.path, 'webview');
|
||||||
bm = BookmarksController(p.join(dir.path, 'bookmarks.json'));
|
bm = BookmarksController(p.join(dir.path, 'bookmarks.json'));
|
||||||
CefRuntime.readyThisRun = true;
|
CefRuntime.readyThisRun = true;
|
||||||
|
|
@ -52,7 +52,7 @@ void main() {
|
||||||
expect(find.text('관리자 권한으로 실행 중이라 Chrome 엔진을 쓸 수 없습니다 (보안)'), findsOneWidget);
|
expect(find.text('관리자 권한으로 실행 중이라 Chrome 엔진을 쓸 수 없습니다 (보안)'), findsOneWidget);
|
||||||
await t.tap(find.text('Edge 엔진으로 바꾸기'));
|
await t.tap(find.text('Edge 엔진으로 바꾸기'));
|
||||||
await t.pump();
|
await t.pump();
|
||||||
expect(c.settings.browserEngine, 'edge');
|
expect(c.settings.browser.engine, 'edge');
|
||||||
await t.pumpWidget(const SizedBox());
|
await t.pumpWidget(const SizedBox());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -64,12 +64,12 @@ void main() {
|
||||||
expect(find.textContaining('샌드박스 (웹 페이지를 가두는 보호) 가 꺼져'), findsOneWidget);
|
expect(find.textContaining('샌드박스 (웹 페이지를 가두는 보호) 가 꺼져'), findsOneWidget);
|
||||||
await t.tap(find.text('취소'));
|
await t.tap(find.text('취소'));
|
||||||
await t.pumpAndSettle();
|
await t.pumpAndSettle();
|
||||||
expect(c.settings.cefWhenElevated, isFalse);
|
expect(c.settings.browser.cefWhenElevated, isFalse);
|
||||||
await t.tap(find.text('그래도 Chrome 엔진 쓰기…'));
|
await t.tap(find.text('그래도 Chrome 엔진 쓰기…'));
|
||||||
await t.pumpAndSettle();
|
await t.pumpAndSettle();
|
||||||
await t.tap(find.text('그래도 쓰기'));
|
await t.tap(find.text('그래도 쓰기'));
|
||||||
await t.pumpAndSettle();
|
await t.pumpAndSettle();
|
||||||
expect(c.settings.cefWhenElevated, isTrue);
|
expect(c.settings.browser.cefWhenElevated, isTrue);
|
||||||
expect(find.text('관리자 권한으로 실행 중이라 Chrome 엔진을 쓸 수 없습니다 (보안)'), findsNothing);
|
expect(find.text('관리자 권한으로 실행 중이라 Chrome 엔진을 쓸 수 없습니다 (보안)'), findsNothing);
|
||||||
await t.pumpWidget(const SizedBox());
|
await t.pumpWidget(const SizedBox());
|
||||||
});
|
});
|
||||||
|
|
@ -86,13 +86,13 @@ void main() {
|
||||||
expect(find.text('다시 시도'), findsOneWidget);
|
expect(find.text('다시 시도'), findsOneWidget);
|
||||||
await t.tap(find.text('Edge 엔진으로 바꾸기'));
|
await t.tap(find.text('Edge 엔진으로 바꾸기'));
|
||||||
await t.pump();
|
await t.pump();
|
||||||
expect(c.settings.browserEngine, 'edge');
|
expect(c.settings.browser.engine, 'edge');
|
||||||
await t.pumpWidget(const SizedBox());
|
await t.pumpWidget(const SizedBox());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('설정: 관리자 권한일 때 Chrome 엔진 쓰기는 처음 꺼짐 · 저장 · 틀린 값은 꺼짐', () {
|
test('설정: 관리자 권한일 때 Chrome 엔진 쓰기는 처음 꺼짐 · 저장 · 틀린 값은 꺼짐', () {
|
||||||
expect(AppSettings().cefWhenElevated, isFalse);
|
expect(AppSettings().browser.cefWhenElevated, isFalse);
|
||||||
expect(AppSettings.fromJson((AppSettings()..cefWhenElevated = true).toJson()).cefWhenElevated, isTrue);
|
expect(AppSettings.fromJson((AppSettings()..browser.cefWhenElevated = true).toJson()).browser.cefWhenElevated, isTrue);
|
||||||
expect(AppSettings.fromJson({'cefWhenElevated': 'yes'}).cefWhenElevated, isFalse);
|
expect(AppSettings.fromJson({'cefWhenElevated': 'yes'}).browser.cefWhenElevated, isFalse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,8 +175,8 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('66-2: 웹 브라우저 화면 옮기기 안내는 한 번만 (설정에 남음)', () {
|
test('66-2: 웹 브라우저 화면 옮기기 안내는 한 번만 (설정에 남음)', () {
|
||||||
expect(AppSettings().browserSwipeHinted, isFalse);
|
expect(AppSettings().browser.swipeHinted, isFalse);
|
||||||
expect(AppSettings.fromJson((AppSettings()..browserSwipeHinted = true).toJson()).browserSwipeHinted, isTrue);
|
expect(AppSettings.fromJson((AppSettings()..browser.swipeHinted = true).toJson()).browser.swipeHinted, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('174: MKV 화면이 맨 처음 화면이 아니어도 (홈 화면 = 탐색기 등) MKV 아이콘이 "지금 여기"', (tester) async {
|
testWidgets('174: MKV 화면이 맨 처음 화면이 아니어도 (홈 화면 = 탐색기 등) MKV 아이콘이 "지금 여기"', (tester) async {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ void main() {
|
||||||
expect(s.tripleTapAction, 'move');
|
expect(s.tripleTapAction, 'move');
|
||||||
expect(s.davExternalOpen, 'ask');
|
expect(s.davExternalOpen, 'ask');
|
||||||
// 이미 있던 설정의 기본값도 결정대로
|
// 이미 있던 설정의 기본값도 결정대로
|
||||||
expect([s.webPauseOnLeave, s.autoCheckUpdates, s.youtubeAdSkip, s.youtubeAdHide, s.runInBackground, s.swipeNav],
|
expect([s.browser.pauseOnLeave, s.autoCheckUpdates, s.browser.adSkip, s.browser.adHide, s.runInBackground, s.swipeNav],
|
||||||
everyElement(isTrue));
|
everyElement(isTrue));
|
||||||
// 탐색기 처음 배치 = 두 창 (규칙 7 · 관리자 결정 3, 10/22)
|
// 탐색기 처음 배치 = 두 창 (규칙 7 · 관리자 결정 3, 10/22)
|
||||||
expect([s.copy.liveSyncOnStart, s.copy.liveSyncIntervalSec, s.explorer.layout, s.reader.zipComic], ['auto', 30, 'dual', false]);
|
expect([s.copy.liveSyncOnStart, s.copy.liveSyncIntervalSec, s.explorer.layout, s.reader.zipComic], ['auto', 30, 'dual', false]);
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ void main() {
|
||||||
// 닫으면 설정을 바꿔도 (같은 상황) 다시 뜨지 않는다
|
// 닫으면 설정을 바꿔도 (같은 상황) 다시 뜨지 않는다
|
||||||
await tester.tap(find.text('닫기'));
|
await tester.tap(find.text('닫기'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.runAsync(() => c.updateSettings((s) => s.homeUrl = 'https://x.example'));
|
await tester.runAsync(() => c.updateSettings((s) => s.browser.homeUrl = 'https://x.example'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('새 비밀번호는 아직 저장되지 않았습니다. 앱을 끄면 예전 비밀번호로 돌아갑니다.'), findsNothing);
|
expect(find.text('새 비밀번호는 아직 저장되지 않았습니다. 앱을 끄면 예전 비밀번호로 돌아갑니다.'), findsNothing);
|
||||||
DavRegistry.configure([]);
|
DavRegistry.configure([]);
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ void main() {
|
||||||
// 예전 판: 모르는 항목을 지우고 저장
|
// 예전 판: 모르는 항목을 지우고 저장
|
||||||
File(file).writeAsStringSync(jsonEncode({'homeUrl': 'https://old.example'}));
|
File(file).writeAsStringSync(jsonEncode({'homeUrl': 'https://old.example'}));
|
||||||
final s = await store().load();
|
final s = await store().load();
|
||||||
expect(s.homeUrl, 'https://old.example');
|
expect(s.browser.homeUrl, 'https://old.example');
|
||||||
expect(s.webdavServers.single.url, 'https://nas');
|
expect(s.webdavServers.single.url, 'https://nas');
|
||||||
expect(s.webdavServers.single.password, 'pw');
|
expect(s.webdavServers.single.password, 'pw');
|
||||||
expect(s.openSubtitlesKey, 'KEY-2');
|
expect(s.openSubtitlesKey, 'KEY-2');
|
||||||
|
|
@ -104,7 +104,7 @@ void main() {
|
||||||
final st = store();
|
final st = store();
|
||||||
final s = await st.load();
|
final s = await st.load();
|
||||||
expect(s.openSubtitlesKey, 'KEY-3');
|
expect(s.openSubtitlesKey, 'KEY-3');
|
||||||
await st.save(s..homeUrl = 'https://x.example');
|
await st.save(s..browser.homeUrl = 'https://x.example');
|
||||||
expect(text(file), contains('KEY-3'));
|
expect(text(file), contains('KEY-3'));
|
||||||
expect(text(file), contains('plain'));
|
expect(text(file), contains('plain'));
|
||||||
expect(secrets.values, {'dav.pw.d1': 'kept'});
|
expect(secrets.values, {'dav.pw.d1': 'kept'});
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ void main() {
|
||||||
|
|
||||||
test('저장: 임시 파일에 쓴 뒤 바꿔치기 · 직전 정상본은 .bak · 남는 임시 파일 없음', () async {
|
test('저장: 임시 파일에 쓴 뒤 바꿔치기 · 직전 정상본은 .bak · 남는 임시 파일 없음', () async {
|
||||||
final store = SettingsStore(file);
|
final store = SettingsStore(file);
|
||||||
await store.save(AppSettings()..homeUrl = 'https://a.example');
|
await store.save(AppSettings()..browser.homeUrl = 'https://a.example');
|
||||||
await store.save(AppSettings()..homeUrl = 'https://b.example');
|
await store.save(AppSettings()..browser.homeUrl = 'https://b.example');
|
||||||
expect((await SettingsStore(file).load()).homeUrl, 'https://b.example');
|
expect((await SettingsStore(file).load()).browser.homeUrl, 'https://b.example');
|
||||||
expect(File('$file.bak').readAsStringSync(), contains('https://a.example'));
|
expect(File('$file.bak').readAsStringSync(), contains('https://a.example'));
|
||||||
expect(File('$file.tmp').existsSync(), isFalse);
|
expect(File('$file.tmp').existsSync(), isFalse);
|
||||||
// 여러 번 빠르게 저장해도 마지막 것이 남는다 (순서대로)
|
// 여러 번 빠르게 저장해도 마지막 것이 남는다 (순서대로)
|
||||||
await Future.wait([for (var i = 0; i < 5; i++) store.save(AppSettings()..homeUrl = 'https://$i.example')]);
|
await Future.wait([for (var i = 0; i < 5; i++) store.save(AppSettings()..browser.homeUrl = 'https://$i.example')]);
|
||||||
expect((await SettingsStore(file).load()).homeUrl, 'https://4.example');
|
expect((await SettingsStore(file).load()).browser.homeUrl, 'https://4.example');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('관리자 지시 (10/21): 보통 저장은 이 판이 만드는 꼴의 열쇠만 지운다 - 모르는 꼴 (새 판의 dav.token.x 등) 은 남김 · 지운 서버의 아는 열쇠만', () async {
|
test('관리자 지시 (10/21): 보통 저장은 이 판이 만드는 꼴의 열쇠만 지운다 - 모르는 꼴 (새 판의 dav.token.x 등) 은 남김 · 지운 서버의 아는 열쇠만', () async {
|
||||||
|
|
@ -98,11 +98,11 @@ void main() {
|
||||||
|
|
||||||
test('깨진 설정 파일: 백업으로 되살리고, 깨진 파일은 보관 · 알림 정보', () async {
|
test('깨진 설정 파일: 백업으로 되살리고, 깨진 파일은 보관 · 알림 정보', () async {
|
||||||
final store = SettingsStore(file);
|
final store = SettingsStore(file);
|
||||||
await store.save(AppSettings()..homeUrl = 'https://good.example');
|
await store.save(AppSettings()..browser.homeUrl = 'https://good.example');
|
||||||
await store.save(AppSettings()..homeUrl = 'https://good2.example'); // .bak = good
|
await store.save(AppSettings()..browser.homeUrl = 'https://good2.example'); // .bak = good
|
||||||
File(file).writeAsStringSync('{"homeUrl": "https://half'); // 반쯤 쓰다 끊긴 파일
|
File(file).writeAsStringSync('{"homeUrl": "https://half'); // 반쯤 쓰다 끊긴 파일
|
||||||
final s = await store.load();
|
final s = await store.load();
|
||||||
expect(s.homeUrl, 'https://good.example');
|
expect(s.browser.homeUrl, 'https://good.example');
|
||||||
expect(store.problem!.restoredFromBackup, isTrue);
|
expect(store.problem!.restoredFromBackup, isTrue);
|
||||||
final kept = store.problem!.brokenCopy!;
|
final kept = store.problem!.brokenCopy!;
|
||||||
expect(File(kept).readAsStringSync(), '{"homeUrl": "https://half');
|
expect(File(kept).readAsStringSync(), '{"homeUrl": "https://half');
|
||||||
|
|
@ -115,7 +115,7 @@ void main() {
|
||||||
File(file).writeAsStringSync('not json');
|
File(file).writeAsStringSync('not json');
|
||||||
final store = SettingsStore(file);
|
final store = SettingsStore(file);
|
||||||
final s = await store.load();
|
final s = await store.load();
|
||||||
expect(s.homeUrl, AppSettings().homeUrl);
|
expect(s.browser.homeUrl, AppSettings().browser.homeUrl);
|
||||||
expect(store.problem!.restoredFromBackup, isFalse);
|
expect(store.problem!.restoredFromBackup, isFalse);
|
||||||
expect(File(store.problem!.brokenCopy!).readAsStringSync(), 'not json');
|
expect(File(store.problem!.brokenCopy!).readAsStringSync(), 'not json');
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ void main() {
|
||||||
File(file).deleteSync();
|
File(file).deleteSync();
|
||||||
File('$file.bak').writeAsStringSync('{"homeUrl": "https://bak.example"}');
|
File('$file.bak').writeAsStringSync('{"homeUrl": "https://bak.example"}');
|
||||||
final again = SettingsStore(file);
|
final again = SettingsStore(file);
|
||||||
expect((await again.load()).homeUrl, 'https://bak.example');
|
expect((await again.load()).browser.homeUrl, 'https://bak.example');
|
||||||
expect(again.problem!.restoredFromBackup, isTrue);
|
expect(again.problem!.restoredFromBackup, isTrue);
|
||||||
// 정상이면 알림 없음
|
// 정상이면 알림 없음
|
||||||
await again.save(AppSettings());
|
await again.save(AppSettings());
|
||||||
|
|
|
||||||
|
|
@ -96,13 +96,13 @@ void main() {
|
||||||
|
|
||||||
test('설정: 번역 (기본 끔) · JavaScript (기본 켬) 저장 · 읽기', () {
|
test('설정: 번역 (기본 끔) · JavaScript (기본 켬) 저장 · 읽기', () {
|
||||||
final d = AppSettings();
|
final d = AppSettings();
|
||||||
expect(d.webTranslate, isFalse);
|
expect(d.browser.translate, isFalse);
|
||||||
expect(d.webJavaScript, isTrue);
|
expect(d.browser.javaScript, isTrue);
|
||||||
final s = AppSettings.fromJson(jsonDecode(jsonEncode((AppSettings()
|
final s = AppSettings.fromJson(jsonDecode(jsonEncode((AppSettings()
|
||||||
..webTranslate = true
|
..browser.translate = true
|
||||||
..webJavaScript = false)
|
..browser.javaScript = false)
|
||||||
.toJson())) as Map<String, dynamic>);
|
.toJson())) as Map<String, dynamic>);
|
||||||
expect(s.webTranslate, isTrue);
|
expect(s.browser.translate, isTrue);
|
||||||
expect(s.webJavaScript, isFalse);
|
expect(s.browser.javaScript, isFalse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,12 @@ console.log(JSON.stringify(r));
|
||||||
void main() {
|
void main() {
|
||||||
test('설정: 광고 건너뛰기 · 숨기기 기본 켜짐, 저장', () {
|
test('설정: 광고 건너뛰기 · 숨기기 기본 켜짐, 저장', () {
|
||||||
final s = AppSettings();
|
final s = AppSettings();
|
||||||
expect([s.youtubeAdSkip, s.youtubeAdHide], [true, true]);
|
expect([s.browser.adSkip, s.browser.adHide], [true, true]);
|
||||||
s
|
s
|
||||||
..youtubeAdSkip = false
|
..browser.adSkip = false
|
||||||
..youtubeAdHide = false;
|
..browser.adHide = false;
|
||||||
final back = AppSettings.fromJson(s.toJson());
|
final back = AppSettings.fromJson(s.toJson());
|
||||||
expect([back.youtubeAdSkip, back.youtubeAdHide], [false, false]);
|
expect([back.browser.adSkip, back.browser.adHide], [false, false]);
|
||||||
expect(youtubeAdScript(skip: false, hide: true), contains('{skip: false, hide: true}'));
|
expect(youtubeAdScript(skip: false, hide: true), contains('{skip: false, hide: true}'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue