M4 1묶음: "Android 인지" 전역 함수 둘 (앱 관리자 단추 · 볼륨 키) 을 PlatformInfo 로 + WakeLock (보기 화면 화면 켜 두기) 을 주입으로
- ExplorerButton.explorerValues · defaultOrder · fromSettings 는 android 를 받는다 (부르는 쪽이 c.services.platform.isAndroid) - 예전 바꿀 수 있는 전역 함수 appsButtonSupported - 보기 화면 볼륨 키는 c.services.platform.isAndroid (예전 ReaderPage.volumeKeysSupported) - PlatformServices.keepScreenOn (처음 값 WakelockPlus - 예전 ReaderPage.keepScreenOn) - 시험: app_manager · explorer_archive_trash · explorer_conflict · reader · reader_more 는 주입으로 (2회 통과). 볼륨 키 시험은 Android 조립과 Windows 조립 두 화면으로 - 사용자가 보게 될 동작: 없음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>modular
parent
39b76e2423
commit
23cc9f6e5b
|
|
@ -32,6 +32,7 @@ PlatformServices shotServices() {
|
||||||
vault: r.vault,
|
vault: r.vault,
|
||||||
privileged: r.privileged,
|
privileged: r.privileged,
|
||||||
isMetered: r.isMetered,
|
isMetered: r.isMetered,
|
||||||
|
keepScreenOn: r.keepScreenOn,
|
||||||
updater: r.updater,
|
updater: r.updater,
|
||||||
usage: r.usage == null ? null : const FixedUsage(),
|
usage: r.usage == null ? null : const FixedUsage(),
|
||||||
models: r.models,
|
models: r.models,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import 'subtitle_provider.dart';
|
||||||
import 'system_usage.dart';
|
import 'system_usage.dart';
|
||||||
import 'updater.dart';
|
import 'updater.dart';
|
||||||
import '../l10n/tr.dart';
|
import '../l10n/tr.dart';
|
||||||
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
|
||||||
/// 플랫폼별 구현을 한 곳에서 고른다.
|
/// 플랫폼별 구현을 한 곳에서 고른다.
|
||||||
/// Android 이식 시 이 파일에 분기만 추가하면 된다.
|
/// Android 이식 시 이 파일에 분기만 추가하면 된다.
|
||||||
|
|
@ -91,6 +92,9 @@ class PlatformServices {
|
||||||
/// 지금 쓰는 망이 데이터 요금제인지 (원격 썸네일 "Wi-Fi 에서만" - 설계 M3 ThumbPolicy). 처음 값 = 실제 (Android 만 묻고 PC 는 false)
|
/// 지금 쓰는 망이 데이터 요금제인지 (원격 썸네일 "Wi-Fi 에서만" - 설계 M3 ThumbPolicy). 처음 값 = 실제 (Android 만 묻고 PC 는 false)
|
||||||
final Future<bool> Function() isMetered;
|
final Future<bool> Function() isMetered;
|
||||||
|
|
||||||
|
/// 화면을 켜 둔다 / 놓는다 (보기 화면의 자동 넘기기 - 설계 M4 WakeLock). 처음 값 = WakelockPlus
|
||||||
|
final Future<void> Function(bool on) keepScreenOn;
|
||||||
|
|
||||||
/// 이 기기 (Android · Windows · 메모리) - 기기마다 다르게 도는 곳은 이것을 본다 (M1, 처음 값 = 이 기기)
|
/// 이 기기 (Android · Windows · 메모리) - 기기마다 다르게 도는 곳은 이것을 본다 (M1, 처음 값 = 이 기기)
|
||||||
final PlatformInfo platform;
|
final PlatformInfo platform;
|
||||||
|
|
||||||
|
|
@ -118,9 +122,11 @@ class PlatformServices {
|
||||||
VaultLocation? vault,
|
VaultLocation? vault,
|
||||||
this.privileged,
|
this.privileged,
|
||||||
Future<bool> Function()? isMetered,
|
Future<bool> Function()? isMetered,
|
||||||
|
Future<void> Function(bool on)? keepScreenOn,
|
||||||
}) : models = models ?? ModelStore(),
|
}) : models = models ?? ModelStore(),
|
||||||
vault = vault ?? VaultLocation.under(storage.tempDirectory),
|
vault = vault ?? VaultLocation.under(storage.tempDirectory),
|
||||||
isMetered = isMetered ?? deviceMetered,
|
isMetered = isMetered ?? deviceMetered,
|
||||||
|
keepScreenOn = keepScreenOn ?? _wakeLock,
|
||||||
platform = platform ?? PlatformInfo.host,
|
platform = platform ?? PlatformInfo.host,
|
||||||
shell = shell ?? NoopShell();
|
shell = shell ?? NoopShell();
|
||||||
|
|
||||||
|
|
@ -192,3 +198,5 @@ class PlatformServices {
|
||||||
throw UnsupportedError(trf('아직 지원하지 않는 플랫폼입니다: {0}', [Platform.operatingSystem]));
|
throw UnsupportedError(trf('아직 지원하지 않는 플랫폼입니다: {0}', [Platform.operatingSystem]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _wakeLock(bool on) => on ? WakelockPlus.enable() : WakelockPlus.disable();
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ class ExplorerPanesBody extends StatelessWidget {
|
||||||
// X-plore b: 길게 누르기 = 짝 동작 (Rsync 화면은 없음)
|
// X-plore b: 길게 누르기 = 짝 동작 (Rsync 화면은 없음)
|
||||||
onLongPress: ctl.rsync ? null : buttons.longPress,
|
onLongPress: ctl.rsync ? null : buttons.longPress,
|
||||||
// Rsync 화면: 늘 같은 버튼 (→ ← ⇄ 모니터링) 을 두 창 사이에
|
// Rsync 화면: 늘 같은 버튼 (→ ← ⇄ 모니터링) 을 두 창 사이에
|
||||||
buttons: ctl.rsync ? ExplorerButton.rsyncButtons : ExplorerButton.fromSettings(s.explorer.buttons),
|
buttons: ctl.rsync ? ExplorerButton.rsyncButtons : ExplorerButton.fromSettings(s.explorer.buttons, android: ctl.c.services.platform.isAndroid),
|
||||||
vertical: side && !rsyncOne,
|
vertical: side && !rsyncOne,
|
||||||
panesSide: side,
|
panesSide: side,
|
||||||
onPressed: buttons.press,
|
onPressed: buttons.press,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
@ -61,30 +60,28 @@ enum ExplorerButton {
|
||||||
/// Rsync 화면에만 있는 버튼 (파일 탐색기 버튼 구성에서는 빼기)
|
/// Rsync 화면에만 있는 버튼 (파일 탐색기 버튼 구성에서는 빼기)
|
||||||
bool get rsyncOnly => this == toRight || this == toLeft || this == both || this == monitor;
|
bool get rsyncOnly => this == toRight || this == toLeft || this == both || this == monitor;
|
||||||
|
|
||||||
/// 파일 탐색기에서 고를 수 있는 버튼
|
/// 파일 탐색기에서 고를 수 있는 버튼. [앱 관리자] 는 Android 에서만 ([android] = c.services.platform.isAndroid - 설계 M4, 예전 전역 함수 appsButtonSupported)
|
||||||
static List<ExplorerButton> get explorerValues => [for (final b in values) if (!b.rsyncOnly && (b != apps || appsButtonSupported())) b];
|
static List<ExplorerButton> explorerValues({required bool android}) =>
|
||||||
|
[for (final b in values) if (!b.rsyncOnly && (b != apps || android)) b];
|
||||||
/// 앱 관리자 버튼을 쓰는 기기 (Android - 시험에서 바꿈)
|
|
||||||
static bool Function() appsButtonSupported = () => Platform.isAndroid;
|
|
||||||
|
|
||||||
/// Rsync 화면의 버튼 (늘 이대로, 두 창 사이)
|
/// Rsync 화면의 버튼 (늘 이대로, 두 창 사이)
|
||||||
// 208: 방향 실행 (Rsync 화면의 핵심) 을 앞에 - 좁아서 [더 보기] 로 가는 것은 뒤쪽
|
// 208: 방향 실행 (Rsync 화면의 핵심) 을 앞에 - 좁아서 [더 보기] 로 가는 것은 뒤쪽
|
||||||
static const rsyncButtons = [toRight, toLeft, both, monitor, up, refresh, newFolder, bookmarks];
|
static const rsyncButtons = [toRight, toLeft, both, monitor, up, refresh, newFolder, bookmarks];
|
||||||
|
|
||||||
/// 147 (관리자): 처음 값 (버튼 구성을 바꾸지 않았을 때) 은 자주 쓰는 것부터 - 좁은 화면에서 [더 보기] 로 가는 것은 뒤쪽
|
/// 147 (관리자): 처음 값 (버튼 구성을 바꾸지 않았을 때) 은 자주 쓰는 것부터 - 좁은 화면에서 [더 보기] 로 가는 것은 뒤쪽
|
||||||
static List<ExplorerButton> get defaultOrder {
|
static List<ExplorerButton> defaultOrder({required bool android}) {
|
||||||
const first = [copy, move, delete, newFolder, rename, select, search, bookmarks, orient];
|
const first = [copy, move, delete, newFolder, rename, select, search, bookmarks, orient];
|
||||||
return [...first, for (final b in explorerValues) if (!first.contains(b)) b];
|
return [...first, for (final b in explorerValues(android: android)) if (!first.contains(b)) b];
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<ExplorerButton> fromSettings(List<String> names) {
|
static List<ExplorerButton> fromSettings(List<String> names, {required bool android}) {
|
||||||
final out = [
|
final out = [
|
||||||
for (final n in names)
|
for (final n in names)
|
||||||
for (final b in explorerValues)
|
for (final b in explorerValues(android: android))
|
||||||
if (b.name == n) b,
|
if (b.name == n) b,
|
||||||
];
|
];
|
||||||
// 사용자가 정한 순서는 그대로 (비어 있을 때만 처음 값)
|
// 사용자가 정한 순서는 그대로 (비어 있을 때만 처음 값)
|
||||||
return out.isEmpty ? defaultOrder : out;
|
return out.isEmpty ? defaultOrder(android: android) : out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,8 +197,9 @@ class ExplorerViewDialogs {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> buttonsDialog(BuildContext context) async {
|
Future<void> buttonsDialog(BuildContext context) async {
|
||||||
final current = ExplorerButton.fromSettings(ctl.c.settings.explorer.buttons);
|
final android = ctl.c.services.platform.isAndroid;
|
||||||
final order = [...current, ...ExplorerButton.explorerValues.where((b) => !current.contains(b))];
|
final current = ExplorerButton.fromSettings(ctl.c.settings.explorer.buttons, android: android);
|
||||||
|
final order = [...current, ...ExplorerButton.explorerValues(android: android).where((b) => !current.contains(b))];
|
||||||
final on = {...current};
|
final on = {...current};
|
||||||
final ok = await showDialog<bool>(
|
final ok = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -237,10 +238,10 @@ class ExplorerViewDialogs {
|
||||||
onPressed: () => set(() {
|
onPressed: () => set(() {
|
||||||
order
|
order
|
||||||
..clear()
|
..clear()
|
||||||
..addAll(ExplorerButton.defaultOrder);
|
..addAll(ExplorerButton.defaultOrder(android: android));
|
||||||
on
|
on
|
||||||
..clear()
|
..clear()
|
||||||
..addAll(ExplorerButton.explorerValues);
|
..addAll(ExplorerButton.explorerValues(android: android));
|
||||||
}),
|
}),
|
||||||
child: Text(tr('기본값')),
|
child: Text(tr('기본값')),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io' show Platform;
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
|
||||||
|
|
||||||
import '../app/app_controller.dart';
|
import '../app/app_controller.dart';
|
||||||
import '../core/reader_sources.dart';
|
import '../core/reader_sources.dart';
|
||||||
|
|
@ -49,11 +47,7 @@ class ReaderPage extends StatefulWidget {
|
||||||
final bool autoPlay;
|
final bool autoPlay;
|
||||||
const ReaderPage({super.key, required this.c, required this.source, this.start = 0, this.autoPlay = false});
|
const ReaderPage({super.key, required this.c, required this.source, this.start = 0, this.autoPlay = false});
|
||||||
|
|
||||||
/// 볼륨 키로 넘기기를 쓰는 기기인지 (Android - 시험에서 바꿈)
|
|
||||||
static bool Function() volumeKeysSupported = () => Platform.isAndroid;
|
|
||||||
|
|
||||||
/// 계속 보기 동안 화면 켜 두기 (시험에서 바꿈)
|
|
||||||
static Future<void> Function(bool on) keepScreenOn = (on) => on ? WakelockPlus.enable() : WakelockPlus.disable();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ReaderPage> createState() => _ReaderPageState();
|
State<ReaderPage> createState() => _ReaderPageState();
|
||||||
|
|
@ -217,7 +211,7 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
// 지금 장이 이미 보이면 바로 재기 시작, 아직 받는 중이면 보인 뒤에 ([_markShown])
|
// 지금 장이 이미 보이면 바로 재기 시작, 아직 받는 중이면 보인 뒤에 ([_markShown])
|
||||||
if (!_show.set(on, _v.isNotEmpty ? _curKey : null)) return;
|
if (!_show.set(on, _v.isNotEmpty ? _curKey : null)) return;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
unawaited(ReaderPage.keepScreenOn(on).catchError((_) {}));
|
unawaited(c.services.keepScreenOn(on).catchError((_) {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _tick() {
|
void _tick() {
|
||||||
|
|
@ -274,7 +268,7 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
_gallery.dispose();
|
_gallery.dispose();
|
||||||
_life.dispose();
|
_life.dispose();
|
||||||
_show.dispose();
|
_show.dispose();
|
||||||
if (_playing) unawaited(ReaderPage.keepScreenOn(false).catchError((_) {}));
|
if (_playing) unawaited(c.services.keepScreenOn(false).catchError((_) {}));
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
_images.dispose();
|
_images.dispose();
|
||||||
_zoom.reset();
|
_zoom.reset();
|
||||||
|
|
@ -311,7 +305,7 @@ class _ReaderPageState extends State<ReaderPage> {
|
||||||
|
|
||||||
/// X-plore 맞추기 (관리자 지시 10/20): 볼륨 키로 넘기기 (설정을 켰고 Android 일 때만 - 처리한 키는 시스템 볼륨을 바꾸지 않는다)
|
/// X-plore 맞추기 (관리자 지시 10/20): 볼륨 키로 넘기기 (설정을 켰고 Android 일 때만 - 처리한 키는 시스템 볼륨을 바꾸지 않는다)
|
||||||
KeyEventResult _onKey(FocusNode _, KeyEvent e) {
|
KeyEventResult _onKey(FocusNode _, KeyEvent e) {
|
||||||
final step = volumeKeyStep(e, enabled: c.settings.reader.volumeKeys && ReaderPage.volumeKeysSupported());
|
final step = volumeKeyStep(e, enabled: c.settings.reader.volumeKeys && c.services.platform.isAndroid);
|
||||||
if (step == null) return KeyEventResult.ignored;
|
if (step == null) return KeyEventResult.ignored;
|
||||||
if (step != 0) _go(step);
|
if (step != 0) _go(step);
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
|
|
|
||||||
|
|
@ -110,11 +110,8 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('가운데 버튼 [앱 관리자] 는 Android 에서만 고를 수 있음', () {
|
test('가운데 버튼 [앱 관리자] 는 Android 에서만 고를 수 있음', () {
|
||||||
final was = ExplorerButton.appsButtonSupported;
|
// 설계 M4: Android 인지는 부르는 쪽이 넘긴다 (c.services.platform.isAndroid)
|
||||||
addTearDown(() => ExplorerButton.appsButtonSupported = was);
|
expect(ExplorerButton.explorerValues(android: false), isNot(contains(ExplorerButton.apps)));
|
||||||
ExplorerButton.appsButtonSupported = () => false;
|
expect(ExplorerButton.explorerValues(android: true), contains(ExplorerButton.apps));
|
||||||
expect(ExplorerButton.explorerValues, isNot(contains(ExplorerButton.apps)));
|
|
||||||
ExplorerButton.appsButtonSupported = () => true;
|
|
||||||
expect(ExplorerButton.explorerValues, contains(ExplorerButton.apps));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ void main() {
|
||||||
device.trash = true;
|
device.trash = true;
|
||||||
device.volumes = () => [(tmp.path, 'T:')];
|
device.volumes = () => [(tmp.path, 'T:')];
|
||||||
// [휴지통] 버튼이 줄에 보이게 (처음 구성은 좁으면 [더 보기] 로 간다)
|
// [휴지통] 버튼이 줄에 보이게 (처음 구성은 좁으면 [더 보기] 로 간다)
|
||||||
c.settings.explorer.buttons = ['trash', for (final b in ExplorerButton.defaultOrder) if (b != ExplorerButton.trash) b.name];
|
c.settings.explorer.buttons = ['trash', for (final b in ExplorerButton.defaultOrder(android: false)) if (b != ExplorerButton.trash) b.name];
|
||||||
addTearDown(() {
|
addTearDown(() {
|
||||||
});
|
});
|
||||||
await open(tester);
|
await open(tester);
|
||||||
|
|
|
||||||
|
|
@ -390,9 +390,9 @@ void main() {
|
||||||
expect(c.settings.explorer.showHidden, !before);
|
expect(c.settings.explorer.showHidden, !before);
|
||||||
// 창 배치 버튼은 좁아도 늘 줄에 (사용자 요청) · 처음 값은 자주 쓰는 것부터 (복사 · 이동 · 삭제 …)
|
// 창 배치 버튼은 좁아도 늘 줄에 (사용자 요청) · 처음 값은 자주 쓰는 것부터 (복사 · 이동 · 삭제 …)
|
||||||
expect(find.byTooltip('창 배치: 좌우 (누르면 위아래)'), findsOneWidget);
|
expect(find.byTooltip('창 배치: 좌우 (누르면 위아래)'), findsOneWidget);
|
||||||
expect(ExplorerButton.fromSettings(const []).take(9).map((b) => b.name),
|
expect(ExplorerButton.fromSettings(const [], android: false).take(9).map((b) => b.name),
|
||||||
['copy', 'move', 'delete', 'newFolder', 'rename', 'select', 'search', 'bookmarks', 'orient']);
|
['copy', 'move', 'delete', 'newFolder', 'rename', 'select', 'search', 'bookmarks', 'orient']);
|
||||||
expect(ExplorerButton.fromSettings(const ['hidden', 'copy']), [ExplorerButton.hidden, ExplorerButton.copy],
|
expect(ExplorerButton.fromSettings(const ['hidden', 'copy'], android: false), [ExplorerButton.hidden, ExplorerButton.copy],
|
||||||
reason: '사용자가 정한 순서는 그대로');
|
reason: '사용자가 정한 순서는 그대로');
|
||||||
// 넉넉한 화면에서는 [더 보기] 없이 모두
|
// 넉넉한 화면에서는 [더 보기] 없이 모두
|
||||||
tester.view.physicalSize = const Size(1600, 1400);
|
tester.view.physicalSize = const Size(1600, 1400);
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,11 @@ class _FolderSized extends ImageFilesSource {
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late List<bool> screenOn;
|
late List<bool> screenOn;
|
||||||
setUp(() {
|
setUp(() => screenOn = []);
|
||||||
screenOn = [];
|
|
||||||
ReaderPage.keepScreenOn = (on) async => screenOn.add(on);
|
|
||||||
});
|
|
||||||
|
|
||||||
AppController controller() =>
|
// 설계 M4 WakeLock: 화면 켜 두기는 PlatformServices.keepScreenOn 으로 넘긴다
|
||||||
AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
AppController controller() => AppController(PlatformServices(
|
||||||
|
mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), keepScreenOn: (on) async => screenOn.add(on)));
|
||||||
|
|
||||||
Future<_Sized> source(WidgetTester t, List<(int, int)> sizes) async {
|
Future<_Sized> source(WidgetTester t, List<(int, int)> sizes) async {
|
||||||
final images = <ui.Image>[];
|
final images = <ui.Image>[];
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import 'package:jj_mkvmaker/platform/windows/process_media_tool.dart';
|
||||||
import 'package:jj_mkvmaker/services/platform_services.dart';
|
import 'package:jj_mkvmaker/services/platform_services.dart';
|
||||||
import 'package:jj_mkvmaker/ui/reader_page.dart';
|
import 'package:jj_mkvmaker/ui/reader_page.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:jj_mkvmaker/services/platform_info.dart';
|
||||||
|
|
||||||
Uint8List _png(int r, int g, int b, {int w = 40, int h = 60}) {
|
Uint8List _png(int r, int g, int b, {int w = 40, int h = 60}) {
|
||||||
final im = img.Image(width: w, height: h);
|
final im = img.Image(width: w, height: h);
|
||||||
|
|
@ -219,14 +220,13 @@ void main() {
|
||||||
tester.view.physicalSize = const Size(900, 1200);
|
tester.view.physicalSize = const Size(900, 1200);
|
||||||
tester.view.devicePixelRatio = 1;
|
tester.view.devicePixelRatio = 1;
|
||||||
addTearDown(tester.view.reset);
|
addTearDown(tester.view.reset);
|
||||||
final was = ReaderPage.volumeKeysSupported;
|
|
||||||
addTearDown(() => ReaderPage.volumeKeysSupported = was);
|
|
||||||
ReaderPage.volumeKeysSupported = () => true;
|
|
||||||
expect(AppSettings().reader.volumeKeys, isFalse);
|
expect(AppSettings().reader.volumeKeys, isFalse);
|
||||||
final files = [
|
final files = [
|
||||||
for (final (i, n) in ['a.png', 'b.png', 'c.png'].indexed) (File(p.join(tmp.path, n))..writeAsBytesSync(_png(i * 80, 0, 0))).path,
|
for (final (i, n) in ['a.png', 'b.png', 'c.png'].indexed) (File(p.join(tmp.path, n))..writeAsBytesSync(_png(i * 80, 0, 0))).path,
|
||||||
];
|
];
|
||||||
final c = AppController(PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService()));
|
// 설계 M4: Android 인지는 PlatformInfo 주입 (예전 전역 함수 ReaderPage.volumeKeysSupported)
|
||||||
|
final c = AppController(
|
||||||
|
PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), platform: PlatformInfo.android));
|
||||||
await tester.pumpWidget(MaterialApp(home: ReaderPage(c: c, source: ImageFilesSource(files, tempDir: tmp.path, title: 'imgs'))));
|
await tester.pumpWidget(MaterialApp(home: ReaderPage(c: c, source: ImageFilesSource(files, tempDir: tmp.path, title: 'imgs'))));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
// 끔: 볼륨 키를 가로채지 않는다 (시스템 볼륨이 바뀜)
|
// 끔: 볼륨 키를 가로채지 않는다 (시스템 볼륨이 바뀜)
|
||||||
|
|
@ -244,8 +244,12 @@ void main() {
|
||||||
await tester.sendKeyEvent(LogicalKeyboardKey.audioVolumeUp);
|
await tester.sendKeyEvent(LogicalKeyboardKey.audioVolumeUp);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('2 / 3'), findsOneWidget);
|
expect(find.text('2 / 3'), findsOneWidget);
|
||||||
// Android 가 아니면 쓰지 않음
|
// Android 가 아니면 쓰지 않음 (같은 설정 · Windows 조립)
|
||||||
ReaderPage.volumeKeysSupported = () => false;
|
final win = AppController(
|
||||||
|
PlatformServices(mediaTool: ProcessMediaTool('x', 'y'), storage: DesktopStorageService(), platform: PlatformInfo.windows));
|
||||||
|
win.settings.reader.volumeKeys = true;
|
||||||
|
await tester.pumpWidget(MaterialApp(home: ReaderPage(c: win, source: ImageFilesSource(files, tempDir: tmp.path, title: 'imgs'))));
|
||||||
|
await tester.pump();
|
||||||
expect(await tester.sendKeyEvent(LogicalKeyboardKey.audioVolumeDown), isFalse);
|
expect(await tester.sendKeyEvent(LogicalKeyboardKey.audioVolumeDown), isFalse);
|
||||||
await tester.pumpWidget(const SizedBox());
|
await tester.pumpWidget(const SizedBox());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue