mirror of
https://mirror.ghproxy.com/https://github.com/StarCitizenToolBox/app.git
synced 2025-01-12 16:54:02 +08:00
feat:riverpod 迁移 Web 汉化
This commit is contained in:
parent
e70fca4899
commit
a6507a6910
@ -527,7 +527,7 @@ class HomeUI extends HookConsumerWidget {
|
|||||||
if (touchKey != null) {
|
if (touchKey != null) {
|
||||||
AnalyticsApi.touch(touchKey);
|
AnalyticsApi.touch(touchKey);
|
||||||
}
|
}
|
||||||
model.goWebView(webTitle, webURL, useLocalization: true);
|
model.goWebView(context, webTitle, webURL, useLocalization: true);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: width,
|
width: width,
|
||||||
@ -592,8 +592,8 @@ class HomeUI extends HookConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
model.goWebView(
|
model.goWebView(context, "RSI 服务器状态",
|
||||||
"RSI 服务器状态", "https://status.robertsspaceindustries.com/",
|
"https://status.robertsspaceindustries.com/",
|
||||||
useLocalization: true);
|
useLocalization: true);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dart_rss/domain/rss_item.dart';
|
import 'package:dart_rss/domain/rss_item.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@ -26,6 +27,8 @@ import 'package:url_launcher/url_launcher_string.dart';
|
|||||||
import 'package:html/parser.dart' as html;
|
import 'package:html/parser.dart' as html;
|
||||||
import 'package:html/dom.dart' as html_dom;
|
import 'package:html/dom.dart' as html_dom;
|
||||||
|
|
||||||
|
import '../webview/webview.dart';
|
||||||
|
|
||||||
part 'home_ui_model.freezed.dart';
|
part 'home_ui_model.freezed.dart';
|
||||||
|
|
||||||
part 'home_ui_model.g.dart';
|
part 'home_ui_model.g.dart';
|
||||||
@ -116,8 +119,80 @@ class HomeUIModel extends _$HomeUIModel {
|
|||||||
|
|
||||||
onMenuTap(String key) {}
|
onMenuTap(String key) {}
|
||||||
|
|
||||||
void goWebView(String webTitle, String webURL,
|
// ignore: avoid_build_context_in_providers
|
||||||
{required bool useLocalization}) {}
|
Future<void> goWebView(BuildContext context, String title, String url,
|
||||||
|
{bool useLocalization = false,
|
||||||
|
bool loginMode = false,
|
||||||
|
RsiLoginCallback? rsiLoginCallback}) async {
|
||||||
|
if (useLocalization) {
|
||||||
|
const tipVersion = 2;
|
||||||
|
final box = await Hive.openBox("app_conf");
|
||||||
|
final skip =
|
||||||
|
await box.get("skip_web_localization_tip_version", defaultValue: 0);
|
||||||
|
if (skip != tipVersion) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
final ok = await showConfirmDialogs(
|
||||||
|
context,
|
||||||
|
"星际公民网站汉化",
|
||||||
|
const Text(
|
||||||
|
"本插功能件仅供大致浏览使用,不对任何有关本功能产生的问题负责!在涉及账号操作前请注意确认网站的原本内容!"
|
||||||
|
"\n\n\n使用此功能登录账号时请确保您的 SC汉化盒子 是从可信任的来源下载。",
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: MediaQuery.of(context).size.width * .6));
|
||||||
|
if (!ok) {
|
||||||
|
if (loginMode) {
|
||||||
|
rsiLoginCallback?.call(null, false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await box.put("skip_web_localization_tip_version", tipVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!await WebviewWindow.isWebviewAvailable()) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showToast(context, "需要安装 WebView2 Runtime");
|
||||||
|
launchUrlString(
|
||||||
|
"https://developer.microsoft.com/en-us/microsoft-edge/webview2/");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
|
final webViewModel = WebViewModel(context,
|
||||||
|
loginMode: loginMode, loginCallback: rsiLoginCallback);
|
||||||
|
if (useLocalization) {
|
||||||
|
state = state.copyWith(isFixing: true, isFixingString: "正在初始化汉化资源...");
|
||||||
|
try {
|
||||||
|
await webViewModel.initLocalization(state.webLocalizationVersionsData!);
|
||||||
|
} catch (e) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
showToast(context, "初始化网页汉化资源失败!$e");
|
||||||
|
}
|
||||||
|
state = state.copyWith(isFixingString: "", isFixing: false);
|
||||||
|
}
|
||||||
|
await webViewModel.initWebView(
|
||||||
|
title: title,
|
||||||
|
applicationSupportDir: appGlobalState.applicationSupportDir!,
|
||||||
|
appVersionData: appGlobalState.networkVersionData!,
|
||||||
|
);
|
||||||
|
|
||||||
|
// if (await File(
|
||||||
|
// "${AppConf.applicationSupportDir}\\webview_data\\enable_webview_localization_capture")
|
||||||
|
// .exists()) {
|
||||||
|
// webViewModel.enableCapture = true;
|
||||||
|
// BaseUIContainer(
|
||||||
|
// uiCreate: () => WebviewLocalizationCaptureUI(),
|
||||||
|
// modelCreate: () =>
|
||||||
|
// WebviewLocalizationCaptureUIModel(webViewModel))
|
||||||
|
// .push(context!)
|
||||||
|
// .then((_) {
|
||||||
|
// webViewModel.enableCapture = false;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
await webViewModel.launch(url, appGlobalState.networkVersionData!);
|
||||||
|
}
|
||||||
|
|
||||||
bool isRSIServerStatusOK(Map map) {
|
bool isRSIServerStatusOK(Map map) {
|
||||||
return (map["status"] == "ok" || map["status"] == "operational");
|
return (map["status"] == "ok" || map["status"] == "operational");
|
||||||
|
321
lib/ui/webview/webview.dart
Normal file
321
lib/ui/webview/webview.dart
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
// ignore_for_file: use_build_context_synchronously
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:cryptography/cryptography.dart';
|
||||||
|
import 'package:desktop_webview_window/desktop_webview_window.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:local_auth/local_auth.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||||
|
import 'package:starcitizen_doctor/common/win32/credentials.dart';
|
||||||
|
import 'package:starcitizen_doctor/data/app_version_data.dart';
|
||||||
|
import 'package:starcitizen_doctor/data/app_web_localization_versions_data.dart';
|
||||||
|
|
||||||
|
typedef RsiLoginCallback = void Function(Map? data, bool success);
|
||||||
|
|
||||||
|
class WebViewModel {
|
||||||
|
late Webview webview;
|
||||||
|
final BuildContext context;
|
||||||
|
|
||||||
|
bool _isClosed = false;
|
||||||
|
|
||||||
|
bool get isClosed => _isClosed;
|
||||||
|
|
||||||
|
WebViewModel(this.context,
|
||||||
|
{this.loginMode = false, this.loginCallback, this.loginChannel = "LIVE"});
|
||||||
|
|
||||||
|
String url = "";
|
||||||
|
bool canGoBack = false;
|
||||||
|
|
||||||
|
final localizationResource = <String, dynamic>{};
|
||||||
|
|
||||||
|
var localizationScript = "";
|
||||||
|
|
||||||
|
bool enableCapture = false;
|
||||||
|
|
||||||
|
bool isEnableToolSiteMirrors = false;
|
||||||
|
|
||||||
|
Map<String, String>? _curReplaceWords;
|
||||||
|
|
||||||
|
Map<String, String>? get curReplaceWords => _curReplaceWords;
|
||||||
|
|
||||||
|
final bool loginMode;
|
||||||
|
final String loginChannel;
|
||||||
|
|
||||||
|
bool _loginModeSuccess = false;
|
||||||
|
|
||||||
|
final RsiLoginCallback? loginCallback;
|
||||||
|
|
||||||
|
initWebView(
|
||||||
|
{String title = "",
|
||||||
|
required String applicationSupportDir,
|
||||||
|
required AppVersionData appVersionData}) async {
|
||||||
|
try {
|
||||||
|
final userBox = await Hive.openBox("app_conf");
|
||||||
|
isEnableToolSiteMirrors =
|
||||||
|
userBox.get("isEnableToolSiteMirrors", defaultValue: false);
|
||||||
|
webview = await WebviewWindow.create(
|
||||||
|
configuration: CreateConfiguration(
|
||||||
|
windowWidth: loginMode ? 960 : 1920,
|
||||||
|
windowHeight: loginMode ? 720 : 1080,
|
||||||
|
userDataFolderWindows: "$applicationSupportDir/webview_data",
|
||||||
|
title: title));
|
||||||
|
// webview.openDevToolsWindow();
|
||||||
|
webview.isNavigating.addListener(() async {
|
||||||
|
if (!webview.isNavigating.value && localizationResource.isNotEmpty) {
|
||||||
|
dPrint("webview Navigating url === $url");
|
||||||
|
if (url.contains("robertsspaceindustries.com")) {
|
||||||
|
// SC 官网
|
||||||
|
dPrint("load script");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
await webview.evaluateJavaScript(localizationScript);
|
||||||
|
dPrint("update replaceWords");
|
||||||
|
final replaceWords = _getLocalizationResource("zh-CN");
|
||||||
|
|
||||||
|
const org = "https://robertsspaceindustries.com/orgs";
|
||||||
|
const citizens = "https://robertsspaceindustries.com/citizens";
|
||||||
|
const organization =
|
||||||
|
"https://robertsspaceindustries.com/account/organization";
|
||||||
|
const concierge =
|
||||||
|
"https://robertsspaceindustries.com/account/concierge";
|
||||||
|
const referral =
|
||||||
|
"https://robertsspaceindustries.com/account/referral-program";
|
||||||
|
const address =
|
||||||
|
"https://robertsspaceindustries.com/account/addresses";
|
||||||
|
|
||||||
|
const hangar = "https://robertsspaceindustries.com/account/pledges";
|
||||||
|
|
||||||
|
const spectrum =
|
||||||
|
"https://robertsspaceindustries.com/spectrum/community/";
|
||||||
|
// 跳过光谱论坛 https://github.com/StarCitizenToolBox/StarCitizenBoxBrowserEx/issues/1
|
||||||
|
if (url.startsWith(spectrum)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.startsWith(org) ||
|
||||||
|
url.startsWith(citizens) ||
|
||||||
|
url.startsWith(organization)) {
|
||||||
|
replaceWords.add({"word": 'members', "replacement": '名成员'});
|
||||||
|
replaceWords.addAll(_getLocalizationResource("orgs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address.startsWith(address)) {
|
||||||
|
replaceWords.addAll(_getLocalizationResource("address"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.startsWith(referral)) {
|
||||||
|
replaceWords.addAll([
|
||||||
|
{"word": 'Total recruits: ', "replacement": '总邀请数:'},
|
||||||
|
{"word": 'Prospects ', "replacement": '未完成的邀请'},
|
||||||
|
{"word": 'Recruits', "replacement": '已完成的邀请'},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.startsWith(concierge)) {
|
||||||
|
replaceWords.clear();
|
||||||
|
replaceWords.addAll(_getLocalizationResource("concierge"));
|
||||||
|
}
|
||||||
|
if (url.startsWith(hangar)) {
|
||||||
|
replaceWords.addAll(_getLocalizationResource("hangar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
_curReplaceWords = {};
|
||||||
|
for (var element in replaceWords) {
|
||||||
|
_curReplaceWords?[element["word"] ?? ""] =
|
||||||
|
element["replacement"] ?? "";
|
||||||
|
}
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
await webview.evaluateJavaScript(
|
||||||
|
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
|
||||||
|
|
||||||
|
/// loginMode
|
||||||
|
if (loginMode) {
|
||||||
|
dPrint(
|
||||||
|
"--- do rsi login ---\n run === getRSILauncherToken(\"$loginChannel\");");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
|
webview.evaluateJavaScript(
|
||||||
|
"getRSILauncherToken(\"$loginChannel\");");
|
||||||
|
}
|
||||||
|
} else if (url.startsWith(await _handleMirrorsUrl(
|
||||||
|
"https://www.erkul.games", appVersionData))) {
|
||||||
|
dPrint("load script");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
await webview.evaluateJavaScript(localizationScript);
|
||||||
|
dPrint("update replaceWords");
|
||||||
|
final replaceWords = _getLocalizationResource("DPS");
|
||||||
|
await webview.evaluateJavaScript(
|
||||||
|
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
|
||||||
|
} else if (url.startsWith(await _handleMirrorsUrl(
|
||||||
|
"https://uexcorp.space", appVersionData))) {
|
||||||
|
dPrint("load script");
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
await webview.evaluateJavaScript(localizationScript);
|
||||||
|
dPrint("update replaceWords");
|
||||||
|
final replaceWords = _getLocalizationResource("UEX");
|
||||||
|
await webview.evaluateJavaScript(
|
||||||
|
"WebLocalizationUpdateReplaceWords(${json.encode(replaceWords)},$enableCapture)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
webview.addOnUrlRequestCallback((url) {
|
||||||
|
dPrint("OnUrlRequestCallback === $url");
|
||||||
|
this.url = url;
|
||||||
|
});
|
||||||
|
webview.onClose.whenComplete(dispose);
|
||||||
|
if (loginMode) {
|
||||||
|
webview.addOnWebMessageReceivedCallback((messageString) {
|
||||||
|
final message = json.decode(messageString);
|
||||||
|
if (message["action"] == "webview_rsi_login_show_window") {
|
||||||
|
webview.setWebviewWindowVisibility(true);
|
||||||
|
_checkAutoLogin(webview);
|
||||||
|
} else if (message["action"] == "webview_rsi_login_success") {
|
||||||
|
_loginModeSuccess = true;
|
||||||
|
loginCallback?.call(message, true);
|
||||||
|
webview.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Future.delayed(const Duration(seconds: 1))
|
||||||
|
.then((value) => {webview.setWebviewWindowVisibility(false)});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showToast(context, "初始化失败:$e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _handleMirrorsUrl(
|
||||||
|
String url, AppVersionData appVersionData) async {
|
||||||
|
var finalUrl = url;
|
||||||
|
if (isEnableToolSiteMirrors) {
|
||||||
|
for (var kv in appVersionData.webMirrors!.entries) {
|
||||||
|
if (url.startsWith(kv.key)) {
|
||||||
|
finalUrl = url.replaceFirst(kv.key, kv.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return finalUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
launch(String url, AppVersionData appVersionData) async {
|
||||||
|
webview.launch(await _handleMirrorsUrl(url, appVersionData));
|
||||||
|
}
|
||||||
|
|
||||||
|
initLocalization(AppWebLocalizationVersionsData v) async {
|
||||||
|
localizationScript = await rootBundle.loadString('assets/web_script.js');
|
||||||
|
|
||||||
|
/// https://github.com/CxJuice/Uex_Chinese_Translate
|
||||||
|
// get versions
|
||||||
|
final hostUrl = URLConf.webTranslateHomeUrl;
|
||||||
|
dPrint("AppWebLocalizationVersionsData === ${v.toJson()}");
|
||||||
|
|
||||||
|
localizationResource["zh-CN"] = await _getJson("$hostUrl/zh-CN-rsi.json",
|
||||||
|
cacheKey: "rsi", version: v.rsi);
|
||||||
|
localizationResource["concierge"] = await _getJson(
|
||||||
|
"$hostUrl/concierge.json",
|
||||||
|
cacheKey: "concierge",
|
||||||
|
version: v.concierge);
|
||||||
|
localizationResource["orgs"] =
|
||||||
|
await _getJson("$hostUrl/orgs.json", cacheKey: "orgs", version: v.orgs);
|
||||||
|
localizationResource["address"] = await _getJson("$hostUrl/addresses.json",
|
||||||
|
cacheKey: "addresses", version: v.addresses);
|
||||||
|
localizationResource["hangar"] = await _getJson("$hostUrl/hangar.json",
|
||||||
|
cacheKey: "hangar", version: v.hangar);
|
||||||
|
localizationResource["UEX"] = await _getJson("$hostUrl/zh-CN-uex.json",
|
||||||
|
cacheKey: "uex", version: v.uex);
|
||||||
|
localizationResource["DPS"] = await _getJson("$hostUrl/zh-CN-dps.json",
|
||||||
|
cacheKey: "dps", version: v.dps);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, String>> _getLocalizationResource(String key) {
|
||||||
|
final List<Map<String, String>> localizations = [];
|
||||||
|
final dict = localizationResource[key];
|
||||||
|
if (dict is Map) {
|
||||||
|
for (var element in dict.entries) {
|
||||||
|
final k = element.key
|
||||||
|
.toString()
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replaceAll(RegExp("/\xa0/g"), ' ')
|
||||||
|
.replaceAll(RegExp("/s{2,}/g"), ' ');
|
||||||
|
localizations
|
||||||
|
.add({"word": k, "replacement": element.value.toString().trim()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return localizations;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> _getJson(String url,
|
||||||
|
{String cacheKey = "", String? version}) async {
|
||||||
|
final box = await Hive.openBox("web_localization_cache_data");
|
||||||
|
if (cacheKey.isNotEmpty) {
|
||||||
|
final localVersion = box.get("${cacheKey}_version}", defaultValue: "");
|
||||||
|
var data = box.get(cacheKey, defaultValue: {});
|
||||||
|
if (data is Map && data.isNotEmpty && localVersion == version) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final startTime = DateTime.now();
|
||||||
|
final r = await RSHttp.getText(url);
|
||||||
|
final endTime = DateTime.now();
|
||||||
|
final data = json.decode(r);
|
||||||
|
if (cacheKey.isNotEmpty) {
|
||||||
|
dPrint(
|
||||||
|
"update $cacheKey v == $version time == ${(endTime.microsecondsSinceEpoch - startTime.microsecondsSinceEpoch) / 1000 / 1000}s");
|
||||||
|
await box.put(cacheKey, data);
|
||||||
|
await box.put("${cacheKey}_version}", version);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addOnWebMessageReceivedCallback(OnWebMessageReceivedCallback callback) {
|
||||||
|
webview.addOnWebMessageReceivedCallback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeOnWebMessageReceivedCallback(
|
||||||
|
OnWebMessageReceivedCallback callback) {
|
||||||
|
webview.removeOnWebMessageReceivedCallback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> dispose() {
|
||||||
|
if (loginMode && !_loginModeSuccess) {
|
||||||
|
loginCallback?.call(null, false);
|
||||||
|
}
|
||||||
|
_isClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _checkAutoLogin(Webview webview) async {
|
||||||
|
final LocalAuthentication localAuth = LocalAuthentication();
|
||||||
|
if (!await localAuth.isDeviceSupported()) return;
|
||||||
|
|
||||||
|
final userBox = await Hive.openBox("rsi_account_data");
|
||||||
|
final email = await userBox.get("account_email", defaultValue: "");
|
||||||
|
|
||||||
|
final pwdE = await userBox.get("account_pwd_encrypted", defaultValue: "");
|
||||||
|
final nonceStr = await userBox.get("nonce", defaultValue: "");
|
||||||
|
final macStr = await userBox.get("mac", defaultValue: "");
|
||||||
|
if (email == "") return;
|
||||||
|
webview.evaluateJavaScript("RSIAutoLogin(\"$email\",\"\")");
|
||||||
|
if (pwdE != "" && nonceStr != "" && macStr != "") {
|
||||||
|
// send toast
|
||||||
|
webview.evaluateJavaScript("SCTShowToast(\"请完成 Windows Hello 验证以填充密码\")");
|
||||||
|
// decrypt
|
||||||
|
if (await localAuth.authenticate(localizedReason: "请输入设备PIN以自动登录RSI账户") !=
|
||||||
|
true) return;
|
||||||
|
final kv = Win32Credentials.read("SCToolbox_RSI_Account_secret");
|
||||||
|
if (kv == null || kv.key != email) return;
|
||||||
|
|
||||||
|
final algorithm = AesGcm.with256bits();
|
||||||
|
final r = await algorithm.decrypt(
|
||||||
|
SecretBox(base64.decode(pwdE),
|
||||||
|
nonce: base64.decode(nonceStr), mac: Mac(base64.decode(macStr))),
|
||||||
|
secretKey: SecretKey(base64.decode(kv.value)));
|
||||||
|
final decryptedPwd = utf8.decode(r);
|
||||||
|
webview.evaluateJavaScript("RSIAutoLogin(\"$email\",\"$decryptedPwd\")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user