Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
5113733ab2 Add sqlite store
Optimize android quick action

Optimize backup and restore

Optimize more details
2026-01-23 13:49:18 +08:00
9 changed files with 70 additions and 112 deletions

View File

@@ -64,7 +64,7 @@ android {
buildTypes {
debug {
isMinifyEnabled = false
applicationIdSuffix = ".dev"
applicationIdSuffix = ".debug"
}
release {

View File

@@ -14,26 +14,20 @@ class Migration {
return _instance!;
}
Future<Config> migrationIfNeeded(
Map<String, Object?>? configMap, {
required Future<Config> Function(MigrationData data) sync,
}) async {
Future<MigrationData> migrationIfNeeded(
Map<String, Object?>? configMap,
) async {
_oldVersion = await preferences.getVersion();
MigrationData data = MigrationData(configMap: configMap);
if (_oldVersion == currentVersion) {
return Config.realFromJson(configMap);
}
if (_oldVersion == 0 && configMap != null) {
final clashConfigMap = await preferences.getClashConfigMap();
if (clashConfigMap != null) {
configMap['patchClashConfig'] = clashConfigMap;
await preferences.clearClashConfig();
}
data = await _oldToNow(configMap);
}
final res = await sync(data);
await preferences.setVersion(currentVersion);
return res;
return data;
}
Future<void> rollback() async {
await preferences.setVersion(_oldVersion);
}
Future<MigrationData> _oldToNow(Map<String, Object?> configMap) async {

View File

@@ -40,36 +40,11 @@ class Preferences {
}
Future<Map<String, Object?>?> getConfigMap() async {
try {
final preferences = await sharedPreferencesCompleter.future;
final configString = preferences?.getString(configKey);
if (configString == null) return null;
final Map<String, Object?>? configMap = json.decode(configString);
return configMap;
} catch (_) {
return null;
}
}
Future<Map<String, Object?>?> getClashConfigMap() async {
try {
final preferences = await sharedPreferencesCompleter.future;
final clashConfigString = preferences?.getString(clashConfigKey);
if (clashConfigString == null) return null;
return json.decode(clashConfigString);
} catch (_) {
return null;
}
}
Future<void> clearClashConfig() async {
try {
final preferences = await sharedPreferencesCompleter.future;
await preferences?.remove(clashConfigKey);
return;
} catch (_) {
return;
}
final preferences = await sharedPreferencesCompleter.future;
final configString = preferences?.getString(configKey);
if (configString == null) return null;
final Map<String, Object?>? configMap = json.decode(configString);
return configMap;
}
Future<Config?> getConfig() async {

View File

@@ -16,9 +16,6 @@ class CommonPrint {
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
final payload = '[APP] $text';
debugPrint(payload);
if (!appController.isAttach) {
return;
}
appController.addLog(Log.app(payload).copyWith(logLevel: logLevel));
}
}

View File

@@ -402,7 +402,6 @@ Future<MigrationData> _oldToNowTask(
final addedRules = standardOverwrite['addedRules'] as List? ?? [];
for (final addRule in addedRules) {
final id = idMap.updateCacheValue(addRule['id'], () => snowflake.id);
addRule['id'] = id;
rules.add(Rule.fromJson(addRule));
links.add(
ProfileRuleLink(
@@ -431,9 +430,9 @@ Future<MigrationData> _oldToNowTask(
final scriptOverwrite = overwrite['scriptOverwrite'] as Map?;
if (scriptOverwrite != null) {
final scriptId = scriptOverwrite['scriptId'] as String?;
rawProfile['scriptId'] = scriptId != null ? idMap[scriptId] : null;
configMap['scriptId'] = scriptId != null ? idMap[scriptId] : null;
}
rawProfile['overwriteType'] = overwrite['type'];
configMap['overwriteType'] = overwrite['type'];
}
final sourceFile = File(_getProfilePath(sourcePath, rawId));
@@ -443,8 +442,9 @@ Future<MigrationData> _oldToNowTask(
}
final currentProfileId = configMap['currentProfileId'];
configMap['currentProfileId'] = currentProfileId != null
? idMap[currentProfileId]
? idMap[currentProfileId.toString()]
: null;
return MigrationData(
configMap: configMap,
profiles: profiles,

View File

@@ -20,7 +20,6 @@ import 'providers/database.dart';
class AppController {
late final BuildContext _context;
late final WidgetRef _ref;
bool isAttach = false;
static AppController? _instance;
@@ -35,12 +34,17 @@ class AppController {
_context = context;
_ref = ref;
await _init();
isAttach = true;
}
}
extension InitControllerExt on AppController {
Future<void> _init() async {
FlutterError.onError = (details) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
logLevel: LogLevel.warning,
);
};
try {
updateTray();
autoUpdateProfiles();

View File

@@ -243,11 +243,4 @@ abstract class Config with _$Config {
}) = _Config;
factory Config.fromJson(Map<String, Object?> json) => _$ConfigFromJson(json);
factory Config.realFromJson(Map<String, Object?>? json) {
if (json == null) {
return Config(themeProps: defaultThemeProps);
}
return _$ConfigFromJson(json);
}
}

View File

@@ -323,7 +323,6 @@ class NetworkDetection extends _$NetworkDetection
with AutoDisposeNotifierMixin {
bool? _preIsStart;
CancelToken? _cancelToken;
int _startMillisecondsEpoch = 0;
@override
NetworkDetectionState build() {
@@ -345,9 +344,6 @@ class NetworkDetection extends _$NetworkDetection
if (!isStart && _preIsStart == false && state.ipInfo != null) {
return;
}
final millisecondsEpoch = DateTime.now().millisecondsSinceEpoch;
_startMillisecondsEpoch = millisecondsEpoch;
final runTime = millisecondsEpoch + 1;
_cancelToken?.cancel();
_cancelToken = CancelToken();
commonPrint.log('checkIp start');
@@ -355,7 +351,7 @@ class NetworkDetection extends _$NetworkDetection
_preIsStart = isStart;
final res = await request.checkIp(cancelToken: _cancelToken);
commonPrint.log('checkIp res: $res');
if (res.isError && runTime > _startMillisecondsEpoch) {
if (res.isError) {
state = state.copyWith(isLoading: true, ipInfo: null);
return;
}

View File

@@ -22,7 +22,6 @@ import 'package:url_launcher/url_launcher.dart';
import 'common/common.dart';
import 'database/database.dart';
import 'enum/enum.dart';
import 'l10n/l10n.dart';
import 'models/models.dart';
@@ -56,12 +55,6 @@ class GlobalState {
}
Future<ProviderContainer> init(int version) async {
FlutterError.onError = (details) {
commonPrint.log(
'exception: ${details.exception} stack: ${details.stack}',
logLevel: LogLevel.warning,
);
};
coreSHA256 = const String.fromEnvironment('CORE_SHA256');
isPre = const String.fromEnvironment('APP_ENV') != 'stable';
await _initDynamicColor();
@@ -78,45 +71,51 @@ class GlobalState {
}
Future<ProviderContainer> _initData(int version) async {
final appState = AppState(
brightness: WidgetsBinding.instance.platformDispatcher.platformBrightness,
version: version,
viewSize: Size.zero,
requests: FixedList(maxLength),
logs: FixedList(maxLength),
traffics: FixedList(30),
totalTraffic: Traffic(),
systemUiOverlayStyle: const SystemUiOverlayStyle(),
);
final appStateOverrides = buildAppStateOverrides(appState);
packageInfo = await PackageInfo.fromPlatform();
final configMap = await preferences.getConfigMap();
final config = await migration.migrationIfNeeded(
configMap,
sync: (data) async {
final newConfigMap = data.configMap;
final config = newConfigMap != null
? Config.fromJson(newConfigMap)
: Config(themeProps: defaultThemeProps);
await Future.wait([
database.restore(data.profiles, data.scripts, data.rules, data.links),
preferences.saveConfig(config),
]);
return config;
},
);
final configOverrides = buildConfigOverrides(config);
final container = ProviderContainer(
overrides: [...appStateOverrides, ...configOverrides],
);
final profiles = await database.profilesDao.all().get();
container.read(profilesProvider.notifier).setAndReorder(profiles);
await AppLocalizations.load(
utils.getLocaleForString(config.appSettingProps.locale) ??
WidgetsBinding.instance.platformDispatcher.locale,
);
await window?.init(version, config.windowProps);
return container;
try {
final appState = AppState(
brightness:
WidgetsBinding.instance.platformDispatcher.platformBrightness,
version: version,
viewSize: Size.zero,
requests: FixedList(maxLength),
logs: FixedList(maxLength),
traffics: FixedList(30),
totalTraffic: Traffic(),
systemUiOverlayStyle: const SystemUiOverlayStyle(),
);
final appStateOverrides = buildAppStateOverrides(appState);
packageInfo = await PackageInfo.fromPlatform();
final configMap = await preferences.getConfigMap();
final migrationData = await migration.migrationIfNeeded(configMap);
final newConfigMap = migrationData.configMap;
final config = newConfigMap != null
? Config.fromJson(newConfigMap)
: Config(themeProps: defaultThemeProps);
final configOverrides = buildConfigOverrides(config);
await database.restore(
migrationData.profiles,
migrationData.scripts,
migrationData.rules,
migrationData.links,
);
final container = ProviderContainer(
overrides: [...appStateOverrides, ...configOverrides],
);
final profiles = await database.profilesDao.all().get();
container.read(profilesProvider.notifier).setAndReorder(profiles);
await AppLocalizations.load(
utils.getLocaleForString(config.appSettingProps.locale) ??
WidgetsBinding.instance.platformDispatcher.locale,
);
await window?.init(version, config.windowProps);
return container;
} catch (e) {
await migration.rollback();
commonPrint.log('init failed $e');
rethrow;
}
}
Future<void> startUpdateTasks([UpdateTasks? tasks]) async {