diff --git a/lib/globals.dart b/lib/globals.dart index d3aac98..ca65c60 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -22,6 +22,7 @@ import 'config/shared_preferences.dart'; import 'config/windows.dart'; import 'main.dart'; import 'platform/clipboard.dart'; +import 'platform/display.dart'; import 'platform/path.dart'; import 'platform/set_title.dart'; import 'tags.dart'; @@ -121,6 +122,7 @@ EHApi get api { final AuthInfo auth = AuthInfo(); final Clipboard platformClipboard = Clipboard(); +final Display platformDisplay = Display(); final Path platformPath = Path(); final TagsInfo tags = TagsInfo(); final GlobalKey rootScaffoldMessengerKey = diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f45a634..6f6b373 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -85,5 +85,6 @@ "maxZipFilenameLength": "Maximum length of filenames in Zip files", "downloadComplete": "Download completed.", "downloadZipFailed": "Failed to download ZIP file.", - "rating": "Rating" + "rating": "Rating", + "preventScreenCapture": "Prevent screen capture" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 986e5b8..fabbefc 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -85,5 +85,6 @@ "maxZipFilenameLength": "Zip文件中文件名的最大长度", "downloadComplete": "下载完毕。", "downloadZipFailed": "Zip文件下载失败。", - "rating": "评分" + "rating": "评分", + "preventScreenCapture": "防止截屏" } diff --git a/lib/main.dart b/lib/main.dart index 73e4441..c5c0c3b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -151,6 +151,9 @@ void main() async { await windowManager.ensureInitialized(); } await initLogger(); + if (prefs.getBool("preventScreenCapture") ?? false) { + await platformDisplay.enableProtect(); + } GoRouter.optionURLReflectsImperativeAPIs = true; runApp(const MainApp()); } diff --git a/lib/platform/display.dart b/lib/platform/display.dart new file mode 100644 index 0000000..6d5352e --- /dev/null +++ b/lib/platform/display.dart @@ -0,0 +1,31 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; + +final _log = Logger("platformDisplay"); + +class Display { + static const platform = + MethodChannel("lifegpc.eh_downloader_flutter/display"); + Future disableProtect() async { + if (kIsWeb) return true; + try { + await platform.invokeMethod("disableProtect"); + return true; + } catch (e) { + _log.warning("Failed to disable protect", e); + return false; + } + } + + Future enableProtect() async { + if (kIsWeb) return true; + try { + await platform.invokeMethod("enableProtect"); + return true; + } catch (e) { + _log.warning("Failed to enable protect", e); + return false; + } + } +} diff --git a/lib/settings.dart b/lib/settings.dart index a006270..290bb0b 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -19,11 +19,13 @@ class SettingsPage extends StatefulWidget { class _SettingsPage extends State with ThemeModeWidget { bool _oriDisplayAd = false; Lang _oriLang = Lang.system; + bool _oriPreventScreenCapture = false; bool _oriShowNsfw = false; bool _oriShowTranslatedTag = false; bool _oriUseTitleJpn = false; bool _displayAd = false; Lang _lang = Lang.system; + bool _preventScreenCapture = false; bool _showNsfw = false; bool _showTranslatedTag = false; bool _useTitleJpn = false; @@ -71,6 +73,14 @@ class _SettingsPage extends State with ThemeModeWidget { _oriShowTranslatedTag = false; _showTranslatedTag = false; } + try { + _oriPreventScreenCapture = prefs.getBool("preventScreenCapture") ?? false; + _preventScreenCapture = _oriPreventScreenCapture; + } catch (e) { + _log.warning("Failed to get preventScreenCapture:", e); + _oriPreventScreenCapture = false; + _preventScreenCapture = false; + } } void fallback(BuildContext context) { @@ -87,6 +97,7 @@ class _SettingsPage extends State with ThemeModeWidget { _showNsfw = false; _displayAd = false; _showTranslatedTag = _oriLang.toLocale().languageCode == "zh"; + _preventScreenCapture = false; }); } @@ -130,6 +141,23 @@ class _SettingsPage extends State with ThemeModeWidget { _oriShowTranslatedTag = _showTranslatedTag; } } + if (_oriPreventScreenCapture != _preventScreenCapture) { + if (!await prefs.setBool("preventScreenCapture", _preventScreenCapture)) { + re = false; + _log.warning("Failed to save preventScreenCapture."); + } else { + _oriPreventScreenCapture = _preventScreenCapture; + } + if (_preventScreenCapture) { + if (!await platformDisplay.enableProtect()) { + _log.warning("Failed to enable protect."); + } + } else { + if (!await platformDisplay.disableProtect()) { + _log.warning("Failed to disable protect."); + } + } + } return re; } @@ -250,6 +278,21 @@ class _SettingsPage extends State with ThemeModeWidget { .showTranslatedTag), ), ), + Container( + padding: const EdgeInsets.symmetric(vertical: 8), + child: CheckboxMenuButton( + value: _preventScreenCapture, + onChanged: (bool? value) { + if (value != null) { + setState(() { + _preventScreenCapture = value; + }); + } + }, + child: Text(AppLocalizations.of(context)! + .preventScreenCapture), + ), + ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index 4095a21..fa1ee3b 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -224,6 +224,25 @@ bool FlutterWindow::OnCreate() { result->NotImplemented(); } }); + flutter::MethodChannel<> display(flutter_controller_->engine()->messenger(), "lifegpc.eh_downloader_flutter/display", + &flutter::StandardMethodCodec::GetInstance()); + display.SetMethodCallHandler([&](const flutter::MethodCall<>& call, std::unique_ptr> result) { + if (call.method_name() == "enableProtect") { + if (!SetWindowDisplayAffinity(Win32Window::GetHandle(), WDA_EXCLUDEFROMCAPTURE)) { + result->Error("ERROR", "Failed to enable protect."); + return; + } + result->Success(); + } else if (call.method_name() == "disableProtect") { + if (!SetWindowDisplayAffinity(Win32Window::GetHandle(), WDA_NONE)) { + result->Error("ERROR", "Failed to disable protect."); + return; + } + result->Success(); + } else { + result->NotImplemented(); + } + }); SetChildContent(flutter_controller_->view()->GetNativeWindow());