Change initialization and logging
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_loggy/flutter_loggy.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
|
import 'package:hiddify/core/analytics/analytics_controller.dart';
|
||||||
import 'package:hiddify/core/app_info/app_info_provider.dart';
|
import 'package:hiddify/core/app_info/app_info_provider.dart';
|
||||||
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
|
import 'package:hiddify/core/logger/logger.dart';
|
||||||
|
import 'package:hiddify/core/logger/logger_controller.dart';
|
||||||
import 'package:hiddify/core/model/environment.dart';
|
import 'package:hiddify/core/model/environment.dart';
|
||||||
import 'package:hiddify/core/preferences/general_preferences.dart';
|
import 'package:hiddify/core/preferences/general_preferences.dart';
|
||||||
import 'package:hiddify/core/preferences/preferences_migration.dart';
|
import 'package:hiddify/core/preferences/preferences_migration.dart';
|
||||||
@@ -18,7 +22,6 @@ import 'package:hiddify/features/profile/notifier/active_profile_notifier.dart';
|
|||||||
import 'package:hiddify/features/system_tray/system_tray_controller.dart';
|
import 'package:hiddify/features/system_tray/system_tray_controller.dart';
|
||||||
import 'package:hiddify/services/auto_start_service.dart';
|
import 'package:hiddify/services/auto_start_service.dart';
|
||||||
import 'package:hiddify/services/deep_link_service.dart';
|
import 'package:hiddify/services/deep_link_service.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
|
||||||
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
@@ -26,20 +29,20 @@ import 'package:loggy/loggy.dart';
|
|||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
final _logger = Loggy('bootstrap');
|
|
||||||
const _testCrashReport = false;
|
|
||||||
final _loggers = MultiLogPrinter(const PrettyPrinter(), []);
|
|
||||||
|
|
||||||
Future<void> lazyBootstrap(
|
Future<void> lazyBootstrap(
|
||||||
WidgetsBinding widgetsBinding,
|
WidgetsBinding widgetsBinding,
|
||||||
Environment env,
|
Environment env,
|
||||||
) async {
|
) async {
|
||||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||||
if (PlatformUtils.isDesktop) await windowManager.ensureInitialized();
|
|
||||||
|
|
||||||
final sentryLogger = SentryLoggyIntegration();
|
Loggy.initLoggy(logPrinter: const PrettyDeveloperPrinter());
|
||||||
_loggers.addPrinter(sentryLogger);
|
|
||||||
Loggy.initLoggy();
|
FlutterError.onError = Logger.logFlutterError;
|
||||||
|
WidgetsBinding.instance.platformDispatcher.onError =
|
||||||
|
Logger.logPlatformDispatcherError;
|
||||||
|
|
||||||
|
final stopWatch = Stopwatch()..start();
|
||||||
|
if (PlatformUtils.isDesktop) await windowManager.ensureInitialized();
|
||||||
|
|
||||||
final container = ProviderContainer(
|
final container = ProviderContainer(
|
||||||
overrides: [
|
overrides: [
|
||||||
@@ -47,65 +50,65 @@ Future<void> lazyBootstrap(
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
final appInfo = await container.read(appInfoProvider.future);
|
await _init(
|
||||||
|
"directories",
|
||||||
|
() => container.read(appDirectoriesProvider.future),
|
||||||
|
);
|
||||||
|
LoggerController.init(container.read(logPathResolverProvider).appFile().path);
|
||||||
|
|
||||||
await container.read(sharedPreferencesProvider.future);
|
final appInfo = await _init(
|
||||||
final enableAnalytics = container.read(enableAnalyticsProvider);
|
"app info",
|
||||||
|
() => container.read(appInfoProvider.future),
|
||||||
|
);
|
||||||
|
await _init(
|
||||||
|
"preferences",
|
||||||
|
() => container.read(sharedPreferencesProvider.future),
|
||||||
|
);
|
||||||
|
|
||||||
await SentryFlutter.init(
|
final enableAnalytics = container.read(analyticsControllerProvider);
|
||||||
(options) {
|
if (enableAnalytics) {
|
||||||
if ((enableAnalytics && !kDebugMode) || _testCrashReport) {
|
await _init(
|
||||||
options.dsn = Environment.sentryDSN;
|
"analytics",
|
||||||
} else {
|
() => container
|
||||||
options.dsn = "";
|
.read(analyticsControllerProvider.notifier)
|
||||||
}
|
.enableAnalytics(),
|
||||||
|
|
||||||
options.environment = env.name;
|
|
||||||
options.dist = appInfo.release.name;
|
|
||||||
options.debug = kDebugMode;
|
|
||||||
options.enableNativeCrashHandling = true;
|
|
||||||
options.enableNdkScopeSync = true;
|
|
||||||
options.attachThreads = true;
|
|
||||||
options.tracesSampleRate = 0.25;
|
|
||||||
options.enableUserInteractionTracing = true;
|
|
||||||
options.addIntegration(sentryLogger);
|
|
||||||
options.beforeSend = sentryBeforeSend;
|
|
||||||
options.logger = (level, message, {exception, logger, stackTrace}) {
|
|
||||||
if (level == SentryLevel.fatal) {
|
|
||||||
_logger.debug(message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
appRunner: () => _lazyBootstrap(widgetsBinding, container, env),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _lazyBootstrap(
|
await _init(
|
||||||
WidgetsBinding widgetsBinding,
|
"preferences migration",
|
||||||
ProviderContainer container,
|
() async {
|
||||||
Environment env,
|
|
||||||
) async {
|
|
||||||
try {
|
try {
|
||||||
await PreferencesMigration(
|
await PreferencesMigration(
|
||||||
sharedPreferences: container.read(sharedPreferencesProvider).requireValue,
|
sharedPreferences:
|
||||||
|
container.read(sharedPreferencesProvider).requireValue,
|
||||||
).migrate();
|
).migrate();
|
||||||
} catch (e) {
|
} catch (e, stackTrace) {
|
||||||
_logger.error("preferences migration failed", e);
|
Logger.bootstrap.error("preferences migration failed", e, stackTrace);
|
||||||
if (env == Environment.dev) rethrow;
|
if (env == Environment.dev) rethrow;
|
||||||
_logger.info("clearing preferences");
|
Logger.bootstrap.info("clearing preferences");
|
||||||
await container.read(sharedPreferencesProvider).requireValue.clear();
|
await container.read(sharedPreferencesProvider).requireValue.clear();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
final debug = container.read(debugModeNotifierProvider) || kDebugMode;
|
final debug = container.read(debugModeNotifierProvider) || kDebugMode;
|
||||||
|
|
||||||
final filesEditor = container.read(filesEditorServiceProvider);
|
await _init(
|
||||||
await filesEditor.init();
|
"logs repository",
|
||||||
await container.read(logRepositoryProvider.future);
|
() => container.read(logRepositoryProvider.future),
|
||||||
await container.read(geoAssetRepositoryProvider.future);
|
);
|
||||||
await container.read(profileRepositoryProvider.future);
|
await _init("logger controller", () => LoggerController.postInit(debug));
|
||||||
|
Logger.bootstrap.info(appInfo.format());
|
||||||
|
|
||||||
initLoggers(container.read, debug);
|
await _init(
|
||||||
_logger.info(container.read(appInfoProvider).requireValue.format());
|
"geo assets repository",
|
||||||
|
() => container.read(geoAssetRepositoryProvider.future),
|
||||||
|
);
|
||||||
|
await _init(
|
||||||
|
"profile repository",
|
||||||
|
() => container.read(profileRepositoryProvider.future),
|
||||||
|
);
|
||||||
|
|
||||||
final silentStart = container.read(silentStartNotifierProvider);
|
final silentStart = container.read(silentStartNotifierProvider);
|
||||||
if (silentStart) {
|
if (silentStart) {
|
||||||
@@ -113,31 +116,41 @@ Future<void> _lazyBootstrap(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (PlatformUtils.isDesktop) {
|
if (PlatformUtils.isDesktop) {
|
||||||
_logger.debug("initializing [Auto Start Service] and [Window Controller]");
|
await _init(
|
||||||
await container.read(autoStartServiceProvider.future);
|
"auto start service",
|
||||||
await container.read(windowControllerProvider.future);
|
() => container.read(autoStartServiceProvider.future),
|
||||||
}
|
);
|
||||||
|
await _init(
|
||||||
await container.read(singboxServiceProvider).init();
|
"window controller",
|
||||||
_logger.debug("initialized [Singbox Service]");
|
() => container.read(windowControllerProvider.future),
|
||||||
|
|
||||||
await container.read(activeProfileProvider.future);
|
|
||||||
await container.read(deepLinkServiceProvider.future);
|
|
||||||
if (PlatformUtils.isDesktop) {
|
|
||||||
try {
|
|
||||||
await container
|
|
||||||
.read(systemTrayControllerProvider.future)
|
|
||||||
.timeout(const Duration(seconds: 1));
|
|
||||||
_logger.debug("initialized [System Tray Controller]");
|
|
||||||
} catch (error, stackTrace) {
|
|
||||||
_logger.warning(
|
|
||||||
"error initializing [System Tray Controller]",
|
|
||||||
error,
|
|
||||||
stackTrace,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _init(
|
||||||
|
"sing-box",
|
||||||
|
() => container.read(singboxServiceProvider).init(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await _safeInit(
|
||||||
|
"active profile",
|
||||||
|
() => container.read(activeProfileProvider.future),
|
||||||
|
);
|
||||||
|
await _init(
|
||||||
|
"deep link service",
|
||||||
|
() => container.read(deepLinkServiceProvider.future),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (PlatformUtils.isDesktop) {
|
||||||
|
await _safeInit(
|
||||||
|
"system tray",
|
||||||
|
() => container.read(systemTrayControllerProvider.future),
|
||||||
|
timeout: 1000,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.bootstrap.info("bootstrap took [${stopWatch.elapsedMilliseconds}ms]");
|
||||||
|
stopWatch.stop();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
parent: container,
|
parent: container,
|
||||||
@@ -150,19 +163,36 @@ Future<void> _lazyBootstrap(
|
|||||||
if (!silentStart) FlutterNativeSplash.remove();
|
if (!silentStart) FlutterNativeSplash.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
void initLoggers(
|
Future<T> _init<T>(
|
||||||
Result Function<Result>(ProviderListenable<Result>) read,
|
String name,
|
||||||
bool debug,
|
Future<T> Function() initializer, {
|
||||||
) {
|
int? timeout,
|
||||||
final logLevel = debug ? LogLevel.all : LogLevel.info;
|
}) async {
|
||||||
final logToFile = debug || (!Platform.isAndroid && !Platform.isIOS);
|
final stopWatch = Stopwatch()..start();
|
||||||
if (logToFile) {
|
Future<T> func() => timeout != null
|
||||||
_loggers.addPrinter(
|
? initializer().timeout(Duration(milliseconds: timeout))
|
||||||
FileLogPrinter(read(logPathResolverProvider).appFile().path),
|
: initializer();
|
||||||
);
|
try {
|
||||||
|
final result = await func();
|
||||||
|
Logger.bootstrap
|
||||||
|
.debug("[$name] initialized in ${stopWatch.elapsedMilliseconds}ms");
|
||||||
|
return result;
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
Logger.bootstrap.error("[$name] error initializing", e, stackTrace);
|
||||||
|
rethrow;
|
||||||
|
} finally {
|
||||||
|
stopWatch.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<T?> _safeInit<T>(
|
||||||
|
String name,
|
||||||
|
Future<T> Function() initializer, {
|
||||||
|
int? timeout,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
return await _init(name, initializer, timeout: timeout);
|
||||||
|
} catch (_) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
Loggy.initLoggy(
|
|
||||||
logPrinter: _loggers,
|
|
||||||
logOptions: LogOptions(logLevel),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
67
lib/core/analytics/analytics_controller.dart
Normal file
67
lib/core/analytics/analytics_controller.dart
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hiddify/core/analytics/analytics_filter.dart';
|
||||||
|
import 'package:hiddify/core/analytics/analytics_logger.dart';
|
||||||
|
import 'package:hiddify/core/app_info/app_info_provider.dart';
|
||||||
|
import 'package:hiddify/core/logger/logger_controller.dart';
|
||||||
|
import 'package:hiddify/core/model/environment.dart';
|
||||||
|
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
||||||
|
import 'package:hiddify/utils/custom_loggers.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
part 'analytics_controller.g.dart';
|
||||||
|
|
||||||
|
const String enableAnalyticsPrefKey = "enable_analytics";
|
||||||
|
|
||||||
|
bool _testCrashReport = false;
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class AnalyticsController extends _$AnalyticsController with AppLogger {
|
||||||
|
@override
|
||||||
|
bool build() {
|
||||||
|
return _preferences.getBool(enableAnalyticsPrefKey) ?? true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferences get _preferences =>
|
||||||
|
ref.read(sharedPreferencesProvider).requireValue;
|
||||||
|
|
||||||
|
Future<void> enableAnalytics() async {
|
||||||
|
loggy.debug("enabling analytics");
|
||||||
|
if (!state) {
|
||||||
|
await _preferences.setBool(enableAnalyticsPrefKey, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final env = ref.read(environmentProvider);
|
||||||
|
final appInfo = await ref.read(appInfoProvider.future);
|
||||||
|
final dsn = !kDebugMode || _testCrashReport ? Environment.sentryDSN : "";
|
||||||
|
final sentryLogger = SentryLoggyIntegration();
|
||||||
|
LoggerController.instance.addPrinter("analytics", sentryLogger);
|
||||||
|
|
||||||
|
await SentryFlutter.init(
|
||||||
|
(options) {
|
||||||
|
options.dsn = dsn;
|
||||||
|
options.environment = env.name;
|
||||||
|
options.dist = appInfo.release.name;
|
||||||
|
options.debug = kDebugMode;
|
||||||
|
options.enableNativeCrashHandling = true;
|
||||||
|
options.enableNdkScopeSync = true;
|
||||||
|
options.attachThreads = true;
|
||||||
|
options.tracesSampleRate = 0.20;
|
||||||
|
options.enableUserInteractionTracing = true;
|
||||||
|
options.addIntegration(sentryLogger);
|
||||||
|
options.beforeSend = sentryBeforeSend;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> disableAnalytics() async {
|
||||||
|
loggy.debug("disabling analytics");
|
||||||
|
await _preferences.setBool(enableAnalyticsPrefKey, false);
|
||||||
|
await Sentry.close();
|
||||||
|
LoggerController.instance.removePrinter("analytics");
|
||||||
|
state = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/core/analytics/analytics_filter.dart
Normal file
29
lib/core/analytics/analytics_filter.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:hiddify/core/model/failures.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
|
FutureOr<SentryEvent?> sentryBeforeSend(SentryEvent event, {Hint? hint}) {
|
||||||
|
if (canSendEvent(event.throwable)) return event;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canSendEvent(dynamic throwable) {
|
||||||
|
return switch (throwable) {
|
||||||
|
UnexpectedFailure(:final error) => canSendEvent(error),
|
||||||
|
DioException _ => false,
|
||||||
|
SocketException _ => false,
|
||||||
|
HttpException _ => false,
|
||||||
|
HandshakeException _ => false,
|
||||||
|
ExpectedFailure _ => false,
|
||||||
|
ExpectedMeasuredFailure _ => false,
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canLogEvent(dynamic throwable) => switch (throwable) {
|
||||||
|
ExpectedMeasuredFailure _ => true,
|
||||||
|
_ => canSendEvent(throwable),
|
||||||
|
};
|
||||||
@@ -2,12 +2,12 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
import 'package:hiddify/services/files_editor_service.dart';
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
LazyDatabase openConnection() {
|
LazyDatabase openConnection() {
|
||||||
return LazyDatabase(() async {
|
return LazyDatabase(() async {
|
||||||
final dbDir = await FilesEditorService.getDatabaseDirectory();
|
final dbDir = await AppDirectories.getDatabaseDirectory();
|
||||||
final file = File(p.join(dbDir.path, 'db.sqlite'));
|
final file = File(p.join(dbDir.path, 'db.sqlite'));
|
||||||
return NativeDatabase.createInBackground(file);
|
return NativeDatabase.createInBackground(file);
|
||||||
});
|
});
|
||||||
|
|||||||
56
lib/core/directories/directories_provider.dart
Normal file
56
lib/core/directories/directories_provider.dart
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:hiddify/core/model/directories.dart';
|
||||||
|
import 'package:hiddify/utils/custom_loggers.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'directories_provider.g.dart';
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class AppDirectories extends _$AppDirectories with InfraLogger {
|
||||||
|
final _methodChannel = const MethodChannel("app.hiddify.com/platform");
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Directories> build() async {
|
||||||
|
final Directories dirs;
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
final paths = await _methodChannel.invokeMethod<Map>("get_paths");
|
||||||
|
loggy.debug("paths: $paths");
|
||||||
|
dirs = (
|
||||||
|
baseDir: Directory(paths?["base"]! as String),
|
||||||
|
workingDir: Directory(paths?["working"]! as String),
|
||||||
|
tempDir: Directory(paths?["temp"]! as String),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final baseDir = await getApplicationSupportDirectory();
|
||||||
|
final workingDir =
|
||||||
|
Platform.isAndroid ? await getExternalStorageDirectory() : baseDir;
|
||||||
|
final tempDir = await getTemporaryDirectory();
|
||||||
|
dirs = (
|
||||||
|
baseDir: baseDir,
|
||||||
|
workingDir: workingDir!,
|
||||||
|
tempDir: tempDir,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dirs.baseDir.existsSync()) {
|
||||||
|
await dirs.baseDir.create(recursive: true);
|
||||||
|
}
|
||||||
|
if (!dirs.workingDir.existsSync()) {
|
||||||
|
await dirs.workingDir.create(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Directory> getDatabaseDirectory() async {
|
||||||
|
if (Platform.isIOS || Platform.isMacOS) {
|
||||||
|
return getLibraryDirectory();
|
||||||
|
} else if (Platform.isWindows || Platform.isLinux) {
|
||||||
|
return getApplicationSupportDirectory();
|
||||||
|
}
|
||||||
|
return getApplicationDocumentsDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,28 +2,6 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:loggy/loggy.dart';
|
import 'package:loggy/loggy.dart';
|
||||||
|
|
||||||
class MultiLogPrinter extends LoggyPrinter {
|
|
||||||
MultiLogPrinter(
|
|
||||||
this.consolePrinter,
|
|
||||||
this.otherPrinters,
|
|
||||||
);
|
|
||||||
|
|
||||||
final LoggyPrinter consolePrinter;
|
|
||||||
List<LoggyPrinter> otherPrinters;
|
|
||||||
|
|
||||||
void addPrinter(LoggyPrinter printer) {
|
|
||||||
otherPrinters.add(printer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onLog(LogRecord record) {
|
|
||||||
consolePrinter.onLog(record);
|
|
||||||
for (final printer in otherPrinters) {
|
|
||||||
printer.onLog(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileLogPrinter extends LoggyPrinter {
|
class FileLogPrinter extends LoggyPrinter {
|
||||||
FileLogPrinter(
|
FileLogPrinter(
|
||||||
String filePath, {
|
String filePath, {
|
||||||
@@ -41,5 +19,15 @@ class FileLogPrinter extends LoggyPrinter {
|
|||||||
void onLog(LogRecord record) {
|
void onLog(LogRecord record) {
|
||||||
final time = record.time.toIso8601String().split('T')[1];
|
final time = record.time.toIso8601String().split('T')[1];
|
||||||
_sink.writeln("$time - $record");
|
_sink.writeln("$time - $record");
|
||||||
|
if (record.error != null) {
|
||||||
|
_sink.writeln(record.error);
|
||||||
|
}
|
||||||
|
if (record.stackTrace != null) {
|
||||||
|
_sink.writeln(record.stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_sink.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
30
lib/core/logger/logger.dart
Normal file
30
lib/core/logger/logger.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:loggy/loggy.dart';
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
static final app = Loggy("app");
|
||||||
|
static final bootstrap = Loggy("bootstrap");
|
||||||
|
|
||||||
|
static void logFlutterError(FlutterErrorDetails details) {
|
||||||
|
if (details.silent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final description = details.exceptionAsString();
|
||||||
|
|
||||||
|
app.error(
|
||||||
|
'Flutter Error: $description',
|
||||||
|
details.exception,
|
||||||
|
details.stack,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool logPlatformDispatcherError(Object error, StackTrace stackTrace) {
|
||||||
|
app.error(
|
||||||
|
'PlatformDispatcherError: $error',
|
||||||
|
error,
|
||||||
|
stackTrace,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
62
lib/core/logger/logger_controller.dart
Normal file
62
lib/core/logger/logger_controller.dart
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter_loggy/flutter_loggy.dart';
|
||||||
|
import 'package:hiddify/core/logger/custom_logger.dart';
|
||||||
|
import 'package:hiddify/utils/custom_loggers.dart';
|
||||||
|
import 'package:loggy/loggy.dart';
|
||||||
|
|
||||||
|
class LoggerController extends LoggyPrinter with InfraLogger {
|
||||||
|
LoggerController(
|
||||||
|
this.consolePrinter,
|
||||||
|
this.otherPrinters,
|
||||||
|
);
|
||||||
|
|
||||||
|
final LoggyPrinter consolePrinter;
|
||||||
|
final Map<String, LoggyPrinter> otherPrinters;
|
||||||
|
|
||||||
|
static LoggerController get instance => _instance;
|
||||||
|
|
||||||
|
static late LoggerController _instance;
|
||||||
|
|
||||||
|
static void init(String appLogPath) {
|
||||||
|
_instance = LoggerController(
|
||||||
|
const PrettyDeveloperPrinter(),
|
||||||
|
{"app": FileLogPrinter(appLogPath)},
|
||||||
|
);
|
||||||
|
Loggy.initLoggy(logPrinter: _instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> postInit(bool debugMode) async {
|
||||||
|
final logLevel = debugMode ? LogLevel.all : LogLevel.info;
|
||||||
|
final logToFile = debugMode || (!Platform.isAndroid && !Platform.isIOS);
|
||||||
|
|
||||||
|
if (!logToFile) _instance.removePrinter("app");
|
||||||
|
|
||||||
|
Loggy.initLoggy(
|
||||||
|
logPrinter: _instance,
|
||||||
|
logOptions: LogOptions(logLevel),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPrinter(String name, LoggyPrinter printer) {
|
||||||
|
loggy.debug("adding [$name] printer");
|
||||||
|
otherPrinters.putIfAbsent(name, () => printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removePrinter(String name) {
|
||||||
|
loggy.debug("removing [$name] printer");
|
||||||
|
final printer = otherPrinters[name];
|
||||||
|
if (printer case FileLogPrinter()) {
|
||||||
|
printer.dispose();
|
||||||
|
}
|
||||||
|
otherPrinters.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onLog(LogRecord record) {
|
||||||
|
consolePrinter.onLog(record);
|
||||||
|
for (final printer in otherPrinters.values) {
|
||||||
|
printer.onLog(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,23 +70,6 @@ class SilentStartNotifier extends _$SilentStartNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
class EnableAnalytics extends _$EnableAnalytics {
|
|
||||||
late final _pref = Pref(
|
|
||||||
ref.watch(sharedPreferencesProvider).requireValue,
|
|
||||||
"enable_analytics",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool build() => _pref.getValue();
|
|
||||||
|
|
||||||
Future<void> update(bool value) {
|
|
||||||
state = value;
|
|
||||||
return _pref.update(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
class DisableMemoryLimit extends _$DisableMemoryLimit {
|
class DisableMemoryLimit extends _$DisableMemoryLimit {
|
||||||
late final _pref = Pref(
|
late final _pref = Pref(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hiddify/core/analytics/analytics_controller.dart';
|
||||||
import 'package:hiddify/core/localization/locale_extensions.dart';
|
import 'package:hiddify/core/localization/locale_extensions.dart';
|
||||||
import 'package:hiddify/core/localization/locale_preferences.dart';
|
import 'package:hiddify/core/localization/locale_preferences.dart';
|
||||||
import 'package:hiddify/core/localization/translations.dart';
|
import 'package:hiddify/core/localization/translations.dart';
|
||||||
@@ -103,7 +104,7 @@ class EnableAnalyticsPrefTile extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final t = ref.watch(translationsProvider);
|
final t = ref.watch(translationsProvider);
|
||||||
|
|
||||||
final autoReport = ref.watch(enableAnalyticsProvider);
|
final autoReport = ref.watch(analyticsControllerProvider);
|
||||||
|
|
||||||
return SwitchListTile(
|
return SwitchListTile(
|
||||||
title: Text(t.settings.general.enableAnalytics),
|
title: Text(t.settings.general.enableAnalytics),
|
||||||
@@ -117,7 +118,15 @@ class EnableAnalyticsPrefTile extends HookConsumerWidget {
|
|||||||
if (onChanged != null) {
|
if (onChanged != null) {
|
||||||
return onChanged!(value);
|
return onChanged!(value);
|
||||||
}
|
}
|
||||||
return ref.read(enableAnalyticsProvider.notifier).update(value);
|
if (autoReport) {
|
||||||
|
await ref
|
||||||
|
.read(analyticsControllerProvider.notifier)
|
||||||
|
.disableAnalytics();
|
||||||
|
} else {
|
||||||
|
await ref
|
||||||
|
.read(analyticsControllerProvider.notifier)
|
||||||
|
.enableAnalytics();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
import 'package:hiddify/features/config_option/data/config_option_data_providers.dart';
|
import 'package:hiddify/features/config_option/data/config_option_data_providers.dart';
|
||||||
import 'package:hiddify/features/connection/data/connection_platform_source.dart';
|
import 'package:hiddify/features/connection/data/connection_platform_source.dart';
|
||||||
import 'package:hiddify/features/connection/data/connection_repository.dart';
|
import 'package:hiddify/features/connection/data/connection_repository.dart';
|
||||||
import 'package:hiddify/features/geo_asset/data/geo_asset_data_providers.dart';
|
import 'package:hiddify/features/geo_asset/data/geo_asset_data_providers.dart';
|
||||||
import 'package:hiddify/features/profile/data/profile_data_providers.dart';
|
import 'package:hiddify/features/profile/data/profile_data_providers.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
|
||||||
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ ConnectionRepository connectionRepository(
|
|||||||
ConnectionRepositoryRef ref,
|
ConnectionRepositoryRef ref,
|
||||||
) {
|
) {
|
||||||
return ConnectionRepositoryImpl(
|
return ConnectionRepositoryImpl(
|
||||||
directories: ref.watch(filesEditorServiceProvider).dirs,
|
directories: ref.watch(appDirectoriesProvider).requireValue,
|
||||||
configOptionRepository: ref.watch(configOptionRepositoryProvider),
|
configOptionRepository: ref.watch(configOptionRepositoryProvider),
|
||||||
singbox: ref.watch(singboxServiceProvider),
|
singbox: ref.watch(singboxServiceProvider),
|
||||||
platformSource: ConnectionPlatformSourceImpl(),
|
platformSource: ConnectionPlatformSourceImpl(),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:hiddify/core/database/database_provider.dart';
|
import 'package:hiddify/core/database/database_provider.dart';
|
||||||
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
import 'package:hiddify/core/http_client/http_client_provider.dart';
|
import 'package:hiddify/core/http_client/http_client_provider.dart';
|
||||||
import 'package:hiddify/features/geo_asset/data/geo_asset_data_source.dart';
|
import 'package:hiddify/features/geo_asset/data/geo_asset_data_source.dart';
|
||||||
import 'package:hiddify/features/geo_asset/data/geo_asset_path_resolver.dart';
|
import 'package:hiddify/features/geo_asset/data/geo_asset_path_resolver.dart';
|
||||||
import 'package:hiddify/features/geo_asset/data/geo_asset_repository.dart';
|
import 'package:hiddify/features/geo_asset/data/geo_asset_repository.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'geo_asset_data_providers.g.dart';
|
part 'geo_asset_data_providers.g.dart';
|
||||||
@@ -27,6 +27,6 @@ GeoAssetDataSource geoAssetDataSource(GeoAssetDataSourceRef ref) {
|
|||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
GeoAssetPathResolver geoAssetPathResolver(GeoAssetPathResolverRef ref) {
|
GeoAssetPathResolver geoAssetPathResolver(GeoAssetPathResolverRef ref) {
|
||||||
return GeoAssetPathResolver(
|
return GeoAssetPathResolver(
|
||||||
ref.watch(filesEditorServiceProvider).dirs.workingDir,
|
ref.watch(appDirectoriesProvider).requireValue.workingDir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hiddify/core/analytics/analytics_controller.dart';
|
||||||
import 'package:hiddify/core/localization/translations.dart';
|
import 'package:hiddify/core/localization/translations.dart';
|
||||||
import 'package:hiddify/core/model/constants.dart';
|
import 'package:hiddify/core/model/constants.dart';
|
||||||
import 'package:hiddify/core/preferences/general_preferences.dart';
|
import 'package:hiddify/core/preferences/general_preferences.dart';
|
||||||
@@ -9,7 +10,6 @@ import 'package:hiddify/features/common/general_pref_tiles.dart';
|
|||||||
import 'package:hiddify/gen/assets.gen.dart';
|
import 'package:hiddify/gen/assets.gen.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
||||||
import 'package:sliver_tools/sliver_tools.dart';
|
import 'package:sliver_tools/sliver_tools.dart';
|
||||||
|
|
||||||
class IntroPage extends HookConsumerWidget with PresLogger {
|
class IntroPage extends HookConsumerWidget with PresLogger {
|
||||||
@@ -73,10 +73,12 @@ class IntroPage extends HookConsumerWidget with PresLogger {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (isStarting.value) return;
|
if (isStarting.value) return;
|
||||||
isStarting.value = true;
|
isStarting.value = true;
|
||||||
if (!ref.read(enableAnalyticsProvider)) {
|
if (!ref.read(analyticsControllerProvider)) {
|
||||||
loggy.info("disabling analytics per user request");
|
loggy.info("disabling analytics per user request");
|
||||||
try {
|
try {
|
||||||
await Sentry.close();
|
await ref
|
||||||
|
.read(analyticsControllerProvider.notifier)
|
||||||
|
.disableAnalytics();
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
loggy.error(
|
loggy.error(
|
||||||
"could not disable analytics",
|
"could not disable analytics",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
import 'package:hiddify/features/log/data/log_path_resolver.dart';
|
import 'package:hiddify/features/log/data/log_path_resolver.dart';
|
||||||
import 'package:hiddify/features/log/data/log_repository.dart';
|
import 'package:hiddify/features/log/data/log_repository.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
|
||||||
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
@@ -19,6 +19,6 @@ Future<LogRepository> logRepository(LogRepositoryRef ref) async {
|
|||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
LogPathResolver logPathResolver(LogPathResolverRef ref) {
|
LogPathResolver logPathResolver(LogPathResolverRef ref) {
|
||||||
return LogPathResolver(
|
return LogPathResolver(
|
||||||
ref.watch(filesEditorServiceProvider).dirs.workingDir,
|
ref.watch(appDirectoriesProvider).requireValue.workingDir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import 'package:hiddify/core/database/database_provider.dart';
|
import 'package:hiddify/core/database/database_provider.dart';
|
||||||
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
import 'package:hiddify/core/http_client/http_client_provider.dart';
|
import 'package:hiddify/core/http_client/http_client_provider.dart';
|
||||||
import 'package:hiddify/features/profile/data/profile_data_source.dart';
|
import 'package:hiddify/features/profile/data/profile_data_source.dart';
|
||||||
import 'package:hiddify/features/profile/data/profile_path_resolver.dart';
|
import 'package:hiddify/features/profile/data/profile_path_resolver.dart';
|
||||||
import 'package:hiddify/features/profile/data/profile_repository.dart';
|
import 'package:hiddify/features/profile/data/profile_repository.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
|
||||||
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
@@ -29,6 +29,6 @@ ProfileDataSource profileDataSource(ProfileDataSourceRef ref) {
|
|||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
ProfilePathResolver profilePathResolver(ProfilePathResolverRef ref) {
|
ProfilePathResolver profilePathResolver(ProfilePathResolverRef ref) {
|
||||||
return ProfilePathResolver(
|
return ProfilePathResolver(
|
||||||
ref.watch(filesEditorServiceProvider).dirs.workingDir,
|
ref.watch(appDirectoriesProvider).requireValue.workingDir,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hiddify/core/app_info/app_info_provider.dart';
|
import 'package:hiddify/core/app_info/app_info_provider.dart';
|
||||||
|
import 'package:hiddify/core/directories/directories_provider.dart';
|
||||||
import 'package:hiddify/core/localization/translations.dart';
|
import 'package:hiddify/core/localization/translations.dart';
|
||||||
import 'package:hiddify/core/model/constants.dart';
|
import 'package:hiddify/core/model/constants.dart';
|
||||||
import 'package:hiddify/core/model/failures.dart';
|
import 'package:hiddify/core/model/failures.dart';
|
||||||
@@ -10,7 +11,6 @@ import 'package:hiddify/features/app_update/notifier/app_update_state.dart';
|
|||||||
import 'package:hiddify/features/app_update/widget/new_version_dialog.dart';
|
import 'package:hiddify/features/app_update/widget/new_version_dialog.dart';
|
||||||
import 'package:hiddify/features/common/nested_app_bar.dart';
|
import 'package:hiddify/features/common/nested_app_bar.dart';
|
||||||
import 'package:hiddify/gen/assets.gen.dart';
|
import 'package:hiddify/gen/assets.gen.dart';
|
||||||
import 'package:hiddify/services/service_providers.dart';
|
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
@@ -65,7 +65,8 @@ class AboutPage extends HookConsumerWidget {
|
|||||||
title: Text(t.settings.general.openWorkingDir),
|
title: Text(t.settings.general.openWorkingDir),
|
||||||
trailing: const Icon(Icons.arrow_outward_outlined),
|
trailing: const Icon(Icons.arrow_outward_outlined),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final path = ref.read(filesEditorServiceProvider).workingDir.uri;
|
final path =
|
||||||
|
ref.watch(appDirectoriesProvider).requireValue.workingDir.uri;
|
||||||
await UriUtils.tryLaunch(path);
|
await UriUtils.tryLaunch(path);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -23,28 +23,7 @@ class GeneralSettingTiles extends HookConsumerWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const LocalePrefTile(),
|
const LocalePrefTile(),
|
||||||
EnableAnalyticsPrefTile(
|
const EnableAnalyticsPrefTile(),
|
||||||
onChanged: (value) async {
|
|
||||||
await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text(t.settings.general.enableAnalytics),
|
|
||||||
content: Text(t.settings.requiresRestartMsg),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => context.pop(true),
|
|
||||||
child: Text(
|
|
||||||
MaterialLocalizations.of(context).okButtonLabel,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return ref.read(enableAnalyticsProvider.notifier).update(value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(t.settings.general.themeMode),
|
title: Text(t.settings.general.themeMode),
|
||||||
subtitle: Text(themeMode.present(t)),
|
subtitle: Text(themeMode.present(t)),
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:hiddify/services/platform_services.dart';
|
|
||||||
import 'package:hiddify/utils/utils.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
typedef Directories = ({
|
|
||||||
Directory baseDir,
|
|
||||||
Directory workingDir,
|
|
||||||
Directory tempDir
|
|
||||||
});
|
|
||||||
|
|
||||||
class FilesEditorService with InfraLogger {
|
|
||||||
FilesEditorService(this.platformServices);
|
|
||||||
|
|
||||||
final PlatformServices platformServices;
|
|
||||||
|
|
||||||
late final Directories dirs;
|
|
||||||
|
|
||||||
Directory get workingDir => dirs.workingDir;
|
|
||||||
|
|
||||||
Future<void> init() async {
|
|
||||||
dirs = await platformServices.getPaths().getOrElse(
|
|
||||||
(error) {
|
|
||||||
loggy.error("error getting paths", error, StackTrace.current);
|
|
||||||
throw error;
|
|
||||||
},
|
|
||||||
).run();
|
|
||||||
|
|
||||||
loggy.info("directories: $dirs");
|
|
||||||
|
|
||||||
if (!await dirs.baseDir.exists()) {
|
|
||||||
await dirs.baseDir.create(recursive: true);
|
|
||||||
}
|
|
||||||
if (!await dirs.workingDir.exists()) {
|
|
||||||
await dirs.workingDir.create(recursive: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Directory> getDatabaseDirectory() async {
|
|
||||||
if (Platform.isIOS || Platform.isMacOS) {
|
|
||||||
return getLibraryDirectory();
|
|
||||||
} else if (Platform.isWindows || Platform.isLinux) {
|
|
||||||
return getApplicationSupportDirectory();
|
|
||||||
}
|
|
||||||
return getApplicationDocumentsDirectory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:fpdart/fpdart.dart';
|
|
||||||
import 'package:hiddify/services/files_editor_service.dart';
|
|
||||||
import 'package:hiddify/utils/utils.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
class PlatformServices with InfraLogger {
|
|
||||||
final _methodChannel = const MethodChannel("app.hiddify.com/platform");
|
|
||||||
|
|
||||||
TaskEither<String, Directories> getPaths() {
|
|
||||||
return TaskEither(
|
|
||||||
() async {
|
|
||||||
loggy.debug("getting paths");
|
|
||||||
final Directories dirs;
|
|
||||||
if (Platform.isIOS) {
|
|
||||||
final paths = await _methodChannel.invokeMethod<Map>("get_paths");
|
|
||||||
loggy.debug("paths: $paths");
|
|
||||||
dirs = (
|
|
||||||
baseDir: Directory(paths?["base"]! as String),
|
|
||||||
workingDir: Directory(paths?["working"]! as String),
|
|
||||||
tempDir: Directory(paths?["temp"]! as String),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
final baseDir = await getApplicationSupportDirectory();
|
|
||||||
final workingDir = Platform.isAndroid
|
|
||||||
? await getExternalStorageDirectory()
|
|
||||||
: baseDir;
|
|
||||||
final tempDir = await getTemporaryDirectory();
|
|
||||||
dirs = (
|
|
||||||
baseDir: baseDir,
|
|
||||||
workingDir: workingDir!,
|
|
||||||
tempDir: tempDir,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return right(dirs);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import 'package:hiddify/services/files_editor_service.dart';
|
|
||||||
import 'package:hiddify/services/platform_services.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|
||||||
|
|
||||||
part 'service_providers.g.dart';
|
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
FilesEditorService filesEditorService(FilesEditorServiceRef ref) =>
|
|
||||||
FilesEditorService(ref.watch(platformServicesProvider));
|
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
PlatformServices platformServices(PlatformServicesRef ref) =>
|
|
||||||
PlatformServices();
|
|
||||||
@@ -2,7 +2,6 @@ export 'alerts.dart';
|
|||||||
export 'async_mutation.dart';
|
export 'async_mutation.dart';
|
||||||
export 'bottom_sheet_page.dart';
|
export 'bottom_sheet_page.dart';
|
||||||
export 'callback_debouncer.dart';
|
export 'callback_debouncer.dart';
|
||||||
export 'custom_log_printer.dart';
|
|
||||||
export 'custom_loggers.dart';
|
export 'custom_loggers.dart';
|
||||||
export 'custom_text_form_field.dart';
|
export 'custom_text_form_field.dart';
|
||||||
export 'date_time_formatter.dart';
|
export 'date_time_formatter.dart';
|
||||||
@@ -11,7 +10,6 @@ export 'mutation_state.dart';
|
|||||||
export 'number_formatters.dart';
|
export 'number_formatters.dart';
|
||||||
export 'placeholders.dart';
|
export 'placeholders.dart';
|
||||||
export 'platform_utils.dart';
|
export 'platform_utils.dart';
|
||||||
export 'sentry_loggy_integration.dart';
|
|
||||||
export 'sentry_riverpod_observer.dart';
|
export 'sentry_riverpod_observer.dart';
|
||||||
export 'sentry_utils.dart';
|
export 'sentry_utils.dart';
|
||||||
export 'text_utils.dart';
|
export 'text_utils.dart';
|
||||||
|
|||||||
Reference in New Issue
Block a user