From 6219663a4c350dcdb2caf348a467457a6d363294 Mon Sep 17 00:00:00 2001 From: problematicconsumer Date: Fri, 22 Dec 2023 14:16:24 +0330 Subject: [PATCH] Change initialization and logging --- lib/bootstrap.dart | 220 ++++++++++-------- lib/core/analytics/analytics_controller.dart | 67 ++++++ lib/core/analytics/analytics_filter.dart | 29 +++ .../analytics/analytics_logger.dart} | 0 .../connection/database_connection.dart | 4 +- .../directories/directories_provider.dart | 56 +++++ .../logger/custom_logger.dart} | 32 +-- lib/core/logger/logger.dart | 30 +++ lib/core/logger/logger_controller.dart | 62 +++++ lib/core/preferences/general_preferences.dart | 17 -- lib/features/common/general_pref_tiles.dart | 13 +- .../data/connection_data_providers.dart | 4 +- .../data/geo_asset_data_providers.dart | 4 +- lib/features/intro/widget/intro_page.dart | 8 +- lib/features/log/data/log_data_providers.dart | 4 +- .../profile/data/profile_data_providers.dart | 4 +- lib/features/settings/about/about_page.dart | 5 +- .../widgets/general_setting_tiles.dart | 23 +- lib/services/files_editor_service.dart | 48 ---- lib/services/platform_services.dart | 41 ---- lib/services/service_providers.dart | 13 -- lib/utils/utils.dart | 2 - 22 files changed, 409 insertions(+), 277 deletions(-) create mode 100644 lib/core/analytics/analytics_controller.dart create mode 100644 lib/core/analytics/analytics_filter.dart rename lib/{utils/sentry_loggy_integration.dart => core/analytics/analytics_logger.dart} (100%) create mode 100644 lib/core/directories/directories_provider.dart rename lib/{utils/custom_log_printer.dart => core/logger/custom_logger.dart} (54%) create mode 100644 lib/core/logger/logger.dart create mode 100644 lib/core/logger/logger_controller.dart delete mode 100644 lib/services/files_editor_service.dart delete mode 100644 lib/services/platform_services.dart delete mode 100644 lib/services/service_providers.dart diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 5278b874..7cafdc82 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -1,10 +1,14 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_loggy/flutter_loggy.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/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/preferences/general_preferences.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/services/auto_start_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/utils/utils.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:window_manager/window_manager.dart'; -final _logger = Loggy('bootstrap'); -const _testCrashReport = false; -final _loggers = MultiLogPrinter(const PrettyPrinter(), []); - Future lazyBootstrap( WidgetsBinding widgetsBinding, Environment env, ) async { FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); - if (PlatformUtils.isDesktop) await windowManager.ensureInitialized(); - final sentryLogger = SentryLoggyIntegration(); - _loggers.addPrinter(sentryLogger); - Loggy.initLoggy(); + Loggy.initLoggy(logPrinter: const PrettyDeveloperPrinter()); + + FlutterError.onError = Logger.logFlutterError; + WidgetsBinding.instance.platformDispatcher.onError = + Logger.logPlatformDispatcherError; + + final stopWatch = Stopwatch()..start(); + if (PlatformUtils.isDesktop) await windowManager.ensureInitialized(); final container = ProviderContainer( overrides: [ @@ -47,65 +50,65 @@ Future lazyBootstrap( ], ); - final appInfo = await container.read(appInfoProvider.future); - - await container.read(sharedPreferencesProvider.future); - final enableAnalytics = container.read(enableAnalyticsProvider); - - await SentryFlutter.init( - (options) { - if ((enableAnalytics && !kDebugMode) || _testCrashReport) { - options.dsn = Environment.sentryDSN; - } else { - options.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.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), + await _init( + "directories", + () => container.read(appDirectoriesProvider.future), ); -} + LoggerController.init(container.read(logPathResolverProvider).appFile().path); -Future _lazyBootstrap( - WidgetsBinding widgetsBinding, - ProviderContainer container, - Environment env, -) async { - try { - await PreferencesMigration( - sharedPreferences: container.read(sharedPreferencesProvider).requireValue, - ).migrate(); - } catch (e) { - _logger.error("preferences migration failed", e); - if (env == Environment.dev) rethrow; - _logger.info("clearing preferences"); - await container.read(sharedPreferencesProvider).requireValue.clear(); + final appInfo = await _init( + "app info", + () => container.read(appInfoProvider.future), + ); + await _init( + "preferences", + () => container.read(sharedPreferencesProvider.future), + ); + + final enableAnalytics = container.read(analyticsControllerProvider); + if (enableAnalytics) { + await _init( + "analytics", + () => container + .read(analyticsControllerProvider.notifier) + .enableAnalytics(), + ); } + await _init( + "preferences migration", + () async { + try { + await PreferencesMigration( + sharedPreferences: + container.read(sharedPreferencesProvider).requireValue, + ).migrate(); + } catch (e, stackTrace) { + Logger.bootstrap.error("preferences migration failed", e, stackTrace); + if (env == Environment.dev) rethrow; + Logger.bootstrap.info("clearing preferences"); + await container.read(sharedPreferencesProvider).requireValue.clear(); + } + }, + ); + final debug = container.read(debugModeNotifierProvider) || kDebugMode; - final filesEditor = container.read(filesEditorServiceProvider); - await filesEditor.init(); - await container.read(logRepositoryProvider.future); - await container.read(geoAssetRepositoryProvider.future); - await container.read(profileRepositoryProvider.future); + await _init( + "logs repository", + () => container.read(logRepositoryProvider.future), + ); + await _init("logger controller", () => LoggerController.postInit(debug)); + Logger.bootstrap.info(appInfo.format()); - initLoggers(container.read, debug); - _logger.info(container.read(appInfoProvider).requireValue.format()); + await _init( + "geo assets repository", + () => container.read(geoAssetRepositoryProvider.future), + ); + await _init( + "profile repository", + () => container.read(profileRepositoryProvider.future), + ); final silentStart = container.read(silentStartNotifierProvider); if (silentStart) { @@ -113,31 +116,41 @@ Future _lazyBootstrap( } if (PlatformUtils.isDesktop) { - _logger.debug("initializing [Auto Start Service] and [Window Controller]"); - await container.read(autoStartServiceProvider.future); - await container.read(windowControllerProvider.future); + await _init( + "auto start service", + () => container.read(autoStartServiceProvider.future), + ); + await _init( + "window controller", + () => container.read(windowControllerProvider.future), + ); } - await container.read(singboxServiceProvider).init(); - _logger.debug("initialized [Singbox Service]"); + 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), + ); - 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 _safeInit( + "system tray", + () => container.read(systemTrayControllerProvider.future), + timeout: 1000, + ); } + Logger.bootstrap.info("bootstrap took [${stopWatch.elapsedMilliseconds}ms]"); + stopWatch.stop(); + runApp( ProviderScope( parent: container, @@ -150,19 +163,36 @@ Future _lazyBootstrap( if (!silentStart) FlutterNativeSplash.remove(); } -void initLoggers( - Result Function(ProviderListenable) read, - bool debug, -) { - final logLevel = debug ? LogLevel.all : LogLevel.info; - final logToFile = debug || (!Platform.isAndroid && !Platform.isIOS); - if (logToFile) { - _loggers.addPrinter( - FileLogPrinter(read(logPathResolverProvider).appFile().path), - ); +Future _init( + String name, + Future Function() initializer, { + int? timeout, +}) async { + final stopWatch = Stopwatch()..start(); + Future func() => timeout != null + ? initializer().timeout(Duration(milliseconds: timeout)) + : 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 _safeInit( + String name, + Future Function() initializer, { + int? timeout, +}) async { + try { + return await _init(name, initializer, timeout: timeout); + } catch (_) { + return null; } - Loggy.initLoggy( - logPrinter: _loggers, - logOptions: LogOptions(logLevel), - ); } diff --git a/lib/core/analytics/analytics_controller.dart b/lib/core/analytics/analytics_controller.dart new file mode 100644 index 00000000..e9157b5c --- /dev/null +++ b/lib/core/analytics/analytics_controller.dart @@ -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 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 disableAnalytics() async { + loggy.debug("disabling analytics"); + await _preferences.setBool(enableAnalyticsPrefKey, false); + await Sentry.close(); + LoggerController.instance.removePrinter("analytics"); + state = false; + } +} diff --git a/lib/core/analytics/analytics_filter.dart b/lib/core/analytics/analytics_filter.dart new file mode 100644 index 00000000..a8976a50 --- /dev/null +++ b/lib/core/analytics/analytics_filter.dart @@ -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 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), + }; diff --git a/lib/utils/sentry_loggy_integration.dart b/lib/core/analytics/analytics_logger.dart similarity index 100% rename from lib/utils/sentry_loggy_integration.dart rename to lib/core/analytics/analytics_logger.dart diff --git a/lib/core/database/connection/database_connection.dart b/lib/core/database/connection/database_connection.dart index f05450e5..046a44f4 100644 --- a/lib/core/database/connection/database_connection.dart +++ b/lib/core/database/connection/database_connection.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:drift/drift.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; LazyDatabase openConnection() { return LazyDatabase(() async { - final dbDir = await FilesEditorService.getDatabaseDirectory(); + final dbDir = await AppDirectories.getDatabaseDirectory(); final file = File(p.join(dbDir.path, 'db.sqlite')); return NativeDatabase.createInBackground(file); }); diff --git a/lib/core/directories/directories_provider.dart b/lib/core/directories/directories_provider.dart new file mode 100644 index 00000000..01595726 --- /dev/null +++ b/lib/core/directories/directories_provider.dart @@ -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 build() async { + final Directories dirs; + if (Platform.isIOS) { + final paths = await _methodChannel.invokeMethod("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 getDatabaseDirectory() async { + if (Platform.isIOS || Platform.isMacOS) { + return getLibraryDirectory(); + } else if (Platform.isWindows || Platform.isLinux) { + return getApplicationSupportDirectory(); + } + return getApplicationDocumentsDirectory(); + } +} diff --git a/lib/utils/custom_log_printer.dart b/lib/core/logger/custom_logger.dart similarity index 54% rename from lib/utils/custom_log_printer.dart rename to lib/core/logger/custom_logger.dart index 5a98e60c..181234cf 100644 --- a/lib/utils/custom_log_printer.dart +++ b/lib/core/logger/custom_logger.dart @@ -2,28 +2,6 @@ import 'dart:io'; import 'package:loggy/loggy.dart'; -class MultiLogPrinter extends LoggyPrinter { - MultiLogPrinter( - this.consolePrinter, - this.otherPrinters, - ); - - final LoggyPrinter consolePrinter; - List 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 { FileLogPrinter( String filePath, { @@ -41,5 +19,15 @@ class FileLogPrinter extends LoggyPrinter { void onLog(LogRecord record) { final time = record.time.toIso8601String().split('T')[1]; _sink.writeln("$time - $record"); + if (record.error != null) { + _sink.writeln(record.error); + } + if (record.stackTrace != null) { + _sink.writeln(record.stackTrace); + } + } + + void dispose() { + _sink.close(); } } diff --git a/lib/core/logger/logger.dart b/lib/core/logger/logger.dart new file mode 100644 index 00000000..89bd655d --- /dev/null +++ b/lib/core/logger/logger.dart @@ -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; + } +} diff --git a/lib/core/logger/logger_controller.dart b/lib/core/logger/logger_controller.dart new file mode 100644 index 00000000..4594ef6a --- /dev/null +++ b/lib/core/logger/logger_controller.dart @@ -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 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 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); + } + } +} diff --git a/lib/core/preferences/general_preferences.dart b/lib/core/preferences/general_preferences.dart index b2dd8f0a..e6912f83 100644 --- a/lib/core/preferences/general_preferences.dart +++ b/lib/core/preferences/general_preferences.dart @@ -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 update(bool value) { - state = value; - return _pref.update(value); - } -} - @Riverpod(keepAlive: true) class DisableMemoryLimit extends _$DisableMemoryLimit { late final _pref = Pref( diff --git a/lib/features/common/general_pref_tiles.dart b/lib/features/common/general_pref_tiles.dart index 1ac1c373..bd202c82 100644 --- a/lib/features/common/general_pref_tiles.dart +++ b/lib/features/common/general_pref_tiles.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.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_preferences.dart'; import 'package:hiddify/core/localization/translations.dart'; @@ -103,7 +104,7 @@ class EnableAnalyticsPrefTile extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final t = ref.watch(translationsProvider); - final autoReport = ref.watch(enableAnalyticsProvider); + final autoReport = ref.watch(analyticsControllerProvider); return SwitchListTile( title: Text(t.settings.general.enableAnalytics), @@ -117,7 +118,15 @@ class EnableAnalyticsPrefTile extends HookConsumerWidget { if (onChanged != null) { 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(); + } }, ); } diff --git a/lib/features/connection/data/connection_data_providers.dart b/lib/features/connection/data/connection_data_providers.dart index a33f150d..96de56d9 100644 --- a/lib/features/connection/data/connection_data_providers.dart +++ b/lib/features/connection/data/connection_data_providers.dart @@ -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/connection/data/connection_platform_source.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/profile/data/profile_data_providers.dart'; -import 'package:hiddify/services/service_providers.dart'; import 'package:hiddify/singbox/service/singbox_service_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -14,7 +14,7 @@ ConnectionRepository connectionRepository( ConnectionRepositoryRef ref, ) { return ConnectionRepositoryImpl( - directories: ref.watch(filesEditorServiceProvider).dirs, + directories: ref.watch(appDirectoriesProvider).requireValue, configOptionRepository: ref.watch(configOptionRepositoryProvider), singbox: ref.watch(singboxServiceProvider), platformSource: ConnectionPlatformSourceImpl(), diff --git a/lib/features/geo_asset/data/geo_asset_data_providers.dart b/lib/features/geo_asset/data/geo_asset_data_providers.dart index 44a8c51f..be5c952d 100644 --- a/lib/features/geo_asset/data/geo_asset_data_providers.dart +++ b/lib/features/geo_asset/data/geo_asset_data_providers.dart @@ -1,9 +1,9 @@ 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/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_repository.dart'; -import 'package:hiddify/services/service_providers.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'geo_asset_data_providers.g.dart'; @@ -27,6 +27,6 @@ GeoAssetDataSource geoAssetDataSource(GeoAssetDataSourceRef ref) { @Riverpod(keepAlive: true) GeoAssetPathResolver geoAssetPathResolver(GeoAssetPathResolverRef ref) { return GeoAssetPathResolver( - ref.watch(filesEditorServiceProvider).dirs.workingDir, + ref.watch(appDirectoriesProvider).requireValue.workingDir, ); } diff --git a/lib/features/intro/widget/intro_page.dart b/lib/features/intro/widget/intro_page.dart index 5732a17d..e08ba753 100644 --- a/lib/features/intro/widget/intro_page.dart +++ b/lib/features/intro/widget/intro_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.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/model/constants.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/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sliver_tools/sliver_tools.dart'; class IntroPage extends HookConsumerWidget with PresLogger { @@ -73,10 +73,12 @@ class IntroPage extends HookConsumerWidget with PresLogger { onPressed: () async { if (isStarting.value) return; isStarting.value = true; - if (!ref.read(enableAnalyticsProvider)) { + if (!ref.read(analyticsControllerProvider)) { loggy.info("disabling analytics per user request"); try { - await Sentry.close(); + await ref + .read(analyticsControllerProvider.notifier) + .disableAnalytics(); } catch (error, stackTrace) { loggy.error( "could not disable analytics", diff --git a/lib/features/log/data/log_data_providers.dart b/lib/features/log/data/log_data_providers.dart index c84aa7b7..9bfeeb20 100644 --- a/lib/features/log/data/log_data_providers.dart +++ b/lib/features/log/data/log_data_providers.dart @@ -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_repository.dart'; -import 'package:hiddify/services/service_providers.dart'; import 'package:hiddify/singbox/service/singbox_service_provider.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -19,6 +19,6 @@ Future logRepository(LogRepositoryRef ref) async { @Riverpod(keepAlive: true) LogPathResolver logPathResolver(LogPathResolverRef ref) { return LogPathResolver( - ref.watch(filesEditorServiceProvider).dirs.workingDir, + ref.watch(appDirectoriesProvider).requireValue.workingDir, ); } diff --git a/lib/features/profile/data/profile_data_providers.dart b/lib/features/profile/data/profile_data_providers.dart index 3520b6a5..3c046be8 100644 --- a/lib/features/profile/data/profile_data_providers.dart +++ b/lib/features/profile/data/profile_data_providers.dart @@ -1,9 +1,9 @@ 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/features/profile/data/profile_data_source.dart'; import 'package:hiddify/features/profile/data/profile_path_resolver.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:riverpod_annotation/riverpod_annotation.dart'; @@ -29,6 +29,6 @@ ProfileDataSource profileDataSource(ProfileDataSourceRef ref) { @Riverpod(keepAlive: true) ProfilePathResolver profilePathResolver(ProfilePathResolverRef ref) { return ProfilePathResolver( - ref.watch(filesEditorServiceProvider).dirs.workingDir, + ref.watch(appDirectoriesProvider).requireValue.workingDir, ); } diff --git a/lib/features/settings/about/about_page.dart b/lib/features/settings/about/about_page.dart index fb7f1b81..c437a86a 100644 --- a/lib/features/settings/about/about_page.dart +++ b/lib/features/settings/about/about_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gap/gap.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/model/constants.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/common/nested_app_bar.dart'; import 'package:hiddify/gen/assets.gen.dart'; -import 'package:hiddify/services/service_providers.dart'; import 'package:hiddify/utils/utils.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -65,7 +65,8 @@ class AboutPage extends HookConsumerWidget { title: Text(t.settings.general.openWorkingDir), trailing: const Icon(Icons.arrow_outward_outlined), onTap: () async { - final path = ref.read(filesEditorServiceProvider).workingDir.uri; + final path = + ref.watch(appDirectoriesProvider).requireValue.workingDir.uri; await UriUtils.tryLaunch(path); }, ), diff --git a/lib/features/settings/widgets/general_setting_tiles.dart b/lib/features/settings/widgets/general_setting_tiles.dart index d3f45807..b2fc962e 100644 --- a/lib/features/settings/widgets/general_setting_tiles.dart +++ b/lib/features/settings/widgets/general_setting_tiles.dart @@ -23,28 +23,7 @@ class GeneralSettingTiles extends HookConsumerWidget { return Column( children: [ const LocalePrefTile(), - EnableAnalyticsPrefTile( - onChanged: (value) async { - await showDialog( - 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); - }, - ), + const EnableAnalyticsPrefTile(), ListTile( title: Text(t.settings.general.themeMode), subtitle: Text(themeMode.present(t)), diff --git a/lib/services/files_editor_service.dart b/lib/services/files_editor_service.dart deleted file mode 100644 index 86bf47c3..00000000 --- a/lib/services/files_editor_service.dart +++ /dev/null @@ -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 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 getDatabaseDirectory() async { - if (Platform.isIOS || Platform.isMacOS) { - return getLibraryDirectory(); - } else if (Platform.isWindows || Platform.isLinux) { - return getApplicationSupportDirectory(); - } - return getApplicationDocumentsDirectory(); - } -} diff --git a/lib/services/platform_services.dart b/lib/services/platform_services.dart deleted file mode 100644 index 71aadcaa..00000000 --- a/lib/services/platform_services.dart +++ /dev/null @@ -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 getPaths() { - return TaskEither( - () async { - loggy.debug("getting paths"); - final Directories dirs; - if (Platform.isIOS) { - final paths = await _methodChannel.invokeMethod("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); - }, - ); - } -} diff --git a/lib/services/service_providers.dart b/lib/services/service_providers.dart deleted file mode 100644 index c568c9c0..00000000 --- a/lib/services/service_providers.dart +++ /dev/null @@ -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(); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index de5fe51f..d913ab6c 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -2,7 +2,6 @@ export 'alerts.dart'; export 'async_mutation.dart'; export 'bottom_sheet_page.dart'; export 'callback_debouncer.dart'; -export 'custom_log_printer.dart'; export 'custom_loggers.dart'; export 'custom_text_form_field.dart'; export 'date_time_formatter.dart'; @@ -11,7 +10,6 @@ export 'mutation_state.dart'; export 'number_formatters.dart'; export 'placeholders.dart'; export 'platform_utils.dart'; -export 'sentry_loggy_integration.dart'; export 'sentry_riverpod_observer.dart'; export 'sentry_utils.dart'; export 'text_utils.dart';