初始化提交

This commit is contained in:
2025-09-23 16:23:15 +08:00
commit 877b18a70f
590 changed files with 53617 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:encrypt/encrypt.dart' as encrypt;
/// AES加密解密工具
class AESUtils {
// Encrypt function: Takes plaintext and key, returns base64 ciphertext and nonce
Map<String, String> encryptData(String plainText, String keyStr) {
// Generate timestamp as nonce
String nonce = DateTime.now().toIso8601String();
// Generate key and IV
final key = generateKey(keyStr);
final iv = generateIv(nonce, keyStr);
// Encrypt the data
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7'));
final encrypted = encrypter.encrypt(plainText, iv: iv);
return {
'data': encrypted.base64,
'time': nonce,
};
}
// Decrypt function: Takes base64 ciphertext, key, and nonce, returns plaintext
String decryptData(String cipherText, String keyStr, String nonce) {
// Generate key and IV
final key = generateKey(keyStr);
final iv = generateIv(nonce, keyStr);
// Decrypt the data
final encrypter = encrypt.Encrypter(
encrypt.AES(key, mode: encrypt.AESMode.cbc, padding: 'PKCS7'));
final decrypted = encrypter.decrypt64(cipherText, iv: iv);
return decrypted;
}
// Generate AES key from input key (32 bytes for AES-256)
encrypt.Key generateKey(String keyStr) {
final keyBytes = sha256.convert(utf8.encode(keyStr)).bytes;
return encrypt.Key(Uint8List.fromList(keyBytes));
}
// Generate IV (Initialization Vector) from nonce and key
encrypt.IV generateIv(String ivStr, String keyStr) {
final md5Hash = md5.convert(utf8.encode(ivStr)).bytes;
final combinedKey = hexEncode(md5Hash) + keyStr;
final finalHash = sha256.convert(utf8.encode(combinedKey)).bytes;
return encrypt.IV(Uint8List.fromList(
finalHash.sublist(0, 16))); // AES-CBC IV must be 16 bytes
}
// Helper function to encode bytes to hex
String hexEncode(List<int> bytes) {
return bytes.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join('');
}
}
+42
View File
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import '../widgets/kr_toast.dart';
class KRCommonUtil {
/// 提示 meesage: 提示内容, toastPosition: 提示显示的位置, timeout: 显示时间(毫秒)
static kr_showToast(String message,
{KRToastPosition toastPosition = KRToastPosition.center,
int timeout = 1500}) {
KRToast.kr_showToast(
message,
position: toastPosition,
duration: Duration(milliseconds: timeout),
);
}
/// 显示加载动画
static kr_showLoading({String? message}) {
KRToast.kr_showLoading(message: message);
}
/// 隐藏加载动画
static kr_hideLoading() {
KRToast.kr_hideLoading();
}
/// 格式化字节数为可读字符串
/// [bytes] 字节数
static String kr_formatBytes(int bytes) {
if (bytes <= 0) return "0 B";
const List<String> suffixes = ["B", "KB", "MB", "GB", "TB"];
int i = 0;
double value = bytes.toDouble();
while (value >= 1024 && i < suffixes.length - 1) {
value /= 1024;
i++;
}
return "${value.toStringAsFixed(2)} ${suffixes[i]}";
}
}
+154
View File
@@ -0,0 +1,154 @@
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart';
import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart';
import 'package:kaer_with_panels/singbox/model/singbox_status.dart';
import '../common/app_config.dart';
/// 支持的国家枚举
enum KRCountry {
cn('中国'),
ir('伊朗'),
af('阿富汗'),
ru('俄罗斯'),
id('印度尼西亚'),
tr('土耳其'),
br('巴西');
final String kr_name;
const KRCountry(this.kr_name);
/// 获取国家代码
String get kr_code => name.toLowerCase();
/// 获取国家名称
String get kr_countryName => kr_name;
/// 从代码获取枚举值
static KRCountry? kr_fromCode(String code) {
try {
return KRCountry.values.firstWhere(
(country) => country.kr_code == code.toLowerCase(),
);
} catch (e) {
return null;
}
}
}
/// 国家工具类
class KRCountryUtil {
/// 存储键
static const String _kr_countryKey = 'kr_current_country';
/// 当前选择的国家
static final Rx<KRCountry> kr_currentCountry = KRCountry.cn.obs;
/// 存储实例
static final KRSecureStorage _kr_storage = KRSecureStorage();
/// 初始化
static Future<void> kr_init() async {
try {
final String? kr_savedCountry =
await _kr_storage.kr_readData(key: _kr_countryKey);
if (kr_savedCountry != null) {
final KRCountry? kr_country = KRCountry.kr_fromCode(kr_savedCountry);
if (kr_country != null) {
kr_currentCountry.value = kr_country;
return;
}
}
if (AppConfig().kr_is_daytime == false) {
kr_currentCountry.value = KRCountry.ru;
} else {
kr_currentCountry.value = KRCountry.cn;
}
} catch (err) {
KRLogUtil.kr_e('初始化国家设置失败: $err', tag: 'CountryUtil');
kr_currentCountry.value = KRCountry.cn;
}
}
/// 设置当前国家
static Future<void> kr_setCurrentCountry(KRCountry kr_country) async {
try {
await _kr_storage.kr_saveData(
key: _kr_countryKey, value: kr_country.kr_code);
kr_currentCountry.value = kr_country;
} catch (err) {
KRLogUtil.kr_e('设置国家失败: $err', tag: 'CountryUtil');
throw err;
}
}
/// 更新国家并重启服务
static Future<void> kr_updateCountry(KRCountry kr_country) async {
try {
// 更新国家设置
await kr_setCurrentCountry(kr_country);
// 如果服务正在运行,重启服务
if (KRSingBoxImp.instance.kr_status.value == SingboxStarted()) {
await KRSingBoxImp.instance.kr_restart();
}
} catch (err) {
KRLogUtil.kr_e('更新国家失败: $err', tag: 'CountryUtil');
rethrow;
}
}
/// 获取当前国家代码
static String kr_getCurrentCountryCode() {
return kr_currentCountry.value.kr_code;
}
static String kr_getCurrentCountryName() {
return kr_getCountryName(kr_currentCountry.value);
}
/// 获取国家名称
static String kr_getCountryName(KRCountry country) {
switch (country) {
case KRCountry.ir:
return AppTranslations.kr_country.ir;
case KRCountry.cn:
return AppTranslations.kr_country.cn;
case KRCountry.af:
return AppTranslations.kr_country.af;
case KRCountry.ru:
return AppTranslations.kr_country.ru;
case KRCountry.id:
return AppTranslations.kr_country.id;
case KRCountry.tr:
return AppTranslations.kr_country.tr;
case KRCountry.br:
return AppTranslations.kr_country.br;
}
}
/// 获取所有支持的国家列表
static List<KRCountry> kr_getSupportedCountries() {
if (AppConfig().kr_is_daytime == false) {
return KRCountry.values.where((element) => element != KRCountry.cn).toList();
}
return KRCountry.values;
}
/// 获取国家信息列表
static List<Map<String, String>> kr_getCountryInfoList() {
return KRCountry.values
.map((country) => {
'code': country.kr_code,
'name': country.kr_countryName,
})
.toList();
}
}
+79
View File
@@ -0,0 +1,79 @@
import 'package:flutter_udid/flutter_udid.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/app/utils/kr_secure_storage.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'dart:io';
import 'dart:convert';
import 'package:crypto/crypto.dart';
/// 设备工具类
class KRDeviceUtil {
static final KRDeviceUtil _instance = KRDeviceUtil._internal();
/// 设备ID缓存
String? _kr_cachedDeviceId;
/// 存储键
static const String _kr_deviceIdKey = 'kr_device_id';
/// 存储实例
final KRSecureStorage _kr_storage = KRSecureStorage();
KRDeviceUtil._internal();
factory KRDeviceUtil() => _instance;
/// 获取设备ID
/// 如果获取失败,返回空字符串
Future<String> kr_getDeviceId() async {
try {
if (_kr_cachedDeviceId != null) {
return _kr_cachedDeviceId!;
}
// 先从存储中获取
final String? kr_savedDeviceId = await _kr_storage.kr_readData(key: _kr_deviceIdKey);
if (kr_savedDeviceId != null) {
_kr_cachedDeviceId = kr_savedDeviceId;
return _kr_cachedDeviceId!;
}
// 根据不同平台获取设备ID
if (Platform.isMacOS || Platform.isWindows ) {
// 获取系统信息
final PackageInfo kr_packageInfo = await PackageInfo.fromPlatform();
final String kr_platform = Platform.operatingSystem;
final String kr_version = Platform.operatingSystemVersion;
final String kr_localHostname = Platform.localHostname;
// 组合信息生成唯一ID
final String kr_deviceInfo = '$kr_platform-$kr_version-$kr_localHostname-${kr_packageInfo.packageName}-${kr_packageInfo.buildNumber}';
_kr_cachedDeviceId = md5.convert(utf8.encode(kr_deviceInfo)).toString();
} else if (Platform.isIOS || Platform.isAndroid ) {
_kr_cachedDeviceId = await FlutterUdid.udid;
} else {
KRLogUtil.kr_e('不支持的平台: ${Platform.operatingSystem}', tag: 'DeviceUtil');
return '';
}
// 保存到存储中
if (_kr_cachedDeviceId != null) {
await _kr_storage.kr_saveData(key: _kr_deviceIdKey, value: _kr_cachedDeviceId!);
}
KRLogUtil.kr_i('获取设备ID: $_kr_cachedDeviceId', tag: 'DeviceUtil');
return _kr_cachedDeviceId ?? '';
} catch (e) {
KRLogUtil.kr_e('获取设备ID失败: $e', tag: 'DeviceUtil');
return '';
}
}
/// 清除缓存的设备ID
void kr_clearDeviceId() {
_kr_cachedDeviceId = null;
_kr_storage.kr_deleteData(key: _kr_deviceIdKey);
}
}
+102
View File
@@ -0,0 +1,102 @@
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
/// 消息类型枚举
enum KRMessageType {
kr_payment,
kr_subscribe_update,
}
/// 消息数据类
class KRMessageData {
final KRMessageType kr_type; // 消息类型
final Map<String, dynamic> kr_data; // 消息数据
KRMessageData({
required this.kr_type,
required this.kr_data,
});
}
/// 消息总线工具类
class KREventBus {
// 单例模式
static final KREventBus _instance = KREventBus._internal();
factory KREventBus() => _instance;
KREventBus._internal();
// 消息流控制器
final _kr_messageController = Rx<KRMessageData?>(null);
/// 发送消息
void kr_sendMessage(
KRMessageType type, {
Map<String, dynamic> data = const {},
}) {
_kr_messageController.value = KRMessageData(
kr_type: type,
kr_data: data,
);
}
/// 监听特定类型的消息
/// 返回 Worker 对象,可用于手动取消订阅
/// 在控制器的 onClose 方法中调用 worker.dispose() 来取消订阅
/// 示例:
/// ```dart
/// class MyController extends GetxController {
/// Worker? _worker;
///
/// @override
/// void onInit() {
/// super.onInit();
/// _worker = KREventBus().kr_listenMessage(
/// KRMessageType.kr_paymentSuccess,
/// (message) => KRLogUtil.kr_d('收到消息: $message', tag: 'EventBus'),
/// );
/// }
///
/// @override
/// void onClose() {
/// _worker?.dispose();
/// super.onClose();
/// }
/// }
/// ```
Worker kr_listenMessage(
KRMessageType type,
Function(KRMessageData) callback,
) {
return ever(_kr_messageController, (KRMessageData? message) {
if (message != null && message.kr_type == type) {
callback(message);
}
});
}
/// 监听多个类型的消息
/// 返回 Worker 对象,可用于手动取消订阅
/// 在控制器的 onClose 方法中调用 worker.dispose() 来取消订阅
Worker kr_listenMessages(
List<KRMessageType> types,
Function(KRMessageData) callback,
) {
return ever(_kr_messageController, (KRMessageData? message) {
if (message != null && types.contains(message.kr_type)) {
callback(message);
}
});
}
/// 监听所有消息
/// 返回 Worker 对象,可用于手动取消订阅
/// 在控制器的 onClose 方法中调用 worker.dispose() 来取消订阅
Worker kr_listenAllMessages(Function(KRMessageData) callback) {
return ever(_kr_messageController, (KRMessageData? message) {
if (message != null) {
callback(message);
}
});
}
}
+95
View File
@@ -0,0 +1,95 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
// import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'dart:ui' as ui;
/// 地图瓦片缓存工具类
class KRFMTC {
static const String kr_storeName = 'kaer_map_store';
/// HTTP 客户端,用于复用连接
static late final http.Client _httpClient = http.Client();
/// 瓦片提供者实例
static late final TileProvider _tileProvider = NetworkTileProvider();
/// 判断是否在中国大陆
static bool _kr_isInMainlandChina() {
// 获取系统语言
final List<Locale> systemLocales = ui.window.locales;
final String? languageCode = systemLocales.isNotEmpty ? systemLocales.first.languageCode : null;
final String? countryCode = systemLocales.isNotEmpty ? systemLocales.first.countryCode : null;
// 获取系统时区
final String timeZoneName = DateTime.now().timeZoneName;
// 检查是否为中文语言环境
bool isChineseLanguage = languageCode == 'zh';
// 检查是否为中国地区代码
bool isChineseRegion = countryCode == 'CN';
// 检查是否为中国时区
bool isChineseTimezone = timeZoneName.contains('China') || timeZoneName == 'Asia/Shanghai';
// 如果同时满足语言和地区或时区条件,则认为在中国大陆
return (isChineseLanguage && (isChineseRegion || isChineseTimezone));
}
/// 获取地图瓦片URL
static String kr_getTileUrl() {
if (_kr_isInMainlandChina()) {
// 使用高德地图
return 'https://webst0{s}.is.autonavi.com/appmaptile?style=7&x={x}&y={y}&z={z}';
} else {
// 使用 OpenStreetMap
return 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
}
}
/// 获取地图瓦片子域名
static List<String> kr_getTileSubdomains() {
if (_kr_isInMainlandChina()) {
return ['1', '2', '3', '4'];
} else {
return ['a', 'b', 'c'];
}
}
/// 获取地图瓦片提供者
static TileProvider kr_getTileProvider() {
return _tileProvider;
}
/// 初始化地图缓存
static Future<void> kr_initMapCache() async {
// 不使用缓存,无需实现
}
/// 清理地图缓存
static Future<void> kr_clearMapCache() async {
// 不使用缓存,无需实现
}
/// 检查瓦片是否已缓存
static Future<bool> kr_isTileCached({
required TileCoordinates coords,
required TileLayer options,
}) async {
return false;
}
/// 获取缓存状态信息
static Future<({bool isAvailable, int cacheCount, String cacheSize})> kr_checkCacheStatus() async {
return (isAvailable: false, cacheCount: 0, cacheSize: '0 MB');
}
/// 释放资源
static void kr_dispose() {
_httpClient.close();
}
}
+45
View File
@@ -0,0 +1,45 @@
import 'package:loggy/loggy.dart';
/// 日志工具类
class KRLogUtil {
static final KRLogUtil _instance = KRLogUtil._internal();
factory KRLogUtil() => _instance;
KRLogUtil._internal();
/// 初始化日志
static void kr_init() {
Loggy.initLoggy(
logPrinter: PrettyPrinter(),
);
}
/// 调试日志
static void kr_d(String message, {String? tag}) {
Loggy('${tag ?? 'KRLogUtil'}').debug(message);
}
/// 信息日志
static void kr_i(String message, {String? tag}) {
Loggy('${tag ?? 'KRLogUtil'}').info(message);
}
/// 警告日志
static void kr_w(String message, {String? tag}) {
Loggy('${tag ?? 'KRLogUtil'}').warning(message);
}
/// 错误日志
static void kr_e(String message, {String? tag, Object? error, StackTrace? stackTrace}) {
Loggy('${tag ?? 'KRLogUtil'}').error(message, error, stackTrace);
}
/// 网络日志
static void kr_network(String message, {String? tag}) {
Loggy('${tag ?? 'Network'}').info(message);
}
/// 性能日志
static void kr_performance(String message, {String? tag}) {
Loggy('${tag ?? 'Performance'}').info(message);
}
}
+172
View File
@@ -0,0 +1,172 @@
import 'package:flutter/material.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:permission_handler/permission_handler.dart';
import '../widgets/dialogs/kr_dialog.dart';
import '../localization/app_translations.dart';
import 'dart:io' show Platform;
import 'dart:io' show InternetAddress;
/// 网络检查工具类
class KRNetworkCheck {
/// 初始化网络检查
static Future<bool> kr_initialize(BuildContext context, {VoidCallback? onPermissionGranted}) async {
try {
if (Platform.isIOS) {
// iOS 平台特殊处理
final hasPermission = await kr_checkNetworkPermission();
if (!hasPermission) {
// 显示网络设置提示对话框
final bool? result = await kr_showNetworkDialog();
if (result == true) {
// 打开系统设置
await kr_openAppSettings();
// 等待用户返回并检查权限
bool hasPermissionAfterSettings = false;
for (int i = 0; i < 10; i++) {
await Future.delayed(const Duration(seconds: 1));
hasPermissionAfterSettings = await kr_checkNetworkPermission();
if (hasPermissionAfterSettings) {
onPermissionGranted?.call();
return true;
}
}
if (!hasPermissionAfterSettings) {
await kr_showNetworkDialog();
return false;
}
}
return false;
}
} else {
// Android 平台处理
final hasPermission = await kr_checkNetworkPermission();
if (!hasPermission) {
final bool? result = await kr_showNetworkDialog();
if (result == true) {
await kr_openAppSettings();
bool hasPermissionAfterSettings = false;
for (int i = 0; i < 10; i++) {
await Future.delayed(const Duration(seconds: 1));
hasPermissionAfterSettings = await kr_checkNetworkPermission();
if (hasPermissionAfterSettings) {
onPermissionGranted?.call();
return true;
}
}
if (!hasPermissionAfterSettings) {
await kr_showNetworkDialog();
return false;
}
}
return false;
}
}
onPermissionGranted?.call();
return true;
} catch (e) {
debugPrint('网络检查初始化错误: $e');
return false;
}
}
/// 检查网络权限
static Future<bool> kr_checkNetworkPermission() async {
try {
if (Platform.isIOS) {
// iOS 平台使用更可靠的方式检查网络状态
final connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult == ConnectivityResult.none) {
return false;
}
// 尝试进行实际的网络请求测试
try {
final result = await InternetAddress.lookup('www.apple.com');
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} catch (e) {
return false;
}
} else {
// Android 平台保持原有逻辑
final connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult != ConnectivityResult.none;
}
} catch (e) {
debugPrint('网络权限检查错误: $e');
return false;
}
}
/// 检查网络连接状态
static Future<bool> kr_checkNetworkConnection() async {
try {
if (Platform.isIOS) {
// iOS 平台使用更可靠的方式检查网络状态
final connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult == ConnectivityResult.none) {
return false;
}
try {
final result = await InternetAddress.lookup('www.apple.com');
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
} catch (e) {
return false;
}
} else {
final connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult != ConnectivityResult.none;
}
} catch (e) {
debugPrint('网络连接检查错误: $e');
return false;
}
}
/// 监听网络状态变化
static Stream<ConnectivityResult> kr_networkStream() {
return Connectivity().onConnectivityChanged;
}
/// 打开应用设置页面
static Future<void> kr_openAppSettings() async {
if (Platform.isIOS) {
// iOS 平台打开网络设置
try {
await openAppSettings();
} catch (e) {
debugPrint('打开设置页面错误: $e');
}
} else {
await openAppSettings();
}
}
/// 显示网络权限对话框
static Future<bool> kr_showNetworkDialog() async {
bool? result;
await KRDialog.show(
title: Platform.isIOS
? AppTranslations.kr_networkPermission.title
: AppTranslations.kr_networkPermission.title,
message: Platform.isIOS
? AppTranslations.kr_networkPermission.description
: AppTranslations.kr_networkPermission.description,
confirmText: AppTranslations.kr_networkPermission.goToSettings,
cancelText: AppTranslations.kr_networkPermission.cancel,
onConfirm: () async {
result = await openAppSettings();
},
onCancel: () {
result = false;
},
);
return result ?? false;
}
}
+62
View File
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:permission_handler/permission_handler.dart';
/// 网络权限工具类
class KRNetworkPermission {
/// 检查网络权限
static Future<bool> kr_checkNetworkPermission() async {
if (GetPlatform.isIOS) {
// iOS 检查网络权限
final status = await Permission.location.status;
if (!status.isGranted) {
return false;
}
} else if (GetPlatform.isAndroid) {
// Android 检查网络权限
final connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult == ConnectivityResult.none) {
return false;
}
}
return true;
}
/// 请求网络权限
static Future<bool> kr_requestNetworkPermission() async {
if (GetPlatform.isIOS) {
// iOS 请求网络权限
final status = await Permission.location.request();
if (!status.isGranted) {
// 直接打开设置页面
await openAppSettings();
}
return status.isGranted;
} else if (GetPlatform.isAndroid) {
// Android 请求网络权限
final connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult == ConnectivityResult.none) {
// 直接打开设置页面
await openAppSettings();
}
return connectivityResult != ConnectivityResult.none;
}
return true;
}
/// 检查网络连接状态
static Future<bool> kr_checkNetworkConnection() async {
try {
final connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult != ConnectivityResult.none;
} catch (e) {
return false;
}
}
/// 监听网络状态变化
static Stream<ConnectivityResult> kr_networkStream() {
return Connectivity().onConnectivityChanged;
}
}
+30
View File
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import '../widgets/dialogs/kr_dialog.dart';
import '../localization/app_translations.dart';
class KRNetworkStatus {
static Future<bool> checkNetworkStatus() async {
final connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult != ConnectivityResult.none) {
return true;
}
bool? result;
await KRDialog.show(
title: AppTranslations.kr_networkStatus.title,
message: AppTranslations.kr_networkStatus.checkNetwork,
confirmText: AppTranslations.kr_networkStatus.retry,
cancelText: AppTranslations.kr_networkStatus.cancel,
onConfirm: () => result = true,
onCancel: () => result = false,
);
if (result == true) {
return checkNetworkStatus();
}
return false;
}
}
+134
View File
@@ -0,0 +1,134 @@
import 'dart:io';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:crypto/crypto.dart';
import 'dart:convert';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:path_provider/path_provider.dart';
class KRSecureStorage {
// 创建一个单例实例
static final KRSecureStorage _instance = KRSecureStorage._internal();
factory KRSecureStorage() => _instance;
// 私有构造函数
KRSecureStorage._internal();
// 存储箱名称
static const String _boxName = 'kaer_secure_storage';
// 加密密钥
static const String _encryptionKey = 'kaer_secure_storage_key';
// 初始化 Hive
Future<void> kr_initHive() async {
try {
if (Platform.isMacOS) {
final baseDir = await getApplicationSupportDirectory();
await Hive.initFlutter(baseDir.path);
} else {
await Hive.initFlutter();
}
// 使用加密适配器
final key = HiveAesCipher(_generateKey());
await Hive.openBox(_boxName, encryptionCipher: key);
} catch (e) {
KRLogUtil.kr_e('初始化 Hive 失败: $e', tag: 'SecureStorage');
}
}
// 生成加密密钥
List<int> _generateKey() {
final key = utf8.encode(_encryptionKey);
final hash = sha256.convert(key);
return hash.bytes;
}
// 获取存储箱
Box<dynamic> get _box => Hive.box(_boxName);
// 存储数据
Future<void> kr_saveData({required String key, required String value}) async {
try {
await _box.put(key, value);
} catch (e) {
KRLogUtil.kr_e('存储数据失败: $e', tag: 'SecureStorage');
}
}
// 读取数据
Future<String?> kr_readData({required String key}) async {
try {
return _box.get(key) as String?;
} catch (e) {
KRLogUtil.kr_e('读取数据失败: $e', tag: 'SecureStorage');
return null;
}
}
// 删除数据
Future<void> kr_deleteData({required String key}) async {
try {
await _box.delete(key);
} catch (e) {
KRLogUtil.kr_e('删除数据失败: $e', tag: 'SecureStorage');
}
}
// 清除所有数据
Future<void> kr_clearAllData() async {
try {
await _box.clear();
} catch (e) {
KRLogUtil.kr_e('清除数据失败: $e', tag: 'SecureStorage');
}
}
// 检查键是否存在
Future<bool> kr_hasKey({required String key}) async {
try {
return _box.containsKey(key);
} catch (e) {
KRLogUtil.kr_e('检查键失败: $e', tag: 'SecureStorage');
return false;
}
}
// 保存布尔值
Future<void> kr_saveBool({required String key, required bool value}) async {
try {
await _box.put(key, value);
} catch (e) {
KRLogUtil.kr_e('存储布尔值失败: $e', tag: 'SecureStorage');
}
}
// 获取布尔值
Future<bool?> kr_getBool({required String key}) async {
try {
return _box.get(key) as bool?;
} catch (e) {
KRLogUtil.kr_e('读取布尔值失败: $e', tag: 'SecureStorage');
return null;
}
}
// 保存整数
Future<void> kr_saveInt({required String key, required int value}) async {
try {
await _box.put(key, value);
} catch (e) {
KRLogUtil.kr_e('存储整数失败: $e', tag: 'SecureStorage');
}
}
// 获取整数
Future<int?> kr_getInt({required String key}) async {
try {
return _box.get(key) as int?;
} catch (e) {
KRLogUtil.kr_e('读取整数失败: $e', tag: 'SecureStorage');
return null;
}
}
}
+147
View File
@@ -0,0 +1,147 @@
import 'package:get/get.dart';
import 'dart:io' show Platform;
import 'package:url_launcher/url_launcher.dart';
import 'package:kaer_with_panels/app/widgets/dialogs/kr_update_dialog.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../localization/app_translations.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import '../model/response/kr_config_data.dart';
/// 更新工具类
class KRUpdateUtil {
/// 单例模式
static final KRUpdateUtil _instance = KRUpdateUtil._internal();
factory KRUpdateUtil() => _instance;
KRUpdateUtil._internal();
/// 当前更新信息
KRUpdateApplication? _kr_currentUpdateInfo;
/// 初始化更新信息
/// [updateInfo] 更新信息对象
void kr_initUpdateInfo(KRUpdateApplication updateInfo) {
_kr_currentUpdateInfo = updateInfo;
}
/// 检查更新
Future<void> kr_checkUpdate() async {
if (_kr_currentUpdateInfo == null) {
return;
}
// 获取当前应用版本号
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
final String currentVersion = packageInfo.version;
// 比较版本号
if (_kr_compareVersions(currentVersion, _kr_currentUpdateInfo!.kr_version) <
0) {
_kr_showUpdateDialog(_kr_currentUpdateInfo!);
}
}
/// 比较版本号
/// 返回值:
/// -1: 当前版本小于目标版本
/// 0: 当前版本等于目标版本
/// 1: 当前版本大于目标版本
int _kr_compareVersions(String currentVersion, String targetVersion) {
return currentVersion.compareTo(targetVersion);
}
/// 显示更新对话框
void _kr_showUpdateDialog(KRUpdateApplication updateInfo) {
// 获取更新内容
String? updateContent = updateInfo.kr_version_description;
// 如果更新内容为空,使用默认内容
if (updateContent?.isEmpty ?? true) {
updateContent = AppTranslations.kr_update.content;
}
KRUpdateDialog.show(
version: updateInfo.kr_version,
updateContent: updateContent,
onConfirm: () {
_kr_handleUpdate(updateInfo);
},
onCancel: () => Get.back(),
showCancelButton: true,
);
}
/// 处理更新
Future<void> _kr_handleUpdate(KRUpdateApplication updateInfo) async {
if (updateInfo.kr_url.isEmpty) {
Get.back();
return;
}
try {
final Uri uri = Uri.parse(updateInfo.kr_url);
if (Platform.isAndroid) {
// Android 平台处理
await _kr_handleAndroidUpdate(uri);
} else if (Platform.isIOS) {
// iOS 平台处理
await _kr_handleIOSUpdate(uri);
} else if (Platform.isMacOS) {
// macOS 平台处理
await _kr_handleMacOSUpdate(uri);
} else if (Platform.isWindows) {
// Windows 平台处理
await _kr_handleWindowsUpdate(uri);
} else if (Platform.isLinux) {
// Linux 平台处理
await _kr_handleLinuxUpdate(uri);
}
} catch (e) {
KRLogUtil.kr_e('更新处理错误: $e', tag: 'UpdateUtil');
} finally {
Get.back();
}
}
/// 处理 Android 平台更新
Future<void> _kr_handleAndroidUpdate(Uri uri) async {
// Android 平台通常是下载 APK 文件
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
/// 处理 iOS 平台更新
Future<void> _kr_handleIOSUpdate(Uri uri) async {
// iOS 平台通常是跳转到 App Store
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
/// 处理 macOS 平台更新
Future<void> _kr_handleMacOSUpdate(Uri uri) async {
// macOS 平台通常是下载 DMG 文件或跳转到 Mac App Store
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
/// 处理 Windows 平台更新
Future<void> _kr_handleWindowsUpdate(Uri uri) async {
// Windows 平台通常是下载 exe 安装文件
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
/// 处理 Linux 平台更新
Future<void> _kr_handleLinuxUpdate(Uri uri) async {
// Linux 平台通常是下载安装包文件
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
}
+224
View File
@@ -0,0 +1,224 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:kaer_with_panels/app/localization/kr_language_utils.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import 'package:kaer_with_panels/singbox/model/singbox_status.dart';
import 'package:kaer_with_panels/app/localization/app_translations.dart';
import 'package:window_manager/window_manager.dart';
import 'package:tray_manager/tray_manager.dart';
import 'dart:io' show Platform;
import '../services/singbox_imp/kr_sing_box_imp.dart';
class KRWindowManager with WindowListener, TrayListener {
static final KRWindowManager _instance = KRWindowManager._internal();
factory KRWindowManager() => _instance;
KRWindowManager._internal();
/// 初始化窗口管理器
Future<void> kr_initWindowManager() async {
KRLogUtil.kr_i('kr_initWindowManager: 开始初始化窗口管理器');
await windowManager.ensureInitialized();
KRLogUtil.kr_i('kr_initWindowManager: 窗口管理器已初始化');
const WindowOptions windowOptions = WindowOptions(
size: Size(800, 668),
minimumSize: Size(800, 668),
center: true,
backgroundColor: Colors.white,
skipTaskbar: false,
title: 'Kaer VPN',
titleBarStyle: TitleBarStyle.normal,
windowButtonVisibility: true,
);
await windowManager.waitUntilReadyToShow(windowOptions);
KRLogUtil.kr_i('kr_initWindowManager: 窗口准备就绪');
// 先添加监听器
windowManager.addListener(this);
KRLogUtil.kr_i('kr_initWindowManager: 已添加窗口监听器');
// 确保在 Windows 下正确设置窗口属性
if (Platform.isWindows) {
await windowManager.setTitleBarStyle(TitleBarStyle.normal);
await windowManager.setTitle('BearVPN');
await windowManager.setSize(const Size(800, 668));
await windowManager.setMinimumSize(const Size(800, 668));
await windowManager.center();
await windowManager.show();
// 阻止窗口关闭
await windowManager.setPreventClose(true);
} else {
await windowManager.setTitle('Kaer VPN');
await windowManager.setSize(const Size(800, 668));
await windowManager.setMinimumSize(const Size(800, 668));
await windowManager.center();
}
// 初始化托盘
await _initTray();
// 初始化平台通道
_initPlatformChannel();
KRLogUtil.kr_i('kr_initWindowManager: 初始化完成');
ever(KRLanguageUtils.kr_language, (_) {
final Menu menu = Menu(
items: [
MenuItem(
label: AppTranslations.kr_tray.openDashboard,
onClick: (_) => _showWindow(),
),
if (Platform.isMacOS) MenuItem.separator(),
if (Platform.isMacOS)
MenuItem(
label: AppTranslations.kr_tray.copyToTerminal,
onClick: (_) => _copyToTerminal(),
),
MenuItem.separator(),
MenuItem(
label: AppTranslations.kr_tray.exitApp,
onClick: (_) => _exitApp(),
),
],
);
trayManager.setContextMenu(menu);
});
}
/// 初始化平台通道
void _initPlatformChannel() {
if (Platform.isMacOS) {
const platform = MethodChannel('kaer_vpn/terminate');
platform.setMethodCallHandler((call) async {
if (call.method == 'onTerminate') {
KRLogUtil.kr_i('收到应用终止通知');
await _handleTerminate();
}
});
}
}
/// 初始化托盘
Future<void> _initTray() async {
KRLogUtil.kr_i('_initTray: 开始初始化托盘');
trayManager.addListener(this);
final String iconPath = Platform.isMacOS
? 'assets/images/tray_icon.png'
: 'assets/images/tray_icon.ico';
await trayManager.setIcon(iconPath);
// 初始化托盘
Future.delayed(const Duration(seconds: 1), () async {
final Menu menu = Menu(
items: [
MenuItem(
label: AppTranslations.kr_tray.openDashboard,
onClick: (_) => _showWindow(),
),
if (Platform.isMacOS) MenuItem.separator(),
if (Platform.isMacOS)
MenuItem(
label: AppTranslations.kr_tray.copyToTerminal,
onClick: (_) => _copyToTerminal(),
),
MenuItem.separator(),
MenuItem(
label: AppTranslations.kr_tray.exitApp,
onClick: (_) => _exitApp(),
),
],
);
await trayManager.setContextMenu(menu);
});
}
/// 复制到终端
Future<void> _copyToTerminal() async {
final String kr_port = KRSingBoxImp.instance.kr_port.toString();
final String proxyText =
'export https_proxy=http://127.0.0.1:$kr_port http_proxy=http://127.0.0.1:$kr_port all_proxy=socks5://127.0.0.1:$kr_port';
await Clipboard.setData(ClipboardData(text: proxyText));
}
/// 退出应用
Future<void> _exitApp() async {
KRLogUtil.kr_i('_exitApp: 退出应用');
await _handleTerminate();
await windowManager.destroy();
}
/// 显示窗口
Future<void> _showWindow() async {
KRLogUtil.kr_i('_showWindow: 开始显示窗口');
try {
await windowManager.show();
await windowManager.focus();
await windowManager.setSkipTaskbar(false);
await windowManager.setAlwaysOnTop(true);
await Future.delayed(const Duration(milliseconds: 100));
await windowManager.setAlwaysOnTop(false);
KRLogUtil.kr_i('_showWindow: 窗口显示成功');
} catch (e) {
KRLogUtil.kr_e('_showWindow: 显示窗口失败 - $e');
}
}
@override
void onWindowEvent(String eventName) {
KRLogUtil.kr_i('onWindowEvent: 收到窗口事件 - $eventName');
// 移除 Windows 下自动显示窗口的逻辑
}
@override
void onWindowClose() async {
if (Platform.isWindows) {
await windowManager.hide();
} else if (Platform.isMacOS) {
await windowManager.hide();
}
}
@override
void onTrayIconMouseDown() {
// 左键点击只显示菜单
trayManager.popUpContextMenu();
}
@override
void onTrayIconRightMouseDown() {
// 右键点击只显示菜单
trayManager.popUpContextMenu();
}
@override
void onWindowFocus() {
// 移除自动显示窗口的逻辑
}
@override
void onWindowBlur() {
// 当窗口失去焦点时,保持窗口可见
}
/// 设置窗口背景颜色
Future<void> kr_setBackgroundColor(Color color) async {
await windowManager.setBackgroundColor(color);
}
/// 处理应用终止
Future<void> _handleTerminate() async {
KRLogUtil.kr_i('_handleTerminate: 处理应用终止');
if (KRSingBoxImp.instance.kr_status == SingboxStatus.started()) {
await KRSingBoxImp.instance.kr_stop();
}
await trayManager.destroy();
}
}